diff --git a/.gitignore b/.gitignore index 1c3dd7a8..cb127719 100644 --- a/.gitignore +++ b/.gitignore @@ -3,157 +3,14 @@ __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - .vscode - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook .ipynb_checkpoints -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ src/main gmon.out gprof.txt /pocketpy amalgamated +web/lib diff --git a/LICENSE b/LICENSE index 15f75cf7..bde60ceb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,339 +1,165 @@ -GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. + 0. Additional Definitions. - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. - The precise terms and conditions for copying, distribution and -modification follow. + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + 1. Exception to Section 3 of the GNU GPL. - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. + 2. Conveying Modified Versions. - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. + 3. Object Code Incorporating Material from Library Header Files. - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. + b) Accompany the object code with a copy of the GNU GPL and this license + document. -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. + 4. Combined Works. -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) + d) Do one of the following: -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. + 5. Combined Libraries. - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. + 6. Revised Versions of the GNU Lesser General Public License. -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/plugins/flutter/.gitignore b/plugins/flutter/.gitignore new file mode 100644 index 00000000..96486fd9 --- /dev/null +++ b/plugins/flutter/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/plugins/flutter/.metadata b/plugins/flutter/.metadata new file mode 100644 index 00000000..7cc31ecb --- /dev/null +++ b/plugins/flutter/.metadata @@ -0,0 +1,36 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + channel: stable + +project_type: plugin_ffi + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: android + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: ios + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: windows + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/plugins/flutter/CHANGELOG.md b/plugins/flutter/CHANGELOG.md new file mode 100644 index 00000000..bb0d193c --- /dev/null +++ b/plugins/flutter/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.4.7 + +The initial version. Hello, world! diff --git a/plugins/flutter/LICENSE b/plugins/flutter/LICENSE new file mode 100644 index 00000000..bde60ceb --- /dev/null +++ b/plugins/flutter/LICENSE @@ -0,0 +1,165 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/plugins/flutter/README.md b/plugins/flutter/README.md new file mode 100644 index 00000000..c3ebd594 --- /dev/null +++ b/plugins/flutter/README.md @@ -0,0 +1,201 @@ +# Welcome to PocketPy + +PocketPy is a lightweight Python interpreter for game engines. + +![logo](https://pocketpy.dev/static/logo_flat.png) + +## Features + +| Name | Example | Supported | +| --------------- | -------------------------- | --------- | +| If Else | `if..else..elif` | YES | +| Loop | `for/while/break/continue` | YES | +| Function | `def f(x,*args,y=1):` | YES | +| Function KwArgs | `def f(**kwargs):` | NO | +| Subclass | `class A(B):` | YES | +| List | `[1, 2, 'a']` | YES | +| ListComp | `[i for i in range(5)]` | YES | +| Dict | `{'a': 1, 'b': 2}` | YES | +| F-String | `f'value is {x}'` | YES | +| Unpacking | `a, b = 1, 2` | YES | +| Star Unpacking | `a, *b = [1, 2, 3]` | NO | +| Throw Exception | `assert/raise` | YES | +| Catch Exception | `try..catch` | NO | +| Eval | `eval()` | YES | +| Import | `import/from..import` | YES | +| Context Block | `with as :` | YES | + + + +## Extra Features + +For features that are PocketPy specific, see [Extra Features](https://pocketpy.dev/extras/goto). +## Introduction + +This plugin provides object-oriented interfaces including full functionality of PocketPy [C-API](https://pocketpy.dev/c-api/vm). +It also provides `JsonRpcServer` class and `os` module bindings. + +Run the following script to install this plugin. +``` +flutter pub add pocketpy +``` + +## Requirements + +#### For Android + +You may need to set the Android NDK version to "21.4.7075529" or higher in `android/app/build.gradle`. +``` +android { + ndkVersion "21.4.7075529" +} +``` + +#### For iOS + +It should work without any setup. + + +#### For Windows + +VS2017 or higher is required to build the windows `.dll`. +Make sure you have C++ component installed. + + +## Basic Example + +```dart +import 'package:pocketpy/pocketpy.dart' as pkpy; + +// Create a virtual machine +pkpy.VM vm = pkpy.VM(); + +// Run a script +String code = 'print("Hello World!")'; +vm.exec(code); + +// Read the output +var _o = vm.read_output(); +print(_o.stdout) // "Hello world!\n" +print(_o.stderr) // "" +``` + + + +## REPL Widget Example + +```dart +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:pocketpy/pocketpy.dart' as pkpy; + +void main() { + runApp(const MaterialApp(home: MyApp())); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + late final pkpy.VM vm; + late final pkpy.REPL repl; + bool needMoreLines = false; + + final TextEditingController _controller = TextEditingController(); + final StringBuffer buffer = StringBuffer(); + + @override + void initState() { + super.initState(); + + // create a pocketpy virtual machine + vm = pkpy.VM(); + + // create a REPL + repl = pkpy.REPL(vm); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + refresh(); + }); + } + + void addMessage(String text) { + setState(() { + buffer.write(text); + }); + } + + void submitCode() { + var text = _controller.text; + _controller.clear(); + setState(() { + buffer.write(needMoreLines ? '... $text' : '>>> $text\n'); + }); + if (text == "exit()") exit(0); + needMoreLines = repl.input(text) == 0; + refresh(); + } + + void refresh() { + // ignore: no_leading_underscores_for_local_identifiers + var _o = vm.read_output(); + if (_o.stdout.isNotEmpty) buffer.write(_o.stdout); + if (_o.stderr.isNotEmpty) buffer.write(_o.stderr); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + var style = const TextStyle(fontSize: 16); + return Scaffold( + appBar: AppBar( + title: const Text('Demo'), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded( + child: SingleChildScrollView( + reverse: true, + child: Text( + buffer.toString(), + style: style, + textAlign: TextAlign.left, + ), + ), + ), + const SizedBox( + height: 16, + ), + SizedBox( + height: 50, + child: TextFormField( + controller: _controller, + style: style, + maxLines: 1, + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: 'Enter Python code', + ), + ), + ), + Container( + height: 60, + alignment: Alignment.centerRight, + child: MaterialButton( + onPressed: submitCode, + color: Colors.blue, + textColor: Colors.white, + child: const Text('Run')), + ), + ]), + ), + ); + } +} +``` diff --git a/plugins/flutter/analysis_options.yaml b/plugins/flutter/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/plugins/flutter/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/plugins/flutter/android/.gitignore b/plugins/flutter/android/.gitignore new file mode 100644 index 00000000..161bdcda --- /dev/null +++ b/plugins/flutter/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/plugins/flutter/android/build.gradle b/plugins/flutter/android/build.gradle new file mode 100644 index 00000000..ee2ddda8 --- /dev/null +++ b/plugins/flutter/android/build.gradle @@ -0,0 +1,59 @@ +// The Android Gradle Plugin builds the native code with the Android NDK. + +group 'com.example.pocketpy' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + // The Android Gradle Plugin knows how to build native code with the NDK. + classpath 'com.android.tools.build:gradle:7.1.2' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + // Bumping the plugin compileSdkVersion requires all clients of this plugin + // to bump the version in their app. + compileSdkVersion 33 + + // Bumping the plugin ndkVersion requires all clients of this plugin to bump + // the version in their app and to download a newer version of the NDK. + ndkVersion "21.4.7075529" + + // Invoke the shared CMake build with the Android Gradle Plugin. + externalNativeBuild { + cmake { + path "../src/CMakeLists.txt" + + // The default CMake version for the Android Gradle Plugin is 3.10.2. + // https://developer.android.com/studio/projects/install-ndk#vanilla_cmake + // + // The Flutter tooling requires that developers have CMake 3.10 or later + // installed. You should not increase this version, as doing so will cause + // the plugin to fail to compile for some customers of the plugin. + // version "3.10.2" + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + defaultConfig { + minSdkVersion 22 + } +} diff --git a/plugins/flutter/android/settings.gradle b/plugins/flutter/android/settings.gradle new file mode 100644 index 00000000..96febe2c --- /dev/null +++ b/plugins/flutter/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'pocketpy' diff --git a/plugins/flutter/android/src/main/AndroidManifest.xml b/plugins/flutter/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..fdbefd18 --- /dev/null +++ b/plugins/flutter/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/plugins/flutter/example/.gitignore b/plugins/flutter/example/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/plugins/flutter/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/plugins/flutter/example/README.md b/plugins/flutter/example/README.md new file mode 100644 index 00000000..e5d6a289 --- /dev/null +++ b/plugins/flutter/example/README.md @@ -0,0 +1,6 @@ +# Example + +In `example/` folder is a REPL widget demo. + +See `main.dart` for details. + diff --git a/plugins/flutter/example/analysis_options.yaml b/plugins/flutter/example/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/plugins/flutter/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/plugins/flutter/example/android/.gitignore b/plugins/flutter/example/android/.gitignore new file mode 100644 index 00000000..6f568019 --- /dev/null +++ b/plugins/flutter/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/plugins/flutter/example/android/app/build.gradle b/plugins/flutter/example/android/app/build.gradle new file mode 100644 index 00000000..1e2fb59b --- /dev/null +++ b/plugins/flutter/example/android/app/build.gradle @@ -0,0 +1,71 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.pocketpy_example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/plugins/flutter/example/android/app/src/debug/AndroidManifest.xml b/plugins/flutter/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..70c9b7f9 --- /dev/null +++ b/plugins/flutter/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/plugins/flutter/example/android/app/src/main/AndroidManifest.xml b/plugins/flutter/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b35219d6 --- /dev/null +++ b/plugins/flutter/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/plugins/flutter/example/android/app/src/main/kotlin/com/example/pocketpy_example/MainActivity.kt b/plugins/flutter/example/android/app/src/main/kotlin/com/example/pocketpy_example/MainActivity.kt new file mode 100644 index 00000000..98dff5ee --- /dev/null +++ b/plugins/flutter/example/android/app/src/main/kotlin/com/example/pocketpy_example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.pocketpy_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/plugins/flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml b/plugins/flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/plugins/flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/plugins/flutter/example/android/app/src/main/res/drawable/launch_background.xml b/plugins/flutter/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/plugins/flutter/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/plugins/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/plugins/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..db77bb4b Binary files /dev/null and b/plugins/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/plugins/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/plugins/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..17987b79 Binary files /dev/null and b/plugins/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/plugins/flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/plugins/flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..09d43914 Binary files /dev/null and b/plugins/flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/plugins/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/plugins/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..d5f1c8d3 Binary files /dev/null and b/plugins/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/plugins/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/plugins/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..4d6372ee Binary files /dev/null and b/plugins/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/plugins/flutter/example/android/app/src/main/res/values-night/styles.xml b/plugins/flutter/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..06952be7 --- /dev/null +++ b/plugins/flutter/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/plugins/flutter/example/android/app/src/main/res/values/styles.xml b/plugins/flutter/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..cb1ef880 --- /dev/null +++ b/plugins/flutter/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/plugins/flutter/example/android/app/src/profile/AndroidManifest.xml b/plugins/flutter/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..70c9b7f9 --- /dev/null +++ b/plugins/flutter/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/plugins/flutter/example/android/build.gradle b/plugins/flutter/example/android/build.gradle new file mode 100644 index 00000000..83ae2200 --- /dev/null +++ b/plugins/flutter/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/plugins/flutter/example/android/gradle.properties b/plugins/flutter/example/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/plugins/flutter/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/plugins/flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/plugins/flutter/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..cb24abda --- /dev/null +++ b/plugins/flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/plugins/flutter/example/android/settings.gradle b/plugins/flutter/example/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/plugins/flutter/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/plugins/flutter/example/ios/.gitignore b/plugins/flutter/example/ios/.gitignore new file mode 100644 index 00000000..7a7f9873 --- /dev/null +++ b/plugins/flutter/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/plugins/flutter/example/ios/Flutter/AppFrameworkInfo.plist b/plugins/flutter/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..9625e105 --- /dev/null +++ b/plugins/flutter/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/plugins/flutter/example/ios/Flutter/Debug.xcconfig b/plugins/flutter/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/plugins/flutter/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/plugins/flutter/example/ios/Flutter/Release.xcconfig b/plugins/flutter/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/plugins/flutter/example/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/plugins/flutter/example/ios/Runner.xcodeproj/project.pbxproj b/plugins/flutter/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..72b69ecb --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,481 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocketpyExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocketpyExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocketpyExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/plugins/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/plugins/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..c87d15a3 --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/plugins/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/plugins/flutter/example/ios/Runner/AppDelegate.swift b/plugins/flutter/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/plugins/flutter/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..dc9ada47 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..28c6bf03 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..f091b6b0 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..4cde1211 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..d0ef06e7 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..dcdc2306 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..c8f9ed8f Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..75b2d164 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..c4df70d3 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..6a84f41e Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..d0e1f585 Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/plugins/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/plugins/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/plugins/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/flutter/example/ios/Runner/Base.lproj/Main.storyboard b/plugins/flutter/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/plugins/flutter/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/flutter/example/ios/Runner/Info.plist b/plugins/flutter/example/ios/Runner/Info.plist new file mode 100644 index 00000000..68f750d2 --- /dev/null +++ b/plugins/flutter/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Pocketpy + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + pocketpy_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/plugins/flutter/example/ios/Runner/Runner-Bridging-Header.h b/plugins/flutter/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/plugins/flutter/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/plugins/flutter/example/lib/main.dart b/plugins/flutter/example/lib/main.dart new file mode 100644 index 00000000..42a42a3e --- /dev/null +++ b/plugins/flutter/example/lib/main.dart @@ -0,0 +1,113 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:pocketpy/pocketpy.dart' as pkpy; + +void main() { + runApp(const MaterialApp(home: MyApp())); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + late final pkpy.VM vm; + late final pkpy.REPL repl; + bool needMoreLines = false; + + final TextEditingController _controller = TextEditingController(); + final StringBuffer buffer = StringBuffer(); + + @override + void initState() { + super.initState(); + + // create a pocketpy virtual machine + vm = pkpy.VM(); + + // create a REPL + repl = pkpy.REPL(vm); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + refresh(); + }); + } + + void addMessage(String text) { + setState(() { + buffer.write(text); + }); + } + + void submitCode() { + var text = _controller.text; + _controller.clear(); + setState(() { + buffer.write(needMoreLines ? '... $text' : '>>> $text\n'); + }); + if (text == "exit()") exit(0); + needMoreLines = repl.input(text) == 0; + refresh(); + } + + void refresh() { + // ignore: no_leading_underscores_for_local_identifiers + var _o = vm.read_output(); + if (_o.stdout.isNotEmpty) buffer.write(_o.stdout); + if (_o.stderr.isNotEmpty) buffer.write(_o.stderr); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + var style = const TextStyle(fontSize: 16); + return Scaffold( + appBar: AppBar( + title: const Text('Demo'), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded( + child: SingleChildScrollView( + reverse: true, + child: Text( + buffer.toString(), + style: style, + textAlign: TextAlign.left, + ), + ), + ), + const SizedBox( + height: 16, + ), + SizedBox( + height: 50, + child: TextFormField( + controller: _controller, + style: style, + maxLines: 1, + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: 'Enter Python code', + ), + ), + ), + Container( + height: 60, + alignment: Alignment.centerRight, + child: MaterialButton( + onPressed: submitCode, + color: Colors.blue, + textColor: Colors.white, + child: const Text('Run')), + ), + ]), + ), + ); + } +} diff --git a/plugins/flutter/example/pubspec.lock b/plugins/flutter/example/pubspec.lock new file mode 100644 index 00000000..92297c03 --- /dev/null +++ b/plugins/flutter/example/pubspec.lock @@ -0,0 +1,175 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + pocketpy: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.4.6" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" +sdks: + dart: ">=2.18.5 <3.0.0" + flutter: ">=2.5.0" diff --git a/plugins/flutter/example/pubspec.yaml b/plugins/flutter/example/pubspec.yaml new file mode 100644 index 00000000..cad97b78 --- /dev/null +++ b/plugins/flutter/example/pubspec.yaml @@ -0,0 +1,98 @@ +name: pocketpy_example +description: Demonstrates how to use the pocketpy plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=2.18.5 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + pocketpy: + # When depending on this package from a real application you should use: + # pocketpy: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/plugins/flutter/example/windows/.gitignore b/plugins/flutter/example/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/plugins/flutter/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/plugins/flutter/example/windows/CMakeLists.txt b/plugins/flutter/example/windows/CMakeLists.txt new file mode 100644 index 00000000..a4bfe07f --- /dev/null +++ b/plugins/flutter/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(pocketpy_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "pocketpy_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/plugins/flutter/example/windows/flutter/CMakeLists.txt b/plugins/flutter/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/plugins/flutter/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/plugins/flutter/example/windows/flutter/generated_plugin_registrant.cc b/plugins/flutter/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..8b6d4680 --- /dev/null +++ b/plugins/flutter/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/plugins/flutter/example/windows/flutter/generated_plugin_registrant.h b/plugins/flutter/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/plugins/flutter/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/plugins/flutter/example/windows/flutter/generated_plugins.cmake b/plugins/flutter/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..2a29fc2a --- /dev/null +++ b/plugins/flutter/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + pocketpy +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/plugins/flutter/example/windows/runner/CMakeLists.txt b/plugins/flutter/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..17411a8a --- /dev/null +++ b/plugins/flutter/example/windows/runner/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/plugins/flutter/example/windows/runner/Runner.rc b/plugins/flutter/example/windows/runner/Runner.rc new file mode 100644 index 00000000..aca4b45a --- /dev/null +++ b/plugins/flutter/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "pocketpy_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "pocketpy_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "pocketpy_example.exe" "\0" + VALUE "ProductName", "pocketpy_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/plugins/flutter/example/windows/runner/flutter_window.cpp b/plugins/flutter/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/plugins/flutter/example/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/plugins/flutter/example/windows/runner/flutter_window.h b/plugins/flutter/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/plugins/flutter/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/plugins/flutter/example/windows/runner/main.cpp b/plugins/flutter/example/windows/runner/main.cpp new file mode 100644 index 00000000..406ee939 --- /dev/null +++ b/plugins/flutter/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"pocketpy_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/plugins/flutter/example/windows/runner/resource.h b/plugins/flutter/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/plugins/flutter/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/flutter/example/windows/runner/resources/app_icon.ico b/plugins/flutter/example/windows/runner/resources/app_icon.ico new file mode 100644 index 00000000..c04e20ca Binary files /dev/null and b/plugins/flutter/example/windows/runner/resources/app_icon.ico differ diff --git a/plugins/flutter/example/windows/runner/runner.exe.manifest b/plugins/flutter/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/plugins/flutter/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/plugins/flutter/example/windows/runner/utils.cpp b/plugins/flutter/example/windows/runner/utils.cpp new file mode 100644 index 00000000..f5bf9fa0 --- /dev/null +++ b/plugins/flutter/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/plugins/flutter/example/windows/runner/utils.h b/plugins/flutter/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/plugins/flutter/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/plugins/flutter/example/windows/runner/win32_window.cpp b/plugins/flutter/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/plugins/flutter/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/plugins/flutter/example/windows/runner/win32_window.h b/plugins/flutter/example/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/plugins/flutter/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/plugins/flutter/ios/Classes/pocketpy.cpp b/plugins/flutter/ios/Classes/pocketpy.cpp new file mode 100644 index 00000000..8ea93da5 --- /dev/null +++ b/plugins/flutter/ios/Classes/pocketpy.cpp @@ -0,0 +1,3 @@ +// Relative import to be able to reuse the C sources. +// See the comment in ../{projectName}}.podspec for more information. +#include "../../src/pocketpy.h" diff --git a/plugins/flutter/ios/pocketpy.podspec b/plugins/flutter/ios/pocketpy.podspec new file mode 100644 index 00000000..cd89432d --- /dev/null +++ b/plugins/flutter/ios/pocketpy.podspec @@ -0,0 +1,33 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint pocketpy.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'pocketpy' + s.version = '0.0.1' + s.summary = 'A new Flutter FFI plugin project.' + s.description = <<-DESC +A new Flutter FFI plugin project. + DESC + s.homepage = 'https://pocketpy.dev' + s.license = { :file => '../LICENSE' } + s.author = { 'blueloveTH' => 'blueloveth@hclcat.games' } + + # This will ensure the source files in Classes/ are included in the native + # builds of apps using this FFI plugin. Podspec does not support relative + # paths, so Classes contains a forwarder C file that relatively imports + # `../src/*` so that the C sources can be shared among all target platforms. + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '9.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'CLANG_CXX_LIBRARY' => 'libc++' + } + s.swift_version = '5.0' +end diff --git a/plugins/flutter/lib/jsonrpc.dart b/plugins/flutter/lib/jsonrpc.dart new file mode 100644 index 00000000..8d8e3207 --- /dev/null +++ b/plugins/flutter/lib/jsonrpc.dart @@ -0,0 +1,146 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers, non_constant_identifier_names + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:pocketpy/pocketpy.dart'; + +class JsonRpcError { + final Map payload = {}; + + JsonRpcError(int code, String message, {dynamic data}) { + payload['code'] = code; + payload['message'] = message; + if (data != null) { + payload['data'] = data; + } + } +} + +class JsonRpcServer { + final Map Function(List)> _methods = {}; + + final void Function()? onPreDispatch; + final void Function()? onPostDispatch; + final bool enableFileAccess; + + JsonRpcServer( + {this.onPreDispatch, + this.onPostDispatch, + this.enableFileAccess = false}) { + if (!enableFileAccess) return; + registerOS(this); + } + + void register(String name, FutureOr Function(List) method) { + _methods[name] = method; + } + + FutureOr _dispatch(ThreadedVM vm) { + if (vm.state != ThreadState.suspended) throw Exception("Unexpected state"); + String? json = vm.read_jsonrpc_request(); + if (json == null) throw Exception("JSONRPC request is null"); + var request = jsonDecode(json); + var f = _methods[request['method']]; + if (f == null) throw JsonRpcError(-32601, "Method not found"); + try { + return f(request['params'] as List); + } catch (e) { + throw JsonRpcError(-32000, e.toString()); + } + } + + FutureOr dispatch(ThreadedVM vm) async { + onPreDispatch?.call(); + try { + dynamic ret = _dispatch(vm); + if (ret is Future) ret = await ret; + vm.write_jsonrpc_response(jsonEncode({"result": ret})); + onPostDispatch?.call(); + } on JsonRpcError catch (e) { + vm.write_jsonrpc_response(jsonEncode({"error": e.payload})); + return; + } + } + + Future attach(ThreadedVM vm, + {Duration? spinFreq = const Duration(milliseconds: 20)}) async { + while (vm.state.index <= ThreadState.running.index) { + if (spinFreq != null) await Future.delayed(spinFreq); + } + switch (vm.state) { + case ThreadState.suspended: + await dispatch(vm); + await attach(vm, spinFreq: spinFreq); + break; + case ThreadState.finished: + break; + default: + throw Exception("Unexpected state"); + } + } + + int _fileId = 0; + final Map _files = {}; + + void registerOS(JsonRpcServer rpcServer) { + rpcServer.register("fopen", (params) { + var path = params[0]; + //var mode = params[1]; + var fp = File(path); + _fileId += 1; + _files[_fileId] = fp; + return _fileId; + }); + + rpcServer.register("fclose", (params) { + var fp = _files[params[0]]; + if (fp == null) throw Exception("FileIO was closed"); + _files.remove(params[0]); + }); + + rpcServer.register("fread", (params) { + var fp = _files[params[0]]; + if (fp == null) throw Exception("FileIO was closed"); + return fp.readAsStringSync(); + }); + + rpcServer.register("fwrite", (params) { + var fp = _files[params[0]]; + if (fp == null) throw Exception("FileIO was closed"); + fp.writeAsStringSync(params[1]); + }); + + rpcServer.register("os.listdir", (params) { + String path = params[0]; + var entries = Directory(path).listSync(followLinks: false); + var ret = entries.map((e) { + return e.path.split(Platform.pathSeparator).last; + }).toList(); + return ret; + }); + + rpcServer.register("os.mkdir", (params) { + String path = params[0]; + Directory(path).createSync(); + }); + + rpcServer.register("os.rmdir", (params) { + String path = params[0]; + Directory(path).deleteSync(recursive: true); + }); + + rpcServer.register("os.remove", (params) { + String path = params[0]; + File(path).deleteSync(); + }); + + rpcServer.register("os.path.exists", (params) { + String path = params[0]; + bool _0 = Directory(path).existsSync(); + bool _1 = File(path).existsSync(); + return (_0 || _1); + }); + } +} diff --git a/plugins/flutter/lib/pocketpy.dart b/plugins/flutter/lib/pocketpy.dart new file mode 100644 index 00000000..29608ed9 --- /dev/null +++ b/plugins/flutter/lib/pocketpy.dart @@ -0,0 +1,188 @@ +// ignore_for_file: non_constant_identifier_names, prefer_typing_uninitialized_variables, constant_identifier_names, no_leading_underscores_for_local_identifiers + +import 'dart:convert' as cvt; +import 'dart:ffi' as ffi; +import 'dart:io'; +import 'package:ffi/ffi.dart'; + +export 'jsonrpc.dart'; + +class Bindings +{ + static ffi.DynamicLibrary _load() { + String _libName = "pocketpy"; + if (Platform.isIOS) { + return ffi.DynamicLibrary.process(); + } + if (Platform.isAndroid || Platform.isLinux) { + return ffi.DynamicLibrary.open('lib$_libName.so'); + } + if (Platform.isWindows) { + return ffi.DynamicLibrary.open('$_libName.dll'); + } + throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); + } + + static final _lib = _load(); + + static final pkpy_delete = _lib.lookupFunction("pkpy_delete"); + static final pkpy_new_repl = _lib.lookupFunction("pkpy_new_repl"); + static final pkpy_repl_input = _lib.lookupFunction line), int Function(ffi.Pointer r, ffi.Pointer line)>("pkpy_repl_input"); + static final pkpy_new_tvm = _lib.lookupFunction("pkpy_new_tvm"); + static final pkpy_tvm_exec_async = _lib.lookupFunction source), bool Function(ffi.Pointer vm, ffi.Pointer source)>("pkpy_tvm_exec_async"); + static final pkpy_tvm_get_state = _lib.lookupFunction("pkpy_tvm_get_state"); + static final pkpy_tvm_read_jsonrpc_request = _lib.lookupFunction Function(ffi.Pointer vm), ffi.Pointer Function(ffi.Pointer vm)>("pkpy_tvm_read_jsonrpc_request"); + static final pkpy_tvm_reset_state = _lib.lookupFunction("pkpy_tvm_reset_state"); + static final pkpy_tvm_terminate = _lib.lookupFunction("pkpy_tvm_terminate"); + static final pkpy_tvm_write_jsonrpc_response = _lib.lookupFunction value), void Function(ffi.Pointer vm, ffi.Pointer value)>("pkpy_tvm_write_jsonrpc_response"); + static final pkpy_new_vm = _lib.lookupFunction("pkpy_new_vm"); + static final pkpy_vm_add_module = _lib.lookupFunction name, ffi.Pointer source), bool Function(ffi.Pointer vm, ffi.Pointer name, ffi.Pointer source)>("pkpy_vm_add_module"); + static final pkpy_vm_eval = _lib.lookupFunction Function(ffi.Pointer vm, ffi.Pointer source), ffi.Pointer Function(ffi.Pointer vm, ffi.Pointer source)>("pkpy_vm_eval"); + static final pkpy_vm_exec = _lib.lookupFunction source), bool Function(ffi.Pointer vm, ffi.Pointer source)>("pkpy_vm_exec"); + static final pkpy_vm_get_global = _lib.lookupFunction Function(ffi.Pointer vm, ffi.Pointer name), ffi.Pointer Function(ffi.Pointer vm, ffi.Pointer name)>("pkpy_vm_get_global"); + static final pkpy_vm_read_output = _lib.lookupFunction Function(ffi.Pointer vm), ffi.Pointer Function(ffi.Pointer vm)>("pkpy_vm_read_output"); +} + + +class PyOutput { + final String stdout; + final String stderr; + PyOutput(this.stdout, this.stderr); + + PyOutput.fromJson(Map json) + : stdout = json['stdout'], stderr = json['stderr']; +} + +class Str { + static final Finalizer> finalizer = Finalizer((p) => calloc.free(p)); + + late final ffi.Pointer _p; + Str(String s) { + _p = s.toNativeUtf8(); + finalizer.attach(this, _p); + } + + ffi.Pointer get p => _p; +} + +class VM { + late final ffi.Pointer pointer; + + VM() { + if (this is ThreadedVM) { + pointer = Bindings.pkpy_new_tvm(false); + } else { + pointer = Bindings.pkpy_new_vm(false); + } + } + + void dispose() { + Bindings.pkpy_delete(pointer); + } + + PyOutput read_output() { + var _o = Bindings.pkpy_vm_read_output(pointer); + String _j = _o.toDartString(); + var ret = PyOutput.fromJson(cvt.jsonDecode(_j)); + Bindings.pkpy_delete(_o); + return ret; + } + + /// Add a source module into a virtual machine. Return `true` if there is no complie error. + bool add_module(String name, String source) + { + var ret = Bindings.pkpy_vm_add_module(pointer, Str(name).p, Str(source).p); + return ret; + } + + /// Evaluate an expression. Return a json representing the result. If there is any error, return `nullptr`. + String? eval(String source) + { + var ret = Bindings.pkpy_vm_eval(pointer, Str(source).p); + if (ret == ffi.nullptr) return null; + String s = ret.toDartString(); + calloc.free(ret); + return s; + } + + /// Run a given source on a virtual machine. Return `true` if there is no compile error. + bool exec(String source) + { + var ret = Bindings.pkpy_vm_exec(pointer, Str(source).p); + return ret; + } + + /// Get a global variable of a virtual machine. Return a json representing the result. If the variable is not found, return `nullptr`. + String? get_global(String name) + { + var ret = Bindings.pkpy_vm_get_global(pointer, Str(name).p); + if (ret == ffi.nullptr) return null; + String s = ret.toDartString(); + calloc.free(ret); + return s; + } + +} + +enum ThreadState { ready, running, suspended, finished } + +class ThreadedVM extends VM { + ThreadState get state => ThreadState.values[Bindings.pkpy_tvm_get_state(pointer)]; + + /// Run a given source on a threaded virtual machine. The excution will be started in a new thread. Return `true` if there is no compile error. + bool exec_async(String source) + { + var ret = Bindings.pkpy_tvm_exec_async(pointer, Str(source).p); + return ret; + } + + /// Read the current JSONRPC request from shared string buffer. + String? read_jsonrpc_request() + { + var ret = Bindings.pkpy_tvm_read_jsonrpc_request(pointer); + if (ret == ffi.nullptr) return null; + String s = ret.toDartString(); + calloc.free(ret); + return s; + } + + /// Set the state of a threaded virtual machine to `THREAD_READY`. The current state should be `THREAD_FINISHED`. + void reset_state() + { + Bindings.pkpy_tvm_reset_state(pointer); + } + + /// Emit a KeyboardInterrupt signal to stop a running threaded virtual machine. + void terminate() + { + Bindings.pkpy_tvm_terminate(pointer); + } + + /// Write a JSONRPC response to shared string buffer. + void write_jsonrpc_response(String value) + { + Bindings.pkpy_tvm_write_jsonrpc_response(pointer, Str(value).p); + } + +} + +class REPL { + late final ffi.Pointer pointer; + + REPL(VM vm) { + pointer = Bindings.pkpy_new_repl(vm.pointer); + } + + void dispose() { + Bindings.pkpy_delete(pointer); + } + + /// Input a source line to an interactive console. Return `0` if need more lines, `1` if execution happened, `2` if execution skipped (compile error or empty input). + int input(String line) + { + var ret = Bindings.pkpy_repl_input(pointer, Str(line).p); + return ret; + } + +} + diff --git a/plugins/flutter/pubspec.yaml b/plugins/flutter/pubspec.yaml new file mode 100644 index 00000000..cbd60930 --- /dev/null +++ b/plugins/flutter/pubspec.yaml @@ -0,0 +1,29 @@ +name: pocketpy +description: A lightweight Python interpreter for game engines. +version: 0.4.7 +homepage: https://pocketpy.dev +repository: https://github.com/blueloveth/pocketpy + +environment: + sdk: '>=2.18.2 <3.0.0' + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + ffi: ^2.0.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +flutter: + plugin: + platforms: + android: + ffiPlugin: true + ios: + ffiPlugin: true + windows: + ffiPlugin: true \ No newline at end of file diff --git a/plugins/flutter/src/CMakeLists.txt b/plugins/flutter/src/CMakeLists.txt new file mode 100644 index 00000000..f56285be --- /dev/null +++ b/plugins/flutter/src/CMakeLists.txt @@ -0,0 +1,23 @@ +# The Flutter tooling requires that developers have CMake 3.10 or later +# installed. You should not increase this version, as doing so will cause +# the plugin to fail to compile for some customers of the plugin. +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(ANDROID_STL c++_static) + +project(pocketpy_library VERSION 0.0.1 LANGUAGES CXX) + +add_library(pocketpy SHARED + "pocketpy.cpp" +) + +add_compile_options("$<$:/utf-8>") + +set_target_properties(pocketpy PROPERTIES + PUBLIC_HEADER pocketpy.h + OUTPUT_NAME "pocketpy" +) + +target_compile_definitions(pocketpy PUBLIC DART_SHARED_LIB) diff --git a/plugins/flutter/src/pocketpy.cpp b/plugins/flutter/src/pocketpy.cpp new file mode 100644 index 00000000..a11446e0 --- /dev/null +++ b/plugins/flutter/src/pocketpy.cpp @@ -0,0 +1 @@ +#include "pocketpy.h" \ No newline at end of file diff --git a/plugins/flutter/src/pocketpy.h b/plugins/flutter/src/pocketpy.h new file mode 100644 index 00000000..44499df4 --- /dev/null +++ b/plugins/flutter/src/pocketpy.h @@ -0,0 +1,6989 @@ +/* + * Copyright (c) 2022 blueloveTH + * Distributed Under The GNU General Public License v2.0 + */ + +#ifndef POCKETPY_H +#define POCKETPY_H + + +#ifdef _MSC_VER +#pragma warning (disable:4267) +#pragma warning (disable:4101) +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef POCKETPY_H +#define UNREACHABLE() throw std::runtime_error( "L" + std::to_string(__LINE__) + " UNREACHABLE()! This should be a bug, please report it"); +#else +#define UNREACHABLE() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " UNREACHABLE()!"); +#endif + +#define PK_VERSION "0.4.6" + + +namespace pkpy{ + template + class shared_ptr { + int* count; + T* ptr; + + inline void _delete(){ + delete count; + delete ptr; + } + + public: + shared_ptr() : count(nullptr), ptr(nullptr) {} + shared_ptr(T* ptr) : count(new int(1)), ptr(ptr) {} + shared_ptr(const shared_ptr& other) : count(other.count), ptr(other.ptr) { + if(count) (*count)++; + } + shared_ptr(shared_ptr&& other) : count(other.count), ptr(other.ptr) { + other.count = nullptr; + other.ptr = nullptr; + } + ~shared_ptr() { + if (count && --(*count) == 0) _delete(); + } + + bool operator==(const shared_ptr& other) const { + return ptr == other.ptr; + } + + bool operator!=(const shared_ptr& other) const { + return ptr != other.ptr; + } + + bool operator==(std::nullptr_t) const { + return ptr == nullptr; + } + + bool operator!=(std::nullptr_t) const { + return ptr != nullptr; + } + + shared_ptr& operator=(const shared_ptr& other) { + if (this != &other) { + if (count && --(*count) == 0) _delete(); + count = other.count; + ptr = other.ptr; + if (count) ++(*count); + } + return *this; + } + + shared_ptr& operator=(shared_ptr&& other) { + if (this != &other) { + if (count && --(*count) == 0) _delete(); + count = other.count; + ptr = other.ptr; + other.count = nullptr; + other.ptr = nullptr; + } + return *this; + } + + T& operator*() const { + return *ptr; + } + T* operator->() const { + return ptr; + } + T* get() const { + return ptr; + } + int use_count() const { + return count ? *count : 0; + } + + void reset(){ + if (count && --(*count) == 0) _delete(); + count = nullptr; + ptr = nullptr; + } + }; + + template + shared_ptr make_shared(Args&&... args) { + return shared_ptr(new T(std::forward(args)...)); + } + + + template + class unique_ptr { + T* ptr; + + public: + unique_ptr() : ptr(nullptr) {} + unique_ptr(T* ptr) : ptr(ptr) {} + unique_ptr(const unique_ptr& other) = delete; + unique_ptr(unique_ptr&& other) : ptr(other.ptr) { + other.ptr = nullptr; + } + ~unique_ptr() { + delete ptr; + } + + bool operator==(const unique_ptr& other) const { + return ptr == other.ptr; + } + + bool operator!=(const unique_ptr& other) const { + return ptr != other.ptr; + } + + bool operator==(std::nullptr_t) const { + return ptr == nullptr; + } + + bool operator!=(std::nullptr_t) const { + return ptr != nullptr; + } + + unique_ptr& operator=(const unique_ptr& other) = delete; + + unique_ptr& operator=(unique_ptr&& other) { + if (this != &other) { + delete ptr; + ptr = other.ptr; + other.ptr = nullptr; + } + return *this; + } + + T& operator*() const { + return *ptr; + } + T* operator->() const { + return ptr; + } + T* get() const { + return ptr; + } + + void reset(){ + delete ptr; + ptr = nullptr; + } + }; + + template + unique_ptr make_unique(Args&&... args) { + return unique_ptr(new T(std::forward(args)...)); + } +}; + + +typedef std::stringstream _StrStream; + +class _StrMemory : public std::string { + mutable std::vector* _u8_index = nullptr; + + mutable bool hash_initialized = false; + mutable size_t _hash; + + void utf8_lazy_init() const{ + if(_u8_index != nullptr) return; + _u8_index = new std::vector(); + _u8_index->reserve(size()); + if(size() > 65535) throw std::runtime_error("String has more than 65535 bytes."); + for(uint16_t i = 0; i < size(); i++){ + // https://stackoverflow.com/questions/3911536/utf-8-unicode-whats-with-0xc0-and-0x80 + if((at(i) & 0xC0) != 0x80) + _u8_index->push_back(i); + } + } +public: + size_t hash() const{ + if(!hash_initialized){ + _hash = std::hash()(*this); + hash_initialized = true; + } + return _hash; + } + + int u8_length() const { + utf8_lazy_init(); + return _u8_index->size(); + } + + std::string u8_getitem(int i) const{ + return u8_substr(i, i+1); + } + + std::string u8_substr(int start, int end) const{ + utf8_lazy_init(); + if(start >= end) return std::string(); + int c_end = end >= _u8_index->size() ? size() : _u8_index->at(end); + return substr(_u8_index->at(start), c_end - _u8_index->at(start)); + } + + _StrMemory(const std::string& s) : std::string(s) {} + _StrMemory(std::string&& s) : std::string(std::move(s)) {} + + ~_StrMemory(){ + if(_u8_index != nullptr) delete _u8_index; + } +}; + + +std::map, std::less<>> _strIntern; + + +class _StrLiteral : public std::string_view { +public: + constexpr _StrLiteral(const char* str, size_t len) : std::string_view(str, len) {} +}; + +inline constexpr _StrLiteral operator "" _c(const char* str, size_t len){ + return _StrLiteral(str, len); +} + +class _Str { +private: + pkpy::shared_ptr<_StrMemory> _s; + bool interned = false; +public: + _Str(const _StrLiteral& s){ + construct(s); + intern(); + } + _Str(const char* s){ + construct(s); + } + _Str(const char* s, size_t len){ + construct(std::string_view(s, len)); + } + _Str(){ + construct(""); + } + _Str(const std::string& s){ + construct(s); + } + _Str(const _Str& s) : _s(s._s), interned(s.interned) {} + + // for move constructor, we do not check if the string is interned!! + _Str(std::string&& s){ + this->_s = pkpy::make_shared<_StrMemory>(std::move(s)); + } + + void construct(const std::string_view& sv){ + auto it = _strIntern.find(sv); + if(it != _strIntern.end()){ + this->_s = it->second; + interned = true; + }else{ + this->_s = pkpy::make_shared<_StrMemory>(std::string(sv)); + } + } + + // force the string to be interned + void intern(){ + if(interned) return; + auto it = _strIntern.find(*this->_s); + if(it == _strIntern.end()) _strIntern[*this->_s] = this->_s; + else this->_s = it->second; + interned = true; + } + + inline int u8_length() const { + return this->_s->u8_length(); + } + + inline _Str u8_getitem(int i) const{ + return _Str(this->_s->u8_getitem(i)); + } + + inline _Str u8_substr(int start, int end) const{ + return _Str(this->_s->u8_substr(start, end)); + } + + inline size_t hash() const{ + return _s->hash(); + } + + inline int size() const { + return _s->size(); + } + + inline bool empty() const { + return _s->empty(); + } + + bool operator==(const _Str& other) const { + if(interned && other.interned) return _s == other._s; + return *_s == *other._s; + } + + bool operator!=(const _Str& other) const { + if(interned && other.interned) return _s != other._s; + return *_s != *other._s; + } + + bool operator<(const _Str& other) const { + return *_s < *other._s; + } + + bool operator>(const _Str& other) const { + return *_s > *other._s; + } + + char operator[](int i) const { + return _s->at(i); + } + + friend std::ostream& operator<<(std::ostream& os, const _Str& s) { + os << *s._s; + return os; + } + + _Str operator+(const _Str& other) const { + return _Str(*_s + *other._s); + } + + _Str operator+(const char* other) const { + return _Str(*_s + other); + } + + _Str operator+(const std::string& other) const { + return _Str(*_s + other); + } + + friend _Str operator+(const char* other, const _Str& s){ + return _Str(other + *s._s); + } + + friend _Str operator+(const std::string& other, const _Str& s){ + return _Str(other + *s._s); + } + + const std::string& str() const { + return *_s; + } + + const char* c_str() const { + return _s->c_str(); + } + + static const std::size_t npos = std::string::npos; + + _Str __lstrip() const { + std::string copy(*_s); + copy.erase(copy.begin(), std::find_if(copy.begin(), copy.end(), [](char c) { + return !std::isspace(c); + })); + return _Str(copy); + } + + _Str __escape(bool single_quote) const { + _StrStream ss; + ss << (single_quote ? '\'' : '"'); + for (auto c = _s->cbegin(); c != _s->cend(); c++) { + switch (*c) { + case '"': + if(!single_quote) ss << '\\'; + ss << '"'; + break; + case '\'': + if(single_quote) ss << '\\'; + ss << '\''; + break; + case '\\': ss << '\\' << '\\'; break; + case '\n': ss << "\\n"; break; + case '\r': ss << "\\r"; break; + case '\t': ss << "\\t"; break; + default: + if ('\x00' <= *c && *c <= '\x1f') { + ss << "\\u" + << std::hex << std::setw(4) << std::setfill('0') << static_cast(*c); + } else { + ss << *c; + } + } + } + ss << (single_quote ? '\'' : '"'); + return ss.str(); + } +}; + +namespace std { + template<> + struct hash<_Str> { + std::size_t operator()(const _Str& s) const { + return s.hash(); + } + }; +} + +// const _Str& __class__ = _Str("__class__"_c); +const _Str& __base__ = _Str("__base__"_c); +const _Str& __new__ = _Str("__new__"_c); +const _Str& __iter__ = _Str("__iter__"_c); +const _Str& __str__ = _Str("__str__"_c); +const _Str& __repr__ = _Str("__repr__"_c); +const _Str& __module__ = _Str("__module__"_c); +const _Str& __getitem__ = _Str("__getitem__"_c); +const _Str& __setitem__ = _Str("__setitem__"_c); +const _Str& __delitem__ = _Str("__delitem__"_c); +const _Str& __contains__ = _Str("__contains__"_c); +const _Str& __init__ = _Str("__init__"_c); +const _Str& __json__ = _Str("__json__"_c); +const _Str& __name__ = _Str("__name__"_c); +const _Str& __len__ = _Str("__len__"_c); + +const _Str CMP_SPECIAL_METHODS[] = { + "__lt__"_c, "__le__"_c, "__eq__"_c, "__ne__"_c, "__gt__"_c, "__ge__"_c +}; // __ne__ should not be used + +const _Str BINARY_SPECIAL_METHODS[] = { + "__add__"_c, "__sub__"_c, "__mul__"_c, "__truediv__"_c, "__floordiv__"_c, "__mod__"_c, "__pow__"_c +}; + +const _Str BITWISE_SPECIAL_METHODS[] = { + "__lshift__"_c, "__rshift__"_c, "__and__"_c, "__or__"_c, "__xor__"_c +}; + +const uint32_t __LoRangeA[] = {170,186,443,448,660,1488,1519,1568,1601,1646,1649,1749,1774,1786,1791,1808,1810,1869,1969,1994,2048,2112,2144,2208,2230,2308,2365,2384,2392,2418,2437,2447,2451,2474,2482,2486,2493,2510,2524,2527,2544,2556,2565,2575,2579,2602,2610,2613,2616,2649,2654,2674,2693,2703,2707,2730,2738,2741,2749,2768,2784,2809,2821,2831,2835,2858,2866,2869,2877,2908,2911,2929,2947,2949,2958,2962,2969,2972,2974,2979,2984,2990,3024,3077,3086,3090,3114,3133,3160,3168,3200,3205,3214,3218,3242,3253,3261,3294,3296,3313,3333,3342,3346,3389,3406,3412,3423,3450,3461,3482,3507,3517,3520,3585,3634,3648,3713,3716,3718,3724,3749,3751,3762,3773,3776,3804,3840,3904,3913,3976,4096,4159,4176,4186,4193,4197,4206,4213,4238,4352,4682,4688,4696,4698,4704,4746,4752,4786,4792,4800,4802,4808,4824,4882,4888,4992,5121,5743,5761,5792,5873,5888,5902,5920,5952,5984,5998,6016,6108,6176,6212,6272,6279,6314,6320,6400,6480,6512,6528,6576,6656,6688,6917,6981,7043,7086,7098,7168,7245,7258,7401,7406,7413,7418,8501,11568,11648,11680,11688,11696,11704,11712,11720,11728,11736,12294,12348,12353,12447,12449,12543,12549,12593,12704,12784,13312,19968,40960,40982,42192,42240,42512,42538,42606,42656,42895,42999,43003,43011,43015,43020,43072,43138,43250,43259,43261,43274,43312,43360,43396,43488,43495,43514,43520,43584,43588,43616,43633,43642,43646,43697,43701,43705,43712,43714,43739,43744,43762,43777,43785,43793,43808,43816,43968,44032,55216,55243,63744,64112,64285,64287,64298,64312,64318,64320,64323,64326,64467,64848,64914,65008,65136,65142,65382,65393,65440,65474,65482,65490,65498,65536,65549,65576,65596,65599,65616,65664,66176,66208,66304,66349,66370,66384,66432,66464,66504,66640,66816,66864,67072,67392,67424,67584,67592,67594,67639,67644,67647,67680,67712,67808,67828,67840,67872,67968,68030,68096,68112,68117,68121,68192,68224,68288,68297,68352,68416,68448,68480,68608,68864,69376,69415,69424,69600,69635,69763,69840,69891,69956,69968,70006,70019,70081,70106,70108,70144,70163,70272,70280,70282,70287,70303,70320,70405,70415,70419,70442,70450,70453,70461,70480,70493,70656,70727,70751,70784,70852,70855,71040,71128,71168,71236,71296,71352,71424,71680,71935,72096,72106,72161,72163,72192,72203,72250,72272,72284,72349,72384,72704,72714,72768,72818,72960,72968,72971,73030,73056,73063,73066,73112,73440,73728,74880,77824,82944,92160,92736,92880,92928,93027,93053,93952,94032,94208,100352,110592,110928,110948,110960,113664,113776,113792,113808,123136,123214,123584,124928,126464,126469,126497,126500,126503,126505,126516,126521,126523,126530,126535,126537,126539,126541,126545,126548,126551,126553,126555,126557,126559,126561,126564,126567,126572,126580,126585,126590,126592,126603,126625,126629,126635,131072,173824,177984,178208,183984,194560}; +const uint32_t __LoRangeB[] = {170,186,443,451,660,1514,1522,1599,1610,1647,1747,1749,1775,1788,1791,1808,1839,1957,1969,2026,2069,2136,2154,2228,2237,2361,2365,2384,2401,2432,2444,2448,2472,2480,2482,2489,2493,2510,2525,2529,2545,2556,2570,2576,2600,2608,2611,2614,2617,2652,2654,2676,2701,2705,2728,2736,2739,2745,2749,2768,2785,2809,2828,2832,2856,2864,2867,2873,2877,2909,2913,2929,2947,2954,2960,2965,2970,2972,2975,2980,2986,3001,3024,3084,3088,3112,3129,3133,3162,3169,3200,3212,3216,3240,3251,3257,3261,3294,3297,3314,3340,3344,3386,3389,3406,3414,3425,3455,3478,3505,3515,3517,3526,3632,3635,3653,3714,3716,3722,3747,3749,3760,3763,3773,3780,3807,3840,3911,3948,3980,4138,4159,4181,4189,4193,4198,4208,4225,4238,4680,4685,4694,4696,4701,4744,4749,4784,4789,4798,4800,4805,4822,4880,4885,4954,5007,5740,5759,5786,5866,5880,5900,5905,5937,5969,5996,6000,6067,6108,6210,6264,6276,6312,6314,6389,6430,6509,6516,6571,6601,6678,6740,6963,6987,7072,7087,7141,7203,7247,7287,7404,7411,7414,7418,8504,11623,11670,11686,11694,11702,11710,11718,11726,11734,11742,12294,12348,12438,12447,12538,12543,12591,12686,12730,12799,19893,40943,40980,42124,42231,42507,42527,42539,42606,42725,42895,42999,43009,43013,43018,43042,43123,43187,43255,43259,43262,43301,43334,43388,43442,43492,43503,43518,43560,43586,43595,43631,43638,43642,43695,43697,43702,43709,43712,43714,43740,43754,43762,43782,43790,43798,43814,43822,44002,55203,55238,55291,64109,64217,64285,64296,64310,64316,64318,64321,64324,64433,64829,64911,64967,65019,65140,65276,65391,65437,65470,65479,65487,65495,65500,65547,65574,65594,65597,65613,65629,65786,66204,66256,66335,66368,66377,66421,66461,66499,66511,66717,66855,66915,67382,67413,67431,67589,67592,67637,67640,67644,67669,67702,67742,67826,67829,67861,67897,68023,68031,68096,68115,68119,68149,68220,68252,68295,68324,68405,68437,68466,68497,68680,68899,69404,69415,69445,69622,69687,69807,69864,69926,69956,70002,70006,70066,70084,70106,70108,70161,70187,70278,70280,70285,70301,70312,70366,70412,70416,70440,70448,70451,70457,70461,70480,70497,70708,70730,70751,70831,70853,70855,71086,71131,71215,71236,71338,71352,71450,71723,71935,72103,72144,72161,72163,72192,72242,72250,72272,72329,72349,72440,72712,72750,72768,72847,72966,72969,73008,73030,73061,73064,73097,73112,73458,74649,75075,78894,83526,92728,92766,92909,92975,93047,93071,94026,94032,100343,101106,110878,110930,110951,111355,113770,113788,113800,113817,123180,123214,123627,125124,126467,126495,126498,126500,126503,126514,126519,126521,126523,126530,126535,126537,126539,126543,126546,126548,126551,126553,126555,126557,126559,126562,126564,126570,126578,126583,126588,126590,126601,126619,126627,126633,126651,173782,177972,178205,183969,191456,195101}; + +bool __isLoChar(uint32_t c) { + auto index = std::lower_bound(__LoRangeA, __LoRangeA + 476, c) - __LoRangeA; + if(c == __LoRangeA[index]) return true; + index -= 1; + if(index < 0) return false; + return c >= __LoRangeA[index] && c <= __LoRangeB[index]; +} +// emhash8::HashMap for C++11/14/17 +// version 1.6.3 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019-2022 Huang Yuanbing & bailuzhou AT 163.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef EMH_KEY + #undef EMH_KEY + #undef EMH_VAL + #undef EMH_KV + #undef EMH_BUCKET + #undef EMH_NEW + #undef EMH_EMPTY + #undef EMH_PREVET + #undef EMH_LIKELY + #undef EMH_UNLIKELY +#endif + +// likely/unlikely +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +# define EMH_LIKELY(condition) __builtin_expect(condition, 1) +# define EMH_UNLIKELY(condition) __builtin_expect(condition, 0) +#else +# define EMH_LIKELY(condition) (condition) +# define EMH_UNLIKELY(condition) (condition) +#endif + +#define EMH_KEY(p, n) p[n].first +#define EMH_VAL(p, n) p[n].second +#define EMH_KV(p, n) p[n] + +#define EMH_INDEX(i, n) i[n] +#define EMH_BUCKET(i, n) i[n].bucket +#define EMH_HSLOT(i, n) i[n].slot +#define EMH_SLOT(i, n) (i[n].slot & _mask) +#define EMH_PREVET(i, n) i[n].slot + +#define EMH_KEYMASK(key, mask) ((size_type)(key) & ~mask) +#define EMH_EQHASH(n, key_hash) (EMH_KEYMASK(key_hash, _mask) == (_index[n].slot & ~_mask)) +#define EMH_NEW(key, val, bucket, key_hash) \ + new(_pairs + _num_filled) value_type(key, val); \ + _etail = bucket; \ + _index[bucket] = {bucket, _num_filled++ | EMH_KEYMASK(key_hash, _mask)} + +#define EMH_EMPTY(i, n) (0 > (int)i[n].bucket) + +namespace emhash8 { + +#ifndef EMH_DEFAULT_LOAD_FACTOR + constexpr static float EMH_DEFAULT_LOAD_FACTOR = 0.80f; + constexpr static float EMH_MIN_LOAD_FACTOR = 0.25f; //< 0.5 +#endif +#if EMH_CACHE_LINE_SIZE < 32 + constexpr static uint32_t EMH_CACHE_LINE_SIZE = 64; +#endif + +template , typename EqT = std::equal_to> +class HashMap +{ +public: + using htype = HashMap; + using value_type = std::pair; + using key_type = KeyT; + using mapped_type = ValueT; + +#ifdef EMH_SMALL_TYPE + using size_type = uint16_t; +#elif EMH_SIZE_TYPE == 0 + using size_type = uint32_t; +#else + using size_type = size_t; +#endif + + using hasher = HashT; + using key_equal = EqT; + + constexpr static size_type INACTIVE = 0-1u; + //constexpr uint32_t END = 0-0x1u; + constexpr static size_type EAD = 2; + + struct Index + { + size_type bucket; + size_type slot; + }; + + class const_iterator; + class iterator + { + public: + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = typename htype::value_type; + using pointer = value_type*; + using const_pointer = const value_type* ; + using reference = value_type&; + using const_reference = const value_type&; + + iterator() : kv_(nullptr) {} + iterator(const_iterator& cit) { + kv_ = cit.kv_; + } + + iterator(const htype* hash_map, size_type bucket) { + kv_ = hash_map->_pairs + (int)bucket; + } + + iterator& operator++() + { + kv_ ++; + return *this; + } + + iterator operator++(int) + { + auto cur = *this; kv_ ++; + return cur; + } + + iterator& operator--() + { + kv_ --; + return *this; + } + + iterator operator--(int) + { + auto cur = *this; kv_ --; + return cur; + } + + reference operator*() const { return *kv_; } + pointer operator->() const { return kv_; } + + bool operator == (const iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const iterator& rhs) const { return kv_ != rhs.kv_; } + bool operator == (const const_iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const const_iterator& rhs) const { return kv_ != rhs.kv_; } + + public: + value_type* kv_; + }; + + class const_iterator + { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename htype::value_type; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + + const_iterator(const iterator& it) { + kv_ = it.kv_; + } + + const_iterator (const htype* hash_map, size_type bucket) { + kv_ = hash_map->_pairs + (int)bucket; + } + + const_iterator& operator++() + { + kv_ ++; + return *this; + } + + const_iterator operator++(int) + { + auto cur = *this; kv_ ++; + return cur; + } + + const_iterator& operator--() + { + kv_ --; + return *this; + } + + const_iterator operator--(int) + { + auto cur = *this; kv_ --; + return cur; + } + + const_reference operator*() const { return *kv_; } + const_pointer operator->() const { return kv_; } + + bool operator == (const iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const iterator& rhs) const { return kv_ != rhs.kv_; } + bool operator == (const const_iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const const_iterator& rhs) const { return kv_ != rhs.kv_; } + public: + const value_type* kv_; + }; + + void init(size_type bucket, float mlf = EMH_DEFAULT_LOAD_FACTOR) + { + _pairs = nullptr; + _index = nullptr; + _mask = _num_buckets = 0; + _num_filled = 0; + max_load_factor(mlf); + rehash(bucket); + } + + HashMap(size_type bucket = 2, float mlf = EMH_DEFAULT_LOAD_FACTOR) + { + init(bucket, mlf); + } + + HashMap(const HashMap& rhs) + { + if (rhs.load_factor() > EMH_MIN_LOAD_FACTOR) { + _pairs = alloc_bucket((size_type)(rhs._num_buckets * rhs.max_load_factor()) + 4); + _index = alloc_index(rhs._num_buckets); + clone(rhs); + } else { + init(rhs._num_filled + 2, EMH_DEFAULT_LOAD_FACTOR); + for (auto it = rhs.begin(); it != rhs.end(); ++it) + insert_unique(it->first, it->second); + } + } + + HashMap(HashMap&& rhs) noexcept + { + init(0); + *this = std::move(rhs); + } + + HashMap(std::initializer_list ilist) + { + init((size_type)ilist.size()); + for (auto it = ilist.begin(); it != ilist.end(); ++it) + do_insert(*it); + } + + template + HashMap(InputIt first, InputIt last, size_type bucket_count=4) + { + init(std::distance(first, last) + bucket_count); + for (; first != last; ++first) + emplace(*first); + } + + HashMap& operator=(const HashMap& rhs) + { + if (this == &rhs) + return *this; + + if (rhs.load_factor() < EMH_MIN_LOAD_FACTOR) { + clear(); free(_pairs); _pairs = nullptr; + rehash(rhs._num_filled + 2); + for (auto it = rhs.begin(); it != rhs.end(); ++it) + insert_unique(it->first, it->second); + return *this; + } + + clearkv(); + + if (_num_buckets != rhs._num_buckets) { + free(_pairs); free(_index); + _index = alloc_index(rhs._num_buckets); + _pairs = alloc_bucket((size_type)(rhs._num_buckets * rhs.max_load_factor()) + 4); + } + + clone(rhs); + return *this; + } + + HashMap& operator=(HashMap&& rhs) noexcept + { + if (this != &rhs) { + swap(rhs); + rhs.clear(); + } + return *this; + } + + template + bool operator == (const Con& rhs) const + { + if (size() != rhs.size()) + return false; + + for (auto it = begin(), last = end(); it != last; ++it) { + auto oi = rhs.find(it->first); + if (oi == rhs.end() || it->second != oi->second) + return false; + } + return true; + } + + template + bool operator != (const Con& rhs) const { return !(*this == rhs); } + + ~HashMap() noexcept + { + clearkv(); + free(_pairs); + free(_index); + } + + void clone(const HashMap& rhs) + { + _hasher = rhs._hasher; +// _eq = rhs._eq; + _num_buckets = rhs._num_buckets; + _num_filled = rhs._num_filled; + _mlf = rhs._mlf; + _last = rhs._last; + _mask = rhs._mask; +#if EMH_HIGH_LOAD + _ehead = rhs._ehead; +#endif + _etail = rhs._etail; + + auto opairs = rhs._pairs; + memcpy((char*)_index, (char*)rhs._index, (_num_buckets + EAD) * sizeof(Index)); + + if (is_copy_trivially()) { + if (opairs) + memcpy((char*)_pairs, (char*)opairs, _num_filled * sizeof(value_type)); + } else { + for (size_type slot = 0; slot < _num_filled; slot++) + new(_pairs + slot) value_type(opairs[slot]); + } + } + + void swap(HashMap& rhs) + { + // std::swap(_eq, rhs._eq); + std::swap(_hasher, rhs._hasher); + std::swap(_pairs, rhs._pairs); + std::swap(_index, rhs._index); + std::swap(_num_buckets, rhs._num_buckets); + std::swap(_num_filled, rhs._num_filled); + std::swap(_mask, rhs._mask); + std::swap(_mlf, rhs._mlf); + std::swap(_last, rhs._last); +#if EMH_HIGH_LOAD + std::swap(_ehead, rhs._ehead); +#endif + std::swap(_etail, rhs._etail); + } + + // ------------------------------------------------------------- + inline iterator first() const { return {this, 0}; } + inline iterator last() const { return {this, _num_filled - 1}; } + + inline iterator begin() { return first(); } + inline const_iterator cbegin() const { return first(); } + inline const_iterator begin() const { return first(); } + + inline iterator end() { return {this, _num_filled}; } + inline const_iterator cend() const { return {this, _num_filled}; } + inline const_iterator end() const { return cend(); } + + inline const value_type* values() const { return _pairs; } + inline const Index* index() const { return _index; } + + inline size_type size() const { return _num_filled; } + inline bool empty() const { return _num_filled == 0; } + inline size_type bucket_count() const { return _num_buckets; } + + /// Returns average number of elements per bucket. + inline float load_factor() const { return static_cast(_num_filled) / (_mask + 1); } + + inline HashT& hash_function() const { return _hasher; } + inline EqT& key_eq() const { return _eq; } + + void max_load_factor(float mlf) + { + if (mlf < 0.991 && mlf > EMH_MIN_LOAD_FACTOR) { + _mlf = (uint32_t)((1 << 27) / mlf); + if (_num_buckets > 0) rehash(_num_buckets); + } + } + + inline constexpr float max_load_factor() const { return (1 << 27) / (float)_mlf; } + inline constexpr size_type max_size() const { return (1ull << (sizeof(size_type) * 8 - 1)); } + inline constexpr size_type max_bucket_count() const { return max_size(); } + +#if EMH_STATIS + //Returns the bucket number where the element with key k is located. + size_type bucket(const KeyT& key) const + { + const auto bucket = hash_bucket(key); + const auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return 0; + else if (bucket == next_bucket) + return bucket + 1; + + return hash_main(bucket) + 1; + } + + //Returns the number of elements in bucket n. + size_type bucket_size(const size_type bucket) const + { + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return 0; + + next_bucket = hash_main(bucket); + size_type ibucket_size = 1; + + //iterator each item in current main bucket + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) { + break; + } + ibucket_size ++; + next_bucket = nbucket; + } + return ibucket_size; + } + + size_type get_main_bucket(const size_type bucket) const + { + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return INACTIVE; + + return hash_main(bucket); + } + + size_type get_diss(size_type bucket, size_type next_bucket, const size_type slots) const + { + auto pbucket = reinterpret_cast(&_pairs[bucket]); + auto pnext = reinterpret_cast(&_pairs[next_bucket]); + if (pbucket / EMH_CACHE_LINE_SIZE == pnext / EMH_CACHE_LINE_SIZE) + return 0; + size_type diff = pbucket > pnext ? (pbucket - pnext) : (pnext - pbucket); + if (diff / EMH_CACHE_LINE_SIZE < slots - 1) + return diff / EMH_CACHE_LINE_SIZE + 1; + return slots - 1; + } + + int get_bucket_info(const size_type bucket, size_type steps[], const size_type slots) const + { + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return -1; + + const auto main_bucket = hash_main(bucket); + if (next_bucket == main_bucket) + return 1; + else if (main_bucket != bucket) + return 0; + + steps[get_diss(bucket, next_bucket, slots)] ++; + size_type ibucket_size = 2; + //find a empty and linked it to tail + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + break; + + steps[get_diss(nbucket, next_bucket, slots)] ++; + ibucket_size ++; + next_bucket = nbucket; + } + return (int)ibucket_size; + } + + void dump_statics() const + { + const size_type slots = 128; + size_type buckets[slots + 1] = {0}; + size_type steps[slots + 1] = {0}; + for (size_type bucket = 0; bucket < _num_buckets; ++bucket) { + auto bsize = get_bucket_info(bucket, steps, slots); + if (bsize > 0) + buckets[bsize] ++; + } + + size_type sumb = 0, collision = 0, sumc = 0, finds = 0, sumn = 0; + puts("============== buckets size ration ========="); + for (size_type i = 0; i < sizeof(buckets) / sizeof(buckets[0]); i++) { + const auto bucketsi = buckets[i]; + if (bucketsi == 0) + continue; + sumb += bucketsi; + sumn += bucketsi * i; + collision += bucketsi * (i - 1); + finds += bucketsi * i * (i + 1) / 2; + printf(" %2u %8u %2.2lf| %.2lf\n", i, bucketsi, bucketsi * 100.0 * i / _num_filled, sumn * 100.0 / _num_filled); + } + + puts("========== collision miss ration ==========="); + for (size_type i = 0; i < sizeof(steps) / sizeof(steps[0]); i++) { + sumc += steps[i]; + if (steps[i] <= 2) + continue; + printf(" %2u %8u %.2lf %.2lf\n", i, steps[i], steps[i] * 100.0 / collision, sumc * 100.0 / collision); + } + + if (sumb == 0) return; + printf(" _num_filled/bucket_size/packed collision/cache_miss/hit_find = %u/%.2lf/%zd/ %.2lf%%/%.2lf%%/%.2lf\n", + _num_filled, _num_filled * 1.0 / sumb, sizeof(value_type), (collision * 100.0 / _num_filled), (collision - steps[0]) * 100.0 / _num_filled, finds * 1.0 / _num_filled); + assert(sumn == _num_filled); + assert(sumc == collision); + puts("============== buckets size end ============="); + } +#endif + + // ------------------------------------------------------------ + template + inline iterator find(const K& key) noexcept + { + return {this, find_filled_slot(key)}; + } + + template + inline const_iterator find(const K& key) const noexcept + { + return {this, find_filled_slot(key)}; + } + + template + ValueT& at(const K& key) + { + const auto slot = find_filled_slot(key); + //throw + return EMH_VAL(_pairs, slot); + } + + template + const ValueT& at(const K& key) const + { + const auto slot = find_filled_slot(key); + //throw + return EMH_VAL(_pairs, slot); + } + + template + inline bool contains(const K& key) const noexcept + { + return find_filled_slot(key) != _num_filled; + } + + template + inline size_type count(const K& key) const noexcept + { + return find_filled_slot(key) == _num_filled ? 0 : 1; + //return find_sorted_bucket(key) == END ? 0 : 1; + //return find_hash_bucket(key) == END ? 0 : 1; + } + + template + std::pair equal_range(const K& key) + { + const auto found = find(key); + if (found.second == _num_filled) + return { found, found }; + else + return { found, std::next(found) }; + } + + void merge(HashMap& rhs) + { + if (empty()) { + *this = std::move(rhs); + return; + } + + for (auto rit = rhs.begin(); rit != rhs.end(); ) { + auto fit = find(rit->first); + if (fit == end()) { + insert_unique(rit->first, std::move(rit->second)); + rit = rhs.erase(rit); + } else { + ++rit; + } + } + } + + /// Returns the matching ValueT or nullptr if k isn't found. + bool try_get(const KeyT& key, ValueT& val) const noexcept + { + const auto slot = find_filled_slot(key); + const auto found = slot != _num_filled; + if (found) { + val = EMH_VAL(_pairs, slot); + } + return found; + } + + /// Returns the matching ValueT or nullptr if k isn't found. + ValueT* try_get(const KeyT& key) noexcept + { + const auto slot = find_filled_slot(key); + return slot != _num_filled ? &EMH_VAL(_pairs, slot) : nullptr; + } + + /// Const version of the above + ValueT* try_get(const KeyT& key) const noexcept + { + const auto slot = find_filled_slot(key); + return slot != _num_filled ? &EMH_VAL(_pairs, slot) : nullptr; + } + + /// set value if key exist + bool try_set(const KeyT& key, const ValueT& val) noexcept + { + const auto slot = find_filled_slot(key); + if (slot == _num_filled) + return false; + + EMH_VAL(_pairs, slot) = val; + return true; + } + + /// set value if key exist + bool try_set(const KeyT& key, ValueT&& val) noexcept + { + const auto slot = find_filled_slot(key); + if (slot == _num_filled) + return false; + + EMH_VAL(_pairs, slot) = std::move(val); + return true; + } + + /// Convenience function. + ValueT get_or_return_default(const KeyT& key) const noexcept + { + const auto slot = find_filled_slot(key); + return slot == _num_filled ? ValueT() : EMH_VAL(_pairs, slot); + } + + // ----------------------------------------------------- + std::pair do_insert(const value_type& value) noexcept + { + const auto key_hash = hash_key(value.first); + const auto bucket = find_or_allocate(value.first, key_hash); + const auto bempty = EMH_EMPTY(_index, bucket); + if (bempty) { + EMH_NEW(value.first, value.second, bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, bempty }; + } + + std::pair do_insert(value_type&& value) noexcept + { + const auto key_hash = hash_key(value.first); + const auto bucket = find_or_allocate(value.first, key_hash); + const auto bempty = EMH_EMPTY(_index, bucket); + if (bempty) { + EMH_NEW(std::move(value.first), std::move(value.second), bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, bempty }; + } + + template + std::pair do_insert(K&& key, V&& val) noexcept + { + const auto key_hash = hash_key(key); + const auto bucket = find_or_allocate(key, key_hash); + const auto bempty = EMH_EMPTY(_index, bucket); + if (bempty) { + EMH_NEW(std::forward(key), std::forward(val), bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, bempty }; + } + + template + std::pair do_assign(K&& key, V&& val) noexcept + { + check_expand_need(); + const auto key_hash = hash_key(key); + const auto bucket = find_or_allocate(key, key_hash); + const auto bempty = EMH_EMPTY(_index, bucket); + if (bempty) { + EMH_NEW(std::forward(key), std::forward(val), bucket, key_hash); + } else { + EMH_VAL(_pairs, EMH_SLOT(_index, bucket)) = std::move(val); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, bempty }; + } + + std::pair insert(const value_type& p) + { + check_expand_need(); + return do_insert(p); + } + + std::pair insert(value_type && p) + { + check_expand_need(); + return do_insert(std::move(p)); + } + + void insert(std::initializer_list ilist) + { + reserve(ilist.size() + _num_filled, false); + for (auto it = ilist.begin(); it != ilist.end(); ++it) + do_insert(*it); + } + + template + void insert(Iter first, Iter last) + { + reserve(std::distance(first, last) + _num_filled, false); + for (; first != last; ++first) + do_insert(first->first, first->second); + } + +#if 0 + template + void insert_unique(Iter begin, Iter end) + { + reserve(std::distance(begin, end) + _num_filled, false); + for (; begin != end; ++begin) { + insert_unique(*begin); + } + } +#endif + + template + size_type insert_unique(K&& key, V&& val) + { + check_expand_need(); + const auto key_hash = hash_key(key); + auto bucket = find_unique_bucket(key_hash); + EMH_NEW(std::forward(key), std::forward(val), bucket, key_hash); + return bucket; + } + + size_type insert_unique(value_type&& value) + { + return insert_unique(std::move(value.first), std::move(value.second)); + } + + inline size_type insert_unique(const value_type& value) + { + return insert_unique(value.first, value.second); + } + + template + inline std::pair emplace(Args&&... args) noexcept + { + check_expand_need(); + return do_insert(std::forward(args)...); + } + + //no any optimize for position + template + iterator emplace_hint(const_iterator hint, Args&&... args) + { + (void)hint; + check_expand_need(); + return do_insert(std::forward(args)...).first; + } + + template + std::pair try_emplace(const KeyT& k, Args&&... args) + { + check_expand_need(); + return do_insert(k, std::forward(args)...); + } + + template + std::pair try_emplace(KeyT&& k, Args&&... args) + { + check_expand_need(); + return do_insert(std::move(k), std::forward(args)...); + } + + template + inline size_type emplace_unique(Args&&... args) + { + return insert_unique(std::forward(args)...); + } + + std::pair insert_or_assign(const KeyT& key, ValueT&& val) { return do_assign(key, std::forward(val)); } + std::pair insert_or_assign(KeyT&& key, ValueT&& val) { return do_assign(std::move(key), std::forward(val)); } + + /// Return the old value or ValueT() if it didn't exist. + ValueT set_get(const KeyT& key, const ValueT& val) + { + check_expand_need(); + const auto key_hash = hash_key(key); + const auto bucket = find_or_allocate(key, key_hash); + if (EMH_EMPTY(_index, bucket)) { + EMH_NEW(key, val, bucket, key_hash); + return ValueT(); + } else { + const auto slot = EMH_SLOT(_index, bucket); + ValueT old_value(val); + std::swap(EMH_VAL(_pairs, slot), old_value); + return old_value; + } + } + + /// Like std::map::operator[]. + ValueT& operator[](const KeyT& key) noexcept + { + check_expand_need(); + const auto key_hash = hash_key(key); + const auto bucket = find_or_allocate(key, key_hash); + if (EMH_EMPTY(_index, bucket)) { + /* Check if inserting a value rather than overwriting an old entry */ + EMH_NEW(key, std::move(ValueT()), bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return EMH_VAL(_pairs, slot); + } + + ValueT& operator[](KeyT&& key) noexcept + { + check_expand_need(); + const auto key_hash = hash_key(key); + const auto bucket = find_or_allocate(key, key_hash); + if (EMH_EMPTY(_index, bucket)) { + EMH_NEW(std::move(key), std::move(ValueT()), bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return EMH_VAL(_pairs, slot); + } + + /// Erase an element from the hash table. + /// return 0 if element was not found + size_type erase(const KeyT& key) noexcept + { + const auto key_hash = hash_key(key); + const auto sbucket = find_filled_bucket(key, key_hash); + if (sbucket == INACTIVE) + return 0; + + const auto main_bucket = key_hash & _mask; + erase_slot(sbucket, (size_type)main_bucket); + return 1; + } + + //iterator erase(const_iterator begin_it, const_iterator end_it) + iterator erase(const const_iterator& cit) noexcept + { + const auto slot = (size_type)(cit.kv_ - _pairs); + size_type main_bucket; + const auto sbucket = find_slot_bucket(slot, main_bucket); //TODO + erase_slot(sbucket, main_bucket); + return {this, slot}; + } + + //only last >= first + iterator erase(const_iterator first, const_iterator last) noexcept + { + auto esize = long(last.kv_ - first.kv_); + auto tsize = long((_pairs + _num_filled) - last.kv_); //last to tail size + auto next = first; + while (tsize -- > 0) { + if (esize-- <= 0) + break; + next = ++erase(next); + } + + //fast erase from last + next = this->last(); + while (esize -- > 0) + next = --erase(next); + + return {this, size_type(next.kv_ - _pairs)}; + } + + template + size_type erase_if(Pred pred) + { + auto old_size = size(); + for (auto it = begin(); it != end();) { + if (pred(*it)) + it = erase(it); + else + ++it; + } + return old_size - size(); + } + + static constexpr bool is_triviall_destructable() + { +#if __cplusplus >= 201402L || _MSC_VER > 1600 + return !(std::is_trivially_destructible::value && std::is_trivially_destructible::value); +#else + return !(std::is_pod::value && std::is_pod::value); +#endif + } + + static constexpr bool is_copy_trivially() + { +#if __cplusplus >= 201103L || _MSC_VER > 1600 + return (std::is_trivially_copyable::value && std::is_trivially_copyable::value); +#else + return (std::is_pod::value && std::is_pod::value); +#endif + } + + void clearkv() + { + if (is_triviall_destructable()) { + while (_num_filled --) + _pairs[_num_filled].~value_type(); + } + } + + /// Remove all elements, keeping full capacity. + void clear() noexcept + { + clearkv(); + + if (_num_filled > 0) + memset((char*)_index, INACTIVE, sizeof(_index[0]) * _num_buckets); + + _last = _num_filled = 0; + _etail = INACTIVE; + +#if EMH_HIGH_LOAD + _ehead = 0; +#endif + } + + void shrink_to_fit(const float min_factor = EMH_DEFAULT_LOAD_FACTOR / 4) + { + if (load_factor() < min_factor && bucket_count() > 10) //safe guard + rehash(_num_filled + 1); + } + +#if EMH_HIGH_LOAD + void set_empty() + { + auto prev = 0; + for (int32_t bucket = 1; bucket < _num_buckets; ++bucket) { + if (EMH_EMPTY(_index, bucket)) { + if (prev != 0) { + EMH_PREVET(_index, bucket) = prev; + EMH_BUCKET(_index, prev) = -bucket; + } + else + _ehead = bucket; + prev = bucket; + } + } + + EMH_PREVET(_index, _ehead) = prev; + EMH_BUCKET(_index, prev) = 0-_ehead; + _ehead = 0-EMH_BUCKET(_index, _ehead); + } + + void clear_empty() + { + auto prev = EMH_PREVET(_index, _ehead); + while (prev != _ehead) { + EMH_BUCKET(_index, prev) = INACTIVE; + prev = EMH_PREVET(_index, prev); + } + EMH_BUCKET(_index, _ehead) = INACTIVE; + _ehead = 0; + } + + //prev-ehead->next + size_type pop_empty(const size_type bucket) + { + const auto prev_bucket = EMH_PREVET(_index, bucket); + const int next_bucket = 0-EMH_BUCKET(_index, bucket); + + EMH_PREVET(_index, next_bucket) = prev_bucket; + EMH_BUCKET(_index, prev_bucket) = -next_bucket; + + _ehead = next_bucket; + return bucket; + } + + //ehead->bucket->next + void push_empty(const int32_t bucket) + { + const int next_bucket = 0-EMH_BUCKET(_index, _ehead); + assert(next_bucket > 0); + + EMH_PREVET(_index, bucket) = _ehead; + EMH_BUCKET(_index, bucket) = -next_bucket; + + EMH_PREVET(_index, next_bucket) = bucket; + EMH_BUCKET(_index, _ehead) = -bucket; + // _ehead = bucket; + } +#endif + + /// Make room for this many elements + bool reserve(uint64_t num_elems, bool force) + { + (void)force; +#if EMH_HIGH_LOAD == 0 + const auto required_buckets = num_elems * _mlf >> 27; + if (EMH_LIKELY(required_buckets < _mask)) // && !force + return false; + +#elif EMH_HIGH_LOAD + const auto required_buckets = num_elems + num_elems * 1 / 9; + if (EMH_LIKELY(required_buckets < _mask)) + return false; + + else if (_num_buckets < 16 && _num_filled < _num_buckets) + return false; + + else if (_num_buckets > EMH_HIGH_LOAD) { + if (_ehead == 0) { + set_empty(); + return false; + } else if (/*_num_filled + 100 < _num_buckets && */EMH_BUCKET(_index, _ehead) != 0-_ehead) { + return false; + } + } +#endif +#if EMH_STATIS + if (_num_filled > EMH_STATIS) dump_statics(); +#endif + + //assert(required_buckets < max_size()); + rehash(required_buckets + 2); + return true; + } + + static value_type* alloc_bucket(size_type num_buckets) + { + auto new_pairs = (char*)malloc((uint64_t)num_buckets * sizeof(value_type)); + return (value_type *)(new_pairs); + } + + static Index* alloc_index(size_type num_buckets) + { + auto new_index = (char*)malloc((uint64_t)(EAD + num_buckets) * sizeof(Index)); + return (Index *)(new_index); + } + + bool reserve(size_type required_buckets) noexcept + { + if (_num_filled != required_buckets) + return reserve(required_buckets, true); + + _last = 0; +#if EMH_HIGH_LOAD + _ehead = 0; +#endif + +#if EMH_SORT + std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) { + const auto hashl = (size_type)hash_key(l.first) & _mask, hashr = (size_type)hash_key(r.first) & _mask; + return hashl < hashr; + //return l.first < r.first; + }); +#endif + + memset((char*)_index, INACTIVE, sizeof(_index[0]) * _num_buckets); + for (size_type slot = 0; slot < _num_filled; slot++) { + const auto& key = EMH_KEY(_pairs, slot); + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + auto& next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + EMH_INDEX(_index, bucket) = {1, slot | EMH_KEYMASK(key_hash, _mask)}; + else { + EMH_HSLOT(_index, bucket) |= EMH_KEYMASK(key_hash, _mask); + next_bucket ++; + } + } + return true; + } + + void rebuild(size_type num_buckets) noexcept + { + free(_index); + auto new_pairs = (value_type*)alloc_bucket((size_type)(num_buckets * max_load_factor()) + 4); + if (is_copy_trivially()) { + memcpy((char*)new_pairs, (char*)_pairs, _num_filled * sizeof(value_type)); + } else { + for (size_type slot = 0; slot < _num_filled; slot++) { + new(new_pairs + slot) value_type(std::move(_pairs[slot])); + if (is_triviall_destructable()) + _pairs[slot].~value_type(); + } + } + free(_pairs); + _pairs = new_pairs; + _index = (Index*)alloc_index (num_buckets); + + memset((char*)_index, INACTIVE, sizeof(_index[0]) * num_buckets); + memset((char*)(_index + num_buckets), 0, sizeof(_index[0]) * EAD); + } + + void rehash(uint64_t required_buckets) + { + if (required_buckets < _num_filled) + return; + + assert(required_buckets < max_size()); + auto num_buckets = _num_filled > (1u << 16) ? (1u << 16) : 4u; + while (num_buckets < required_buckets) { num_buckets *= 2; } + +#if EMH_REHASH_LOG + auto last = _last; + size_type collision = 0; +#endif + +#if EMH_HIGH_LOAD + _ehead = 0; +#endif + _last = _mask / 4; + + _mask = num_buckets - 1; +#if EMH_PACK_TAIL > 1 + _last = _mask; + num_buckets += num_buckets * EMH_PACK_TAIL / 100; //add more 5-10% +#endif + _num_buckets = num_buckets; + + rebuild(num_buckets); + +#ifdef EMH_SORT + std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) { + const auto hashl = hash_key(l.first), hashr = hash_key(r.first); + auto diff = int64_t((hashl & _mask) - (hashr & _mask)); + if (diff != 0) + return diff < 0; + return hashl < hashr; +// return l.first < r.first; + }); +#endif + + _etail = INACTIVE; + for (size_type slot = 0; slot < _num_filled; ++slot) { + const auto& key = EMH_KEY(_pairs, slot); + const auto key_hash = hash_key(key); + const auto bucket = find_unique_bucket(key_hash); + EMH_INDEX(_index, bucket) = {bucket, slot | EMH_KEYMASK(key_hash, _mask)}; + +#if EMH_REHASH_LOG + if (bucket != hash_main(bucket)) + collision ++; +#endif + } + +#if EMH_REHASH_LOG + if (_num_filled > EMH_REHASH_LOG) { + auto mbucket = _num_filled - collision; + char buff[255] = {0}; + sprintf(buff, " _num_filled/aver_size/K.V/pack/collision|last = %u/%.2lf/%s.%s/%zd|%.2lf%%,%.2lf%%", + _num_filled, double (_num_filled) / mbucket, typeid(KeyT).name(), typeid(ValueT).name(), sizeof(_pairs[0]), collision * 100.0 / _num_filled, last * 100.0 / _num_buckets); +#ifdef EMH_LOG + static uint32_t ihashs = 0; EMH_LOG() << "hash_nums = " << ihashs ++ << "|" <<__FUNCTION__ << "|" << buff << endl; +#else + puts(buff); +#endif + } +#endif + } + +private: + // Can we fit another element? + inline bool check_expand_need() + { + return reserve(_num_filled, false); + } + + size_type slot_to_bucket(const size_type slot) const noexcept + { + size_type main_bucket; + return find_slot_bucket(slot, main_bucket); //TODO + } + + //very slow + void erase_slot(const size_type sbucket, const size_type main_bucket) noexcept + { + const auto slot = EMH_SLOT(_index, sbucket); + const auto ebucket = erase_bucket(sbucket, main_bucket); + const auto last_slot = --_num_filled; + if (EMH_LIKELY(slot != last_slot)) { + const auto last_bucket = (_etail == INACTIVE || ebucket == _etail) + ? slot_to_bucket(last_slot) : _etail; + + EMH_KV(_pairs, slot) = std::move(EMH_KV(_pairs, last_slot)); + EMH_HSLOT(_index, last_bucket) = slot | (EMH_HSLOT(_index, last_bucket) & ~_mask); + } + + if (is_triviall_destructable()) + _pairs[last_slot].~value_type(); + + _etail = INACTIVE; + EMH_INDEX(_index, ebucket) = {INACTIVE, 0}; +#if EMH_HIGH_LOAD + if (_ehead) { + if (10 * _num_filled < 8 * _num_buckets) + clear_empty(); + else if (ebucket) + push_empty(ebucket); + } +#endif + } + + size_type erase_bucket(const size_type bucket, const size_type main_bucket) noexcept + { + const auto next_bucket = EMH_BUCKET(_index, bucket); + if (bucket == main_bucket) { + if (main_bucket != next_bucket) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + EMH_INDEX(_index, main_bucket) = { + (nbucket == next_bucket) ? main_bucket : nbucket, + EMH_HSLOT(_index, next_bucket) + }; + } + return next_bucket; + } + + const auto prev_bucket = find_prev_bucket(main_bucket, bucket); + EMH_BUCKET(_index, prev_bucket) = (bucket == next_bucket) ? prev_bucket : next_bucket; + return bucket; + } + + // Find the slot with this key, or return bucket size + size_type find_slot_bucket(const size_type slot, size_type& main_bucket) const + { + const auto key_hash = hash_key(EMH_KEY(_pairs, slot)); + const auto bucket = main_bucket = size_type(key_hash & _mask); + if (slot == EMH_SLOT(_index, bucket)) + return bucket; + + auto next_bucket = EMH_BUCKET(_index, bucket); + while (true) { + if (EMH_LIKELY(slot == EMH_SLOT(_index, next_bucket))) + return next_bucket; + next_bucket = EMH_BUCKET(_index, next_bucket); + } + + return INACTIVE; + } + + // Find the slot with this key, or return bucket size + size_type find_filled_bucket(const KeyT& key, uint64_t key_hash) const noexcept + { + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if (EMH_UNLIKELY((int)next_bucket < 0)) + return INACTIVE; + + if (EMH_EQHASH(bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return bucket; + } + if (next_bucket == bucket) + return INACTIVE; + + while (true) { + if (EMH_EQHASH(next_bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, next_bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return next_bucket; + } + + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + return INACTIVE; + next_bucket = nbucket; + } + + return INACTIVE; + } + + // Find the slot with this key, or return bucket size + template + size_type find_filled_slot(const K& key) const noexcept + { + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return _num_filled; + + if (EMH_EQHASH(bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return slot; + } + if (next_bucket == bucket) + return _num_filled; + + while (true) { + if (EMH_EQHASH(next_bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, next_bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return slot; + } + + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + return _num_filled; + next_bucket = nbucket; + } + + return _num_filled; + } + +#if EMH_SORT + size_type find_hash_bucket(const KeyT& key) const noexcept + { + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + const auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return END; + + auto slot = EMH_SLOT(_index, bucket); + if (_eq(key, EMH_KEY(_pairs, slot++))) + return slot; + else if (next_bucket == bucket) + return END; + + while (true) { + const auto& okey = EMH_KEY(_pairs, slot++); + if (_eq(key, okey)) + return slot; + + const auto hasho = hash_key(okey); + if ((hasho & _mask) != bucket) + break; + else if (hasho > key_hash) + break; + else if (EMH_UNLIKELY(slot >= _num_filled)) + break; + } + + return END; + } + + //only for find/can not insert + size_type find_sorted_bucket(const KeyT& key) const noexcept + { + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + const auto slots = (int)(EMH_BUCKET(_index, bucket)); //TODO + if (slots < 0 /**|| key < EMH_KEY(_pairs, slot)*/) + return END; + + const auto slot = EMH_SLOT(_index, bucket); + auto ormask = _index[bucket].slot & ~_mask; + auto hmask = EMH_KEYMASK(key_hash, _mask); + if ((hmask | ormask) != ormask) + return END; + + if (_eq(key, EMH_KEY(_pairs, slot))) + return slot; + else if (slots == 1 || key < EMH_KEY(_pairs, slot)) + return END; + +#if EMH_SORT + if (key < EMH_KEY(_pairs, slot) || key > EMH_KEY(_pairs, slots + slot - 1)) + return END; +#endif + + for (size_type i = 1; i < slots; ++i) { + const auto& okey = EMH_KEY(_pairs, slot + i); + if (_eq(key, okey)) + return slot + i; +// else if (okey > key) +// return END; + } + + return END; + } +#endif + + //kick out bucket and find empty to occpuy + //it will break the orgin link and relnik again. + //before: main_bucket-->prev_bucket --> bucket --> next_bucket + //atfer : main_bucket-->prev_bucket --> (removed)--> new_bucket--> next_bucket + size_type kickout_bucket(const size_type kmain, const size_type bucket) noexcept + { + const auto next_bucket = EMH_BUCKET(_index, bucket); + const auto new_bucket = find_empty_bucket(next_bucket, 2); + const auto prev_bucket = find_prev_bucket(kmain, bucket); + + const auto last = next_bucket == bucket ? new_bucket : next_bucket; + EMH_INDEX(_index, new_bucket) = {last, EMH_HSLOT(_index, bucket)}; + + EMH_BUCKET(_index, prev_bucket) = new_bucket; + EMH_BUCKET(_index, bucket) = INACTIVE; + + return bucket; + } + +/* +** inserts a new key into a hash table; first, check whether key's main +** bucket/position is free. If not, check whether colliding node/bucket is in its main +** position or not: if it is not, move colliding bucket to an empty place and +** put new key in its main position; otherwise (colliding bucket is in its main +** position), new key goes to an empty position. +*/ + template + size_type find_or_allocate(const K& key, uint64_t key_hash) noexcept + { + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) { +#if EMH_HIGH_LOAD + if (next_bucket != INACTIVE) + pop_empty(bucket); +#endif + return bucket; + } + + const auto slot = EMH_SLOT(_index, bucket); + if (EMH_EQHASH(bucket, key_hash)) + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return bucket; + + //check current bucket_key is in main bucket or not + const auto kmain = hash_bucket(EMH_KEY(_pairs, slot)); + if (kmain != bucket) + return kickout_bucket(kmain, bucket); + else if (next_bucket == bucket) + return EMH_BUCKET(_index, next_bucket) = find_empty_bucket(next_bucket, 1); + + uint32_t csize = 1; + //find next linked bucket and check key + while (true) { + const auto eslot = EMH_SLOT(_index, next_bucket); + if (EMH_EQHASH(next_bucket, key_hash)) { + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, eslot)))) + return next_bucket; + } + + csize += 1; + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + break; + next_bucket = nbucket; + } + + //find a empty and link it to tail + const auto new_bucket = find_empty_bucket(next_bucket, csize); + return EMH_BUCKET(_index, next_bucket) = new_bucket; + } + + size_type find_unique_bucket(uint64_t key_hash) noexcept + { + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) { +#if EMH_HIGH_LOAD + if (next_bucket != INACTIVE) + pop_empty(bucket); +#endif + return bucket; + } + + //check current bucket_key is in main bucket or not + const auto kmain = hash_main(bucket); + if (EMH_UNLIKELY(kmain != bucket)) + return kickout_bucket(kmain, bucket); + else if (EMH_UNLIKELY(next_bucket != bucket)) + next_bucket = find_last_bucket(next_bucket); + + return EMH_BUCKET(_index, next_bucket) = find_empty_bucket(next_bucket, 2); + } + +/*** + Different probing techniques usually provide a trade-off between memory locality and avoidance of clustering. +Since Robin Hood hashing is relatively resilient to clustering (both primary and secondary), linear probing is the most cache friendly alternativeis typically used. + + It's the core algorithm of this hash map with highly optimization/benchmark. +normaly linear probing is inefficient with high load factor, it use a new 3-way linear +probing strategy to search empty slot. from benchmark even the load factor > 0.9, it's more 2-3 timer fast than +one-way search strategy. + +1. linear or quadratic probing a few cache line for less cache miss from input slot "bucket_from". +2. the first search slot from member variant "_last", init with 0 +3. the second search slot from calculated pos "(_num_filled + _last) & _mask", it's like a rand value +*/ + // key is not in this mavalue. Find a place to put it. + size_type find_empty_bucket(const size_type bucket_from, uint32_t csize) noexcept + { +#if EMH_HIGH_LOAD + if (_ehead) + return pop_empty(_ehead); +#endif + + auto bucket = bucket_from; + if (EMH_EMPTY(_index, ++bucket) || EMH_EMPTY(_index, ++bucket)) + return bucket; + +#ifdef EMH_QUADRATIC + constexpr size_type linear_probe_length = 2 * EMH_CACHE_LINE_SIZE / sizeof(Index);//16 + for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length; ) { + bucket = (bucket_from + offset) & _mask; + if (EMH_EMPTY(_index, bucket) || EMH_EMPTY(_index, ++bucket)) + return bucket; + offset += step; //7/8. 12. 16 + } +#else + constexpr size_type quadratic_probe_length = 6u; + for (size_type offset = 4u, step = 3u; step < quadratic_probe_length; ) { + bucket = (bucket_from + offset) & _mask; + if (EMH_EMPTY(_index, bucket) || EMH_EMPTY(_index, ++bucket)) + return bucket; + offset += step++;//3.4.5 + } +#endif + +#if EMH_PREFETCH + __builtin_prefetch(static_cast(_index + _last + 1), 0, EMH_PREFETCH); +#endif + + for (;;) { +#if EMH_PACK_TAIL + //find empty bucket and skip next + if (EMH_EMPTY(_index, _last++))// || EMH_EMPTY(_index, _last++)) + return _last++ - 1; + + if (EMH_UNLIKELY(_last >= _num_buckets)) + _last = 0; + + auto medium = (_mask / 4 + _last++) & _mask; + if (EMH_EMPTY(_index, medium)) + return medium; +#else + if (EMH_EMPTY(_index, ++_last))// || EMH_EMPTY(_index, ++_last)) + return _last++; + + _last &= _mask; + auto medium = (_num_buckets / 2 + _last) & _mask; + if (EMH_EMPTY(_index, medium))// || EMH_EMPTY(_index, ++medium)) + return _last = medium; +#endif + } + + return 0; + } + + size_type find_last_bucket(size_type main_bucket) const + { + auto next_bucket = EMH_BUCKET(_index, main_bucket); + if (next_bucket == main_bucket) + return main_bucket; + + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + return next_bucket; + next_bucket = nbucket; + } + } + + size_type find_prev_bucket(const size_type main_bucket, const size_type bucket) const + { + auto next_bucket = EMH_BUCKET(_index, main_bucket); + if (next_bucket == bucket) + return main_bucket; + + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == bucket) + return next_bucket; + next_bucket = nbucket; + } + } + + inline size_type hash_bucket(const KeyT& key) const noexcept + { + return (size_type)hash_key(key) & _mask; + } + + inline size_type hash_main(const size_type bucket) const noexcept + { + const auto slot = EMH_SLOT(_index, bucket); + return (size_type)hash_key(EMH_KEY(_pairs, slot)) & _mask; + } + +#if EMH_INT_HASH + static constexpr uint64_t KC = UINT64_C(11400714819323198485); + static uint64_t hash64(uint64_t key) + { +#if __SIZEOF_INT128__ && EMH_INT_HASH == 1 + __uint128_t r = key; r *= KC; + return (uint64_t)(r >> 64) + (uint64_t)r; +#elif EMH_INT_HASH == 2 + //MurmurHash3Mixer + uint64_t h = key; + h ^= h >> 33; + h *= 0xff51afd7ed558ccd; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53; + h ^= h >> 33; + return h; +#elif _WIN64 && EMH_INT_HASH == 1 + uint64_t high; + return _umul128(key, KC, &high) + high; +#elif EMH_INT_HASH == 3 + auto ror = (key >> 32) | (key << 32); + auto low = key * 0xA24BAED4963EE407ull; + auto high = ror * 0x9FB21C651E98DF25ull; + auto mix = low + high; + return mix; +#elif EMH_INT_HASH == 1 + uint64_t r = key * UINT64_C(0xca4bcaa75ec3f625); + return (r >> 32) + r; +#elif EMH_WYHASH64 + return wyhash64(key, KC); +#else + uint64_t x = key; + x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); + x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); + x = x ^ (x >> 31); + return x; +#endif + } +#endif + +#if EMH_WYHASH_HASH + //#define WYHASH_CONDOM 1 + inline uint64_t wymix(uint64_t A, uint64_t B) + { +#if defined(__SIZEOF_INT128__) + __uint128_t r = A; r *= B; +#if WYHASH_CONDOM2 + A ^= (uint64_t)r; B ^= (uint64_t)(r >> 64); +#else + A = (uint64_t)r; B = (uint64_t)(r >> 64); +#endif + +#elif defined(_MSC_VER) && defined(_M_X64) +#if WYHASH_CONDOM2 + uint64_t a, b; + a = _umul128(A, B, &b); + A ^= a; B ^= b; +#else + A = _umul128(A, B, &B); +#endif +#else + uint64_t ha = A >> 32, hb = B >> 32, la = (uint32_t)A, lb = (uint32_t)B, hi, lo; + uint64_t rh = ha * hb, rm0 = ha * lb, rm1 = hb * la, rl = la * lb, t = rl + (rm0 << 32), c = t < rl; + lo = t + (rm1 << 32); c += lo < t; hi = rh + (rm0 >> 32) + (rm1 >> 32) + c; +#if WYHASH_CONDOM2 + A ^= lo; B ^= hi; +#else + A = lo; B = hi; +#endif +#endif + return A ^ B; + } + + //multiply and xor mix function, aka MUM + static inline uint64_t wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v; } + static inline uint64_t wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v; } + static inline uint64_t wyr3(const uint8_t *p, size_t k) { + return (((uint64_t)p[0]) << 16) | (((uint64_t)p[k >> 1]) << 8) | p[k - 1]; + } + + static constexpr uint64_t secret[4] = { + 0xa0761d6478bd642full, 0xe7037ed1a0b428dbull, + 0x8ebc6af09c88c6e3ull, 0x589965cc75374cc3ull}; + +public: + //wyhash main function https://github.com/wangyi-fudan/wyhash + static uint64_t wyhashstr(const char *key, const size_t len) + { + uint64_t a = 0, b = 0, seed = secret[0]; + const uint8_t *p = (const uint8_t*)key; + if (EMH_LIKELY(len <= 16)) { + if (EMH_LIKELY(len >= 4)) { + const auto half = (len >> 3) << 2; + a = (wyr4(p) << 32U) | wyr4(p + half); p += len - 4; + b = (wyr4(p) << 32U) | wyr4(p - half); + } else if (len) { + a = wyr3(p, len); + } + } else { + size_t i = len; + if (EMH_UNLIKELY(i > 48)) { + uint64_t see1 = seed, see2 = seed; + do { + seed = wymix(wyr8(p + 0) ^ secret[1], wyr8(p + 8) ^ seed); + see1 = wymix(wyr8(p + 16) ^ secret[2], wyr8(p + 24) ^ see1); + see2 = wymix(wyr8(p + 32) ^ secret[3], wyr8(p + 40) ^ see2); + p += 48; i -= 48; + } while (EMH_LIKELY(i > 48)); + seed ^= see1 ^ see2; + } + while (i > 16) { + seed = wymix(wyr8(p) ^ secret[1], wyr8(p + 8) ^ seed); + i -= 16; p += 16; + } + a = wyr8(p + i - 16); + b = wyr8(p + i - 8); + } + + return wymix(secret[1] ^ len, wymix(a ^ secret[1], b ^ seed)); + } +#endif + +private: + template::value, uint32_t>::type = 0> + inline uint64_t hash_key(const UType key) const + { +#if EMH_INT_HASH + return hash64(key); +#elif EMH_IDENTITY_HASH + return key + (key >> 24); +#else + return _hasher(key); +#endif + } + + template::value, uint32_t>::type = 0> + inline uint64_t hash_key(const UType& key) const + { +#if EMH_WYHASH_HASH + return wyhashstr(key.data(), key.size()); +#else + return _hasher(key); +#endif + } + + template::value && !std::is_same::value, uint32_t>::type = 0> + inline uint64_t hash_key(const UType& key) const + { + return _hasher(key); + } + +private: + Index* _index; + value_type*_pairs; + + HashT _hasher; + EqT _eq; + uint32_t _mlf; + size_type _mask; + size_type _num_buckets; + size_type _num_filled; + size_type _last; +#if EMH_HIGH_LOAD + size_type _ehead; +#endif + size_type _etail; +}; +} // namespace emhash + + + + +struct PyObject; +typedef pkpy::shared_ptr PyVar; +typedef PyVar PyVarOrNull; + +class PyVarList: public std::vector { + PyVar& at(size_t) = delete; + + inline void __checkIndex(size_t i) const { + if (i >= size()){ + auto msg = "std::vector index out of range, " + std::to_string(i) + " not in [0, " + std::to_string(size()) + ")"; + throw std::out_of_range(msg); + } + } +public: + PyVar& operator[](size_t i) { + __checkIndex(i); + return std::vector::operator[](i); + } + + const PyVar& operator[](size_t i) const { + __checkIndex(i); + return std::vector::operator[](i); + } + + // define constructors the same as std::vector + using std::vector::vector; +}; + + +class PyVarDict: public emhash8::HashMap<_Str, PyVar> { + PyVar& at(const _Str&) = delete; + +public: + PyVar& operator[](const _Str& key) { + return emhash8::HashMap<_Str, PyVar>::operator[](key); + } + + const PyVar& operator[](const _Str& key) const { + auto it = find(key); + if (it == end()){ + auto msg = "map key not found, '" + key.str() + "'"; + throw std::out_of_range(msg); + } + return it->second; + } + + using emhash8::HashMap<_Str, PyVar>::HashMap; +}; + + +namespace pkpy { + const uint8_t MAX_POOLING_N = 10; + static std::vector* _poolArgList = new std::vector[MAX_POOLING_N]; + + class ArgList { + PyVar* _args = nullptr; + uint8_t _size = 0; + + inline void __checkIndex(uint8_t i) const { + if (i >= _size){ + auto msg = "pkpy:ArgList index out of range, " + std::to_string(i) + " not in [0, " + std::to_string(size()) + ")"; + throw std::out_of_range(msg); + } + } + + void __tryAlloc(uint8_t n){ + if(n > 255) UNREACHABLE(); + if(n >= MAX_POOLING_N || _poolArgList[n].empty()){ + this->_size = n; + this->_args = new PyVar[n]; + }else{ + this->_args = _poolArgList[n].back(); + this->_size = n; + _poolArgList[n].pop_back(); + } + } + + void __tryRelease(){ + if(_size == 0 || _args == nullptr) return; + if(_size >= MAX_POOLING_N || _poolArgList[_size].size() > 32){ + delete[] _args; + }else{ + for(uint8_t i = 0; i < _size; i++) _args[i].reset(); + _poolArgList[_size].push_back(_args); + } + } + + public: + ArgList(uint8_t n){ + if(n != 0) __tryAlloc(n); + } + + ArgList(const ArgList& other){ + __tryAlloc(other._size); + for(uint8_t i=0; i<_size; i++){ + _args[i] = other._args[i]; + } + } + + ArgList(ArgList&& other){ + this->_args = other._args; + this->_size = other._size; + other._args = nullptr; + other._size = 0; + } + + ArgList(PyVarList&& other){ + __tryAlloc(other.size()); + for(uint8_t i=0; i<_size; i++){ + _args[i] = std::move(other[i]); + } + other.clear(); + } + + // deprecated, this is very slow, do not use it!!! + ArgList(std::initializer_list args){ + __tryAlloc(args.size()); + uint8_t i = 0; + for(auto& arg: args) this->_args[i++] = arg; + } + + PyVar& operator[](uint8_t i){ + __checkIndex(i); + return _args[i]; + } + + const PyVar& operator[](uint8_t i) const { + __checkIndex(i); + return _args[i]; + } + + inline PyVar& _index(uint8_t i){ + return _args[i]; + } + + // overload = for && + ArgList& operator=(ArgList&& other){ + if(this != &other){ + __tryRelease(); + this->_args = other._args; + this->_size = other._size; + other._args = nullptr; + other._size = 0; + } + return *this; + } + + uint8_t size() const { + return _size; + } + + ArgList subList(uint8_t start) const { + if(start >= _size) return ArgList(0); + ArgList ret(_size - start); + for(uint8_t i=start; i<_size; i++){ + ret[i-start] = _args[i]; + } + return ret; + } + + PyVarList toList() const { + PyVarList ret(_size); + for(uint8_t i=0; i<_size; i++){ + ret[i] = _args[i]; + } + return ret; + } + + ~ArgList(){ + __tryRelease(); + } + }; +} + + +const char* __BUILTINS_CODE = R"( +def len(x): + return x.__len__() + +str.__mul__ = lambda self, n: ''.join([self for _ in range(n)]) + +def __str4split(self, sep): + if sep == "": + return list(self) + res = [] + i = 0 + while i < len(self): + if self[i:i+len(sep)] == sep: + res.append(self[:i]) + self = self[i+len(sep):] + i = 0 + else: + i += 1 + res.append(self) + return res +str.split = __str4split +del __str4split + +def __str4index(self, sub): + for i in range(len(self) - len(sub) + 1): + if self[i:i+len(sub)] == sub: + return i + return -1 +str.index = __str4index +del __str4index + +list.__repr__ = lambda self: '[' + ', '.join([repr(i) for i in self]) + ']' +tuple.__repr__ = lambda self: '(' + ', '.join([repr(i) for i in self]) + ')' +list.__json__ = lambda self: '[' + ', '.join([i.__json__() for i in self]) + ']' +tuple.__json__ = lambda self: '[' + ', '.join([i.__json__() for i in self]) + ']' + +def __list4extend(self, other): + for i in other: + self.append(i) +list.extend = __list4extend +del __list4extend + +def __list4remove(self, value): + for i in range(len(self)): + if self[i] == value: + del self[i] + return True + return False +list.remove = __list4remove +del __list4remove + +def __list4index(self, value): + for i in range(len(self)): + if self[i] == value: + return i + return -1 +list.index = __list4index +del __list4index + +def __list4__mul__(self, n): + a = [] + for i in range(n): + a.extend(self) + return a +list.__mul__ = __list4__mul__ +del __list4__mul__ + +def __iterable4__eq__(self, other): + if len(self) != len(other): + return False + for i in range(len(self)): + if self[i] != other[i]: + return False + return True +list.__eq__ = __iterable4__eq__ +tuple.__eq__ = __iterable4__eq__ +del __iterable4__eq__ + +def __iterable4count(self, x): + res = 0 + for i in self: + if i == x: + res += 1 + return res +list.count = __iterable4count +tuple.count = __iterable4count +del __iterable4count + +def __iterable4__contains__(self, item): + for i in self: + if i == item: + return True + return False +list.__contains__ = __iterable4__contains__ +tuple.__contains__ = __iterable4__contains__ +del __iterable4__contains__ + +list.__new__ = lambda obj: [i for i in obj] + +# https://github.com/python/cpython/blob/main/Objects/dictobject.c +class dict: + def __init__(self): + self._capacity = 8 + self._a = [None] * self._capacity + self._len = 0 + + def __len__(self): + return self._len + + def __probe(self, key): + i = hash(key) % self._capacity + while self._a[i] is not None: + if self._a[i][0] == key: + return True, i + i = ((5*i) + 1) % self._capacity + return False, i + + def __getitem__(self, key): + ok, i = self.__probe(key) + if not ok: + raise KeyError(key) + return self._a[i][1] + + def __contains__(self, key): + ok, i = self.__probe(key) + return ok + + def __setitem__(self, key, value): + ok, i = self.__probe(key) + if ok: + self._a[i][1] = value + else: + self._a[i] = [key, value] + self._len += 1 + if self._len > self._capacity * 0.6: + self.__resize_2x() + + def __delitem__(self, key): + ok, i = self.__probe(key) + if not ok: + raise KeyError(key) + self._a[i] = None + self._len -= 1 + + def __resize_2x(self): + old_a = self._a + self._capacity *= 2 + self._a = [None] * self._capacity + self._len = 0 + for kv in old_a: + if kv is not None: + self[kv[0]] = kv[1] + + def keys(self): + return [kv[0] for kv in self._a if kv is not None] + + def values(self): + return [kv[1] for kv in self._a if kv is not None] + + def items(self): + return [kv for kv in self._a if kv is not None] + + def clear(self): + self._a = [None] * self._capacity + self._len = 0 + + def update(self, other): + for k, v in other.items(): + self[k] = v + + def copy(self): + d = dict() + for kv in self._a: + if kv is not None: + d[kv[0]] = kv[1] + return d + + def __repr__(self): + a = [repr(k)+': '+repr(v) for k,v in self.items()] + return '{'+ ', '.join(a) + '}' + + def __json__(self): + a = [] + for k,v in self.items(): + if type(k) is not str: + raise TypeError('json keys must be strings, got ' + repr(k) ) + a.append(k.__json__()+': '+v.__json__()) + return '{'+ ', '.join(a) + '}' + +def round(x): + if x >= 0: + return int(x + 0.5) + else: + return int(x - 0.5) + +def max(a, b): + if a > b: + return a + return b + +def min(a, b): + if a < b: + return a + return b + +def sum(iterable): + res = 0 + for i in iterable: + res += i + return res + +def map(f, iterable): + return [f(i) for i in iterable] + +def zip(a, b): + return [(a[i], b[i]) for i in range(min(len(a), len(b)))] + +def sorted(iterable, key=None, reverse=False): + if key is None: + key = lambda x: x + a = [key(i) for i in iterable] + b = list(iterable) + for i in range(len(a)): + for j in range(i+1, len(a)): + if (a[i] > a[j]) ^ reverse: + a[i], a[j] = a[j], a[i] + b[i], b[j] = b[j], b[i] + return b + +import json as _json + +def jsonrpc(method, params, raw=False): + assert type(method) is str + assert type(params) is list or type(params) is tuple + data = { + 'jsonrpc': '2.0', + 'method': method, + 'params': params, + } + ret = __string_channel_call(_json.dumps(data)) + ret = _json.loads(ret) + if raw: + return ret + assert type(ret) is dict + if 'result' in ret: + return ret['result'] + raise JsonRpcError(ret['error']['message']) + +def input(prompt=None): + return jsonrpc('input', [prompt]) + +class FileIO: + def __init__(self, path, mode): + assert type(path) is str + assert type(mode) is str + assert mode in ['r', 'w'] + self.path = path + self.mode = mode + self.fp = jsonrpc('fopen', [path, mode]) + + def read(self): + assert self.mode == 'r' + return jsonrpc('fread', [self.fp]) + + def write(self, s): + assert self.mode == 'w' + assert type(s) is str + jsonrpc('fwrite', [self.fp, s]) + + def close(self): + jsonrpc('fclose', [self.fp]) + + def __enter__(self): + pass + + def __exit__(self): + self.close() + +def open(path, mode='r'): + return FileIO(path, mode) +)"; + +const char* __OS_CODE = R"( +def listdir(path): + assert type(path) is str + return jsonrpc("os.listdir", [path]) + +def mkdir(path): + assert type(path) is str + return jsonrpc("os.mkdir", [path]) + +def rmdir(path): + assert type(path) is str + return jsonrpc("os.rmdir", [path]) + +def remove(path): + assert type(path) is str + return jsonrpc("os.remove", [path]) + +path = object() + +def __path4exists(path): + assert type(path) is str + return jsonrpc("os.path.exists", [path]) +path.exists = __path4exists +del __path4exists +)"; + +const char* __RANDOM_CODE = R"( +import time as _time + +__all__ = ['Random', 'seed', 'random', 'randint', 'uniform'] + +def _int32(x): + return int(0xffffffff & x) + +class Random: + def __init__(self, seed=None): + if seed is None: + seed = int(_time.time() * 1000000) + seed = _int32(seed) + self.mt = [0] * 624 + self.mt[0] = seed + self.mti = 0 + for i in range(1, 624): + self.mt[i] = _int32(1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i) + + def extract_number(self): + if self.mti == 0: + self.twist() + y = self.mt[self.mti] + y = y ^ y >> 11 + y = y ^ y << 7 & 2636928640 + y = y ^ y << 15 & 4022730752 + y = y ^ y >> 18 + self.mti = (self.mti + 1) % 624 + return _int32(y) + + def twist(self): + for i in range(0, 624): + y = _int32((self.mt[i] & 0x80000000) + (self.mt[(i + 1) % 624] & 0x7fffffff)) + self.mt[i] = (y >> 1) ^ self.mt[(i + 397) % 624] + + if y % 2 != 0: + self.mt[i] = self.mt[i] ^ 0x9908b0df + + def seed(self, x): + assert type(x) is int + self.mt = [0] * 624 + self.mt[0] = _int32(x) + self.mti = 0 + for i in range(1, 624): + self.mt[i] = _int32(1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i) + + def random(self): + return self.extract_number() / 2 ** 32 + + def randint(self, a, b): + assert type(a) is int and type(b) is int + assert a <= b + return int(self.random() * (b - a + 1)) + a + + def uniform(self, a, b): + assert type(a) is int or type(a) is float + assert type(b) is int or type(b) is float + if a > b: + a, b = b, a + return self.random() * (b - a) + a + + def shuffle(self, L): + for i in range(len(L)): + j = self.randint(i, len(L) - 1) + L[i], L[j] = L[j], L[i] + + def choice(self, L): + return L[self.randint(0, len(L) - 1)] + +_inst = Random() +seed = _inst.seed +random = _inst.random +randint = _inst.randint +uniform = _inst.uniform +shuffle = _inst.shuffle +choice = _inst.choice + +)"; + + +class NeedMoreLines { +public: + NeedMoreLines(bool isClassDef) : isClassDef(isClassDef) {} + bool isClassDef; +}; + +enum CompileMode { + EXEC_MODE, + EVAL_MODE, + SINGLE_MODE, // for REPL + JSON_MODE, +}; + +struct SourceMetadata { + const char* source; + _Str filename; + std::vector lineStarts; + CompileMode mode; + + _Str getLine(int lineno) const { + if(lineno == -1) return ""; + const char* _start = lineStarts.at(lineno-1); + const char* i = _start; + while(*i != '\n' && *i != '\0') i++; + return _Str(_start, i-_start); + } + + SourceMetadata(const char* source, _Str filename, CompileMode mode) { + source = strdup(source); + // Skip utf8 BOM if there is any. + if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3; + this->filename = filename; + this->source = source; + lineStarts.push_back(source); + this->mode = mode; + } + + _Str snapshot(int lineno){ + _StrStream ss; + ss << " " << "File \"" << filename << "\", line " << lineno << '\n'; + _Str line = getLine(lineno).__lstrip(); + if(line.empty()) line = ""; + ss << " " << line << '\n'; + return ss.str(); + } + + ~SourceMetadata(){ + free((void*)source); + } +}; + +typedef pkpy::shared_ptr _Source; + +class _Error : public std::exception { +private: + _Str _what; +public: + _Error(_Str type, _Str msg, _Str desc){ + _what = desc + type + ": " + msg; + } + + const char* what() const noexcept override { + return _what.c_str(); + } +}; + +class CompileError : public _Error { +public: + CompileError(_Str type, _Str msg, _Str snapshot) + : _Error(type, msg, snapshot) {} +}; + +class RuntimeError : public _Error { +private: + static _Str __concat(std::stack<_Str> snapshots){ + _StrStream ss; + ss << "Traceback (most recent call last):" << '\n'; + while(!snapshots.empty()){ + ss << snapshots.top(); + snapshots.pop(); + } + return ss.str(); + } +public: + RuntimeError(_Str type, _Str msg, const std::stack<_Str>& snapshots) + : _Error(type, msg, __concat(snapshots)) {} +}; + + +typedef int64_t _Int; +typedef double _Float; + +struct CodeObject; +struct BasePointer; +class VM; +class Frame; + +typedef pkpy::shared_ptr _Pointer; +typedef PyVar (*_CppFunc)(VM*, const pkpy::ArgList&); +typedef pkpy::shared_ptr _Code; + +struct Function { + _Str name; + _Code code; + std::vector<_Str> args; + _Str starredArg; // empty if no *arg + PyVarDict kwArgs; // empty if no k=v + std::vector<_Str> kwArgsOrder; + + bool hasName(const _Str& val) const { + bool _0 = std::find(args.begin(), args.end(), val) != args.end(); + bool _1 = starredArg == val; + bool _2 = kwArgs.find(val) != kwArgs.end(); + return _0 || _1 || _2; + } +}; + +struct _BoundedMethod { + PyVar obj; + PyVar method; +}; + +struct _Range { + _Int start = 0; + _Int stop = -1; + _Int step = 1; +}; + +struct _Slice { + int start = 0; + int stop = 0x7fffffff; + + void normalize(int len){ + if(start < 0) start += len; + if(stop < 0) stop += len; + if(start < 0) start = 0; + if(stop > len) stop = len; + } +}; + +class _Iterator { +protected: + PyVar _ref; // keep a reference to the object so it will not be deleted while iterating + VM* vm; +public: + virtual PyVar next() = 0; + virtual bool hasNext() = 0; + _Pointer var; + _Iterator(VM* vm, PyVar _ref) : vm(vm), _ref(_ref) {} +}; + +typedef pkpy::shared_ptr _Func; +typedef std::variant,_BoundedMethod,_Range,_Slice,_Pointer> _Value; + +const int VALUE_SIZE = sizeof(_Value); + + +struct PyObject { + PyVarDict attribs; + _Value _native; + PyVar _type; + + inline bool isType(const PyVar& type){ + return this->_type == type; + } + + inline void setType(const PyVar& type){ + this->_type = type; + // this->attribs[__class__] = type; + } + + // currently __name__ is only used for 'type' + _Str getName(){ + _Value val = attribs[__name__]->_native; + return std::get<_Str>(val); + } + + _Str getTypeName(){ + return _type->getName(); + } + + PyObject(const _Value& val): _native(val) {} + PyObject(_Value&& val): _native(std::move(val)) {} +}; + + +class RangeIterator : public _Iterator { +private: + _Int current; + _Range r; +public: + RangeIterator(VM* vm, PyVar _ref) : _Iterator(vm, _ref) { + this->r = std::get<_Range>(_ref->_native); + this->current = r.start; + } + + bool hasNext() override { + if(r.step > 0){ + return current < r.stop; + }else{ + return current > r.stop; + } + } + + PyVar next() override; +}; + +class VectorIterator : public _Iterator { +private: + size_t index = 0; + const PyVarList* vec; +public: + VectorIterator(VM* vm, PyVar _ref) : _Iterator(vm, _ref) { + vec = &std::get(_ref->_native); + } + + bool hasNext(){ + return index < vec->size(); + } + + PyVar next(){ + return vec->operator[](index++); + } +}; + +class StringIterator : public _Iterator { +private: + size_t index = 0; + _Str str; +public: + StringIterator(VM* vm, PyVar _ref) : _Iterator(vm, _ref) { + str = std::get<_Str>(_ref->_native); + } + + bool hasNext(){ + return index < str.u8_length(); + } + + PyVar next(); +}; + + + +typedef uint8_t _TokenType; + +constexpr const char* __TOKENS[] = { + "@error", "@eof", "@eol", "@sof", + ".", ",", ":", ";", "#", "(", ")", "[", "]", "{", "}", "%", + "+", "-", "*", "/", "//", "**", "=", ">", "<", "...", "->", + "<<", ">>", "&", "|", "^", "?", + "==", "!=", ">=", "<=", + "+=", "-=", "*=", "/=", "//=", + /** KW_BEGIN **/ + "class", "import", "as", "def", "lambda", "pass", "del", "from", "with", + "None", "in", "is", "and", "or", "not", "True", "False", "global", + "goto", "label", // extended keywords, not available in cpython + "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise", + /** KW_END **/ + "is not", "not in", + "@id", "@num", "@str", "@fstr", + "@indent", "@dedent" +}; + +const _TokenType __TOKENS_LEN = sizeof(__TOKENS) / sizeof(__TOKENS[0]); + +constexpr _TokenType TK(const char* const token) { + for(int k=0; k<__TOKENS_LEN; k++){ + const char* i = __TOKENS[k]; + const char* j = token; + while(*i && *j && *i == *j){ + i++; j++; + } + if(*i == *j) return k; + } + return 0; +} + +#define TK_STR(t) __TOKENS[t] + +const _TokenType __KW_BEGIN = TK("class"); +const _TokenType __KW_END = TK("raise"); + +const emhash8::HashMap __KW_MAP = [](){ + emhash8::HashMap map; + for(int k=__KW_BEGIN; k<=__KW_END; k++) map[__TOKENS[k]] = k; + return map; +}(); + + +struct Token{ + _TokenType type; + + const char* start; //< Begining of the token in the source. + int length; //< Number of chars of the token. + int line; //< Line number of the token (1 based). + PyVar value; //< Literal value of the token. + + const _Str str() const { + return _Str(start, length); + } + + const _Str info() const { + _StrStream ss; + _Str raw = str(); + if (raw == _Str("\n")) raw = "\\n"; + ss << line << ": " << TK_STR(type) << " '" << raw << "'"; + return ss.str(); + } +}; + +enum Precedence { + PREC_NONE, + PREC_ASSIGNMENT, // = + PREC_COMMA, // , + PREC_TERNARY, // ?: + PREC_LOGICAL_OR, // or + PREC_LOGICAL_AND, // and + PREC_EQUALITY, // == != + PREC_TEST, // in is + PREC_COMPARISION, // < > <= >= + PREC_BITWISE_OR, // | + PREC_BITWISE_XOR, // ^ + PREC_BITWISE_AND, // & + PREC_BITWISE_SHIFT, // << >> + PREC_TERM, // + - + PREC_FACTOR, // * / % // + PREC_UNARY, // - not + PREC_EXPONENT, // ** + PREC_CALL, // () + PREC_SUBSCRIPT, // [] + PREC_ATTRIB, // .index + PREC_PRIMARY, +}; + +// The context of the parsing phase for the compiler. +struct Parser { + _Source src; + + const char* token_start; + const char* current_char; + int current_line = 1; + Token previous, current; + std::queue nexts; + std::stack indents; + + int brackets_level_0 = 0; + int brackets_level_1 = 0; + int brackets_level_2 = 0; + + Token nextToken(){ + if(nexts.empty()) return makeErrToken(); + Token t = nexts.front(); + if(t.type == TK("@eof") && indents.size()>1){ + nexts.pop(); + indents.pop(); + return Token{TK("@dedent"), token_start, 0, current_line}; + } + nexts.pop(); + return t; + } + + char peekChar() { + return *current_char; + } + + char peekNextChar() { + if (peekChar() == '\0') return '\0'; + return *(current_char + 1); + } + + int eatSpaces(){ + int count = 0; + while (true) { + switch (peekChar()) { + case ' ': count++; break; + case '\t': count+=4; break; + default: return count; + } + eatChar(); + } + } + + bool eatIndentation(){ + if(brackets_level_0 > 0 || brackets_level_1 > 0 || brackets_level_2 > 0) return true; + int spaces = eatSpaces(); + // https://docs.python.org/3/reference/lexical_analysis.html#indentation + if(spaces > indents.top()){ + indents.push(spaces); + nexts.push(Token{TK("@indent"), token_start, 0, current_line}); + } else if(spaces < indents.top()){ + while(spaces < indents.top()){ + indents.pop(); + nexts.push(Token{TK("@dedent"), token_start, 0, current_line}); + } + if(spaces != indents.top()){ + return false; + } + } + return true; + } + + char eatChar() { + char c = peekChar(); + if(c == '\n') throw std::runtime_error("eatChar() cannot consume a newline"); + current_char++; + return c; + } + + char eatCharIncludeNewLine() { + char c = peekChar(); + current_char++; + if (c == '\n'){ + current_line++; + src->lineStarts.push_back(current_char); + } + return c; + } + + inline bool isNameStart(char c){ + if(isalpha(c) || c=='_') return true; + if(!isascii(c)) return true; + return false; + } + + int eatName() { + current_char--; + while(true){ + uint8_t c = peekChar(); + int u8bytes = 0; + if((c & 0b10000000) == 0b00000000) u8bytes = 1; + else if((c & 0b11100000) == 0b11000000) u8bytes = 2; + else if((c & 0b11110000) == 0b11100000) u8bytes = 3; + else if((c & 0b11111000) == 0b11110000) u8bytes = 4; + else return 1; + if(u8bytes == 1){ + if(isalpha(c) || c=='_' || isdigit(c)) { + current_char++; + continue; + }else{ + break; + } + } + // handle multibyte char + std::string u8str(current_char, u8bytes); + if(u8str.size() != u8bytes) return 2; + uint32_t value = 0; + for(int k=0; k < u8bytes; k++){ + uint8_t b = u8str[k]; + if(k==0){ + if(u8bytes == 2) value = (b & 0b00011111) << 6; + else if(u8bytes == 3) value = (b & 0b00001111) << 12; + else if(u8bytes == 4) value = (b & 0b00000111) << 18; + }else{ + value |= (b & 0b00111111) << (6*(u8bytes-k-1)); + } + } + if(__isLoChar(value)) current_char += u8bytes; + else break; + } + + int length = (int)(current_char - token_start); + if(length == 0) return 3; + std::string_view name(token_start, length); + + if(src->mode == JSON_MODE){ + if(name == "true"){ + setNextToken(TK("True")); + } else if(name == "false"){ + setNextToken(TK("False")); + } else if(name == "null"){ + setNextToken(TK("None")); + } else { + return 4; + } + return 0; + } + + if(__KW_MAP.count(name)){ + if(name == "not"){ + if(strncmp(current_char, " in", 3) == 0){ + current_char += 3; + setNextToken(TK("not in")); + return 0; + } + }else if(name == "is"){ + if(strncmp(current_char, " not", 4) == 0){ + current_char += 4; + setNextToken(TK("is not")); + return 0; + } + } + setNextToken(__KW_MAP.at(name)); + } else { + setNextToken(TK("@id")); + } + return 0; + } + + void skipLineComment() { + char c; + while ((c = peekChar()) != '\0') { + if (c == '\n') return; + eatChar(); + } + } + + // If the current char is [c] consume it and advance char by 1 and returns + // true otherwise returns false. + bool matchChar(char c) { + if (peekChar() != c) return false; + eatCharIncludeNewLine(); + return true; + } + + // Returns an error token from the current position for reporting error. + Token makeErrToken() { + return Token{TK("@error"), token_start, (int)(current_char - token_start), current_line}; + } + + // Initialize the next token as the type. + void setNextToken(_TokenType type, PyVar value=nullptr) { + + switch(type){ + case TK("("): brackets_level_0++; break; + case TK(")"): brackets_level_0--; break; + case TK("["): brackets_level_1++; break; + case TK("]"): brackets_level_1--; break; + case TK("{"): brackets_level_2++; break; + case TK("}"): brackets_level_2--; break; + } + + nexts.push( Token{ + type, + token_start, + (int)(current_char - token_start), + current_line - ((type == TK("@eol")) ? 1 : 0), + value + }); + } + + void setNextTwoCharToken(char c, _TokenType one, _TokenType two) { + if (matchChar(c)) setNextToken(two); + else setNextToken(one); + } + + Parser(_Source src) { + this->src = src; + this->token_start = src->source; + this->current_char = src->source; + this->nexts.push(Token{TK("@sof"), token_start, 0, current_line}); + this->indents.push(0); + } +}; + + +class Frame; + +struct BasePointer { + virtual PyVar get(VM*, Frame*) const = 0; + virtual void set(VM*, Frame*, PyVar) const = 0; + virtual void del(VM*, Frame*) const = 0; +}; + +enum NameScope { + NAME_LOCAL = 0, + NAME_GLOBAL = 1, + NAME_ATTR = 2, +}; + +struct NamePointer : BasePointer { + const _Str name; + const NameScope scope; + NamePointer(const _Str& name, NameScope scope) : name(name), scope(scope) {} + + bool operator==(const NamePointer& other) const { + return name == other.name && scope == other.scope; + } + + PyVar get(VM* vm, Frame* frame) const; + void set(VM* vm, Frame* frame, PyVar val) const; + void del(VM* vm, Frame* frame) const; +}; + +struct AttrPointer : BasePointer { + mutable PyVar obj; + const NamePointer* attr; + AttrPointer(PyVar obj, const NamePointer* attr) : obj(obj), attr(attr) {} + + PyVar get(VM* vm, Frame* frame) const; + void set(VM* vm, Frame* frame, PyVar val) const; + void del(VM* vm, Frame* frame) const; +}; + +struct IndexPointer : BasePointer { + mutable PyVar obj; + const PyVar index; + IndexPointer(PyVar obj, PyVar index) : obj(obj), index(index) {} + + PyVar get(VM* vm, Frame* frame) const; + void set(VM* vm, Frame* frame, PyVar val) const; + void del(VM* vm, Frame* frame) const; +}; + +struct CompoundPointer : BasePointer { + const std::vector<_Pointer> pointers; + CompoundPointer(const std::vector<_Pointer>& pointers) : pointers(pointers) {} + CompoundPointer(std::vector<_Pointer>&& pointers) : pointers(pointers) {} + + PyVar get(VM* vm, Frame* frame) const; + void set(VM* vm, Frame* frame, PyVar val) const; + void del(VM* vm, Frame* frame) const; +}; + +struct UserPointer : BasePointer { + const _Pointer p; + uint64_t f_id; + UserPointer(_Pointer p, uint64_t f_id) : p(p), f_id(f_id) {} + + PyVar get(VM* vm, Frame* frame) const; + void set(VM* vm, Frame* frame, PyVar val) const; + void del(VM* vm, Frame* frame) const; +}; + + +enum Opcode { + #define OPCODE(name) OP_##name, + #ifdef OPCODE + +OPCODE(NO_OP) +OPCODE(DELETED_OP) +OPCODE(LOAD_CONST) +OPCODE(IMPORT_NAME) +OPCODE(PRINT_EXPR) +OPCODE(POP_TOP) +OPCODE(CALL) +OPCODE(RETURN_VALUE) + +OPCODE(BINARY_OP) +OPCODE(COMPARE_OP) +OPCODE(BITWISE_OP) +OPCODE(IS_OP) +OPCODE(CONTAINS_OP) + +OPCODE(UNARY_NEGATIVE) +OPCODE(UNARY_NOT) + +OPCODE(DUP_TOP) + +OPCODE(BUILD_LIST) +OPCODE(BUILD_MAP) +OPCODE(BUILD_SLICE) + +OPCODE(LIST_APPEND) + +OPCODE(GET_ITER) +OPCODE(FOR_ITER) + +OPCODE(POP_JUMP_IF_FALSE) +OPCODE(JUMP_ABSOLUTE) +OPCODE(SAFE_JUMP_ABSOLUTE) +OPCODE(JUMP_IF_TRUE_OR_POP) +OPCODE(JUMP_IF_FALSE_OR_POP) + +// non-standard python opcodes +OPCODE(LOAD_NONE) +OPCODE(LOAD_TRUE) +OPCODE(LOAD_FALSE) +OPCODE(LOAD_EVAL_FN) // load eval() callable into stack +OPCODE(LOAD_LAMBDA) // LOAD_CONST + set __module__ attr +OPCODE(LOAD_ELLIPSIS) + +OPCODE(ASSERT) +OPCODE(RAISE_ERROR) + +OPCODE(STORE_FUNCTION) +OPCODE(BUILD_CLASS) + +OPCODE(LOAD_NAME_PTR) // no arg +OPCODE(BUILD_ATTR_PTR) // arg for the name_ptr, [ptr, name_ptr] -> (*ptr).name_ptr +OPCODE(BUILD_INDEX_PTR) // no arg, [ptr, expr] -> (*ptr)[expr] +OPCODE(STORE_NAME_PTR) // arg for the name_ptr, [expr], directly store to the name_ptr without pushing it to the stack +OPCODE(STORE_PTR) // no arg, [ptr, expr] -> *ptr = expr +OPCODE(DELETE_PTR) // no arg, [ptr] -> [] -> delete ptr +OPCODE(BUILD_ATTR_PTR_PTR) // arg for the name_ptr, [ptr, name_ptr] -> (*ptr)->name_ptr + +OPCODE(BUILD_SMART_TUPLE) // if all elements are pointers, build a compound pointer, otherwise build a tuple +OPCODE(BUILD_STRING) // arg is the expr count, build a string from the top of the stack + +OPCODE(GOTO) +OPCODE(UNARY_REF) // for & +OPCODE(UNARY_DEREF) // for * + +OPCODE(WITH_ENTER) +OPCODE(WITH_EXIT) + +#endif + #undef OPCODE +}; + +static const char* OP_NAMES[] = { + #define OPCODE(name) #name, + #ifdef OPCODE + +OPCODE(NO_OP) +OPCODE(DELETED_OP) +OPCODE(LOAD_CONST) +OPCODE(IMPORT_NAME) +OPCODE(PRINT_EXPR) +OPCODE(POP_TOP) +OPCODE(CALL) +OPCODE(RETURN_VALUE) + +OPCODE(BINARY_OP) +OPCODE(COMPARE_OP) +OPCODE(BITWISE_OP) +OPCODE(IS_OP) +OPCODE(CONTAINS_OP) + +OPCODE(UNARY_NEGATIVE) +OPCODE(UNARY_NOT) + +OPCODE(DUP_TOP) + +OPCODE(BUILD_LIST) +OPCODE(BUILD_MAP) +OPCODE(BUILD_SLICE) + +OPCODE(LIST_APPEND) + +OPCODE(GET_ITER) +OPCODE(FOR_ITER) + +OPCODE(POP_JUMP_IF_FALSE) +OPCODE(JUMP_ABSOLUTE) +OPCODE(SAFE_JUMP_ABSOLUTE) +OPCODE(JUMP_IF_TRUE_OR_POP) +OPCODE(JUMP_IF_FALSE_OR_POP) + +// non-standard python opcodes +OPCODE(LOAD_NONE) +OPCODE(LOAD_TRUE) +OPCODE(LOAD_FALSE) +OPCODE(LOAD_EVAL_FN) // load eval() callable into stack +OPCODE(LOAD_LAMBDA) // LOAD_CONST + set __module__ attr +OPCODE(LOAD_ELLIPSIS) + +OPCODE(ASSERT) +OPCODE(RAISE_ERROR) + +OPCODE(STORE_FUNCTION) +OPCODE(BUILD_CLASS) + +OPCODE(LOAD_NAME_PTR) // no arg +OPCODE(BUILD_ATTR_PTR) // arg for the name_ptr, [ptr, name_ptr] -> (*ptr).name_ptr +OPCODE(BUILD_INDEX_PTR) // no arg, [ptr, expr] -> (*ptr)[expr] +OPCODE(STORE_NAME_PTR) // arg for the name_ptr, [expr], directly store to the name_ptr without pushing it to the stack +OPCODE(STORE_PTR) // no arg, [ptr, expr] -> *ptr = expr +OPCODE(DELETE_PTR) // no arg, [ptr] -> [] -> delete ptr +OPCODE(BUILD_ATTR_PTR_PTR) // arg for the name_ptr, [ptr, name_ptr] -> (*ptr)->name_ptr + +OPCODE(BUILD_SMART_TUPLE) // if all elements are pointers, build a compound pointer, otherwise build a tuple +OPCODE(BUILD_STRING) // arg is the expr count, build a string from the top of the stack + +OPCODE(GOTO) +OPCODE(UNARY_REF) // for & +OPCODE(UNARY_DEREF) // for * + +OPCODE(WITH_ENTER) +OPCODE(WITH_EXIT) + +#endif + #undef OPCODE +}; + +struct ByteCode{ + uint8_t op; + int arg; + uint16_t line; +}; + +_Str pad(const _Str& s, const int n){ + return s + std::string(n - s.size(), ' '); +} + +struct CodeObject { + _Source src; + _Str name; + + CodeObject(_Source src, _Str name) { + this->src = src; + this->name = name; + } + + CompileMode mode() const { + return src->mode; + } + + std::vector co_code; + PyVarList co_consts; + std::vector co_names; + std::vector<_Str> co_global_names; + + // for goto use + // note: some opcodes moves the bytecode, such as listcomp + // goto/label should be put at toplevel statements + emhash8::HashMap<_Str, int> co_labels; + + void addLabel(const _Str& label){ + if(co_labels.find(label) != co_labels.end()){ + _Str msg = "label '" + label + "' already exists"; + throw std::runtime_error(msg.c_str()); + } + co_labels[label] = co_code.size(); + } + + int addName(_Str name, NameScope scope){ + name.intern(); + if(scope == NAME_LOCAL && std::find(co_global_names.begin(), co_global_names.end(), name) != co_global_names.end()){ + scope = NAME_GLOBAL; + } + auto p = NamePointer(name, scope); + for(int i=0; igetTypeName(); + if(i != co_consts.size() - 1) consts << ", "; + } + + _StrStream names; + names << "co_names: "; + for(int i=0; i(&co_consts[i]->_native); + if(fn) ss << '\n' << (*fn)->code->name << ":\n" << (*fn)->code->toString(); + } + return _Str(ss.str()); + } +}; + +class Frame { +private: + std::vector s_data; + int ip = 0; + std::stack forLoops; // record the FOR_ITER bytecode index +public: + PyVar _module; + PyVarDict f_locals; + + uint64_t id; + + inline PyVarDict& f_globals(){ + return _module->attribs; + } + + const CodeObject* code; + + Frame(const CodeObject* code, PyVar _module, const PyVarDict& locals) + : code(code), _module(_module), f_locals(locals) { + + static uint64_t frame_id = 1; + id = frame_id++; + } + + inline const ByteCode& readCode() { + return code->co_code[ip++]; + } + + _Str errorSnapshot(){ + int line = code->co_code[ip-1].line; + return code->src->snapshot(line); + } + + inline int stackSize() const { + return s_data.size(); + } + + inline bool isCodeEnd() const { + return ip >= code->co_code.size(); + } + + inline PyVar __pop(){ + if(s_data.empty()) throw std::runtime_error("s_data.empty() is true"); + PyVar v = std::move(s_data.back()); + s_data.pop_back(); + return v; + } + + inline PyVar __deref_pointer(VM*, PyVar); + + inline PyVar popValue(VM* vm){ + return __deref_pointer(vm, __pop()); + } + + inline PyVar topValue(VM* vm){ + if(s_data.empty()) throw std::runtime_error("s_data.empty() is true"); + return __deref_pointer(vm, s_data.back()); + } + + inline PyVar& __top(){ + if(s_data.empty()) throw std::runtime_error("s_data.empty() is true"); + return s_data.back(); + } + + inline PyVar __topValueN(VM* vm, int n=-1){ + return __deref_pointer(vm, s_data[s_data.size() + n]); + } + + inline void push(const PyVar& v){ + s_data.push_back(v); + } + + inline void push(PyVar&& v){ + s_data.emplace_back(std::move(v)); + } + + + void __reportForIter(){ + int lastIp = ip - 1; + if(forLoops.empty()) forLoops.push(lastIp); + else{ + if(forLoops.top() == lastIp) return; + if(forLoops.top() < lastIp) forLoops.push(lastIp); + else UNREACHABLE(); + } + } + + inline void jump(int i){ + this->ip = i; + } + + void safeJump(int i){ + this->ip = i; + while(!forLoops.empty()){ + int start = forLoops.top(); + int end = code->co_code[start].arg; + if(i < start || i >= end){ + //printf("%d <- [%d, %d)\n", i, start, end); + __pop(); // pop the iterator + forLoops.pop(); + }else{ + break; + } + } + } + + pkpy::ArgList popNValuesReversed(VM* vm, int n){ + pkpy::ArgList v(n); + for(int i=n-1; i>=0; i--) v._index(i) = std::move(popValue(vm)); + return v; + } + + pkpy::ArgList __popNReversed(int n){ + pkpy::ArgList v(n); + for(int i=n-1; i>=0; i--) v._index(i) = std::move(__pop()); + return v; + } +}; + + +#define __DEF_PY_AS_C(type, ctype, ptype) \ + inline ctype& Py##type##_AS_C(const PyVar& obj) { \ + __checkType(obj, ptype); \ + return std::get(obj->_native); \ + } + +#define __DEF_PY(type, ctype, ptype) \ + inline PyVar Py##type(ctype value) { \ + return newObject(ptype, value); \ + } + +#define DEF_NATIVE(type, ctype, ptype) \ + __DEF_PY(type, ctype, ptype) \ + __DEF_PY_AS_C(type, ctype, ptype) + +typedef void(*PrintFn)(const VM*, const char*); + +class VM { + std::atomic _stopFlag = false; + std::vector _smallIntegers; // [-5, 256] +protected: + std::deque< pkpy::unique_ptr > callstack; + PyVarDict _modules; // loaded modules + std::map<_Str, _Code> _lazyModules; // lazy loaded modules + PyVar __py2py_call_signal; + + void _checkStopFlag(){ + if(_stopFlag){ + _stopFlag = false; + _error("KeyboardInterrupt", ""); + } + } + + PyVar runFrame(Frame* frame){ + while(!frame->isCodeEnd()){ + const ByteCode& byte = frame->readCode(); + //printf("[%d] %s (%d)\n", frame->stackSize(), OP_NAMES[byte.op], byte.arg); + //printf("%s\n", frame->code->src->getLine(byte.line).c_str()); + + _checkStopFlag(); + + switch (byte.op) + { + case OP_NO_OP: break; // do nothing + case OP_LOAD_CONST: frame->push(frame->code->co_consts[byte.arg]); break; + case OP_LOAD_LAMBDA: { + PyVar obj = frame->code->co_consts[byte.arg]; + setAttr(obj, __module__, frame->_module); + frame->push(obj); + } break; + case OP_LOAD_NAME_PTR: { + const BasePointer* p = new NamePointer(frame->code->co_names[byte.arg]); + frame->push(PyPointer(_Pointer(p))); + } break; + case OP_STORE_NAME_PTR: { + const auto& p = frame->code->co_names[byte.arg]; + p.set(this, frame, frame->popValue(this)); + } break; + case OP_BUILD_ATTR_PTR: { + const auto& attr = frame->code->co_names[byte.arg]; + PyVar obj = frame->popValue(this); + const BasePointer* p = new AttrPointer(obj, &attr); + frame->push(PyPointer(_Pointer(p))); + } break; + case OP_BUILD_ATTR_PTR_PTR: { + const auto& attr = frame->code->co_names[byte.arg]; + PyVar obj = frame->popValue(this); + __checkType(obj, _tp_user_pointer); + const _Pointer& p = std::get<_Pointer>(obj->_native); + frame->push(PyPointer(_Pointer(new AttrPointer(p->get(this, frame), &attr)))); + } break; + case OP_BUILD_INDEX_PTR: { + PyVar index = frame->popValue(this); + PyVar obj = frame->popValue(this); + frame->push(PyPointer(_Pointer(new IndexPointer(obj, index)))); + } break; + case OP_STORE_PTR: { + PyVar obj = frame->popValue(this); + const _Pointer p = PyPointer_AS_C(frame->__pop()); + p->set(this, frame, std::move(obj)); + } break; + case OP_DELETE_PTR: { + const _Pointer p = PyPointer_AS_C(frame->__pop()); + p->del(this, frame); + } break; + case OP_BUILD_SMART_TUPLE: + { + pkpy::ArgList items = frame->__popNReversed(byte.arg); + bool done = false; + for(int i=0; iisType(_tp_pointer)) { + done = true; + PyVarList values(items.size()); + for(int i=0; i__deref_pointer(this, items[i]); + } + frame->push(PyTuple(values)); + break; + } + } + if(done) break; + std::vector<_Pointer> pointers(items.size()); + for(int i=0; ipush(PyPointer(_Pointer(new CompoundPointer(pointers)))); + } break; + case OP_BUILD_STRING: + { + pkpy::ArgList items = frame->popNValuesReversed(this, byte.arg); + _StrStream ss; + for(int i=0; ipush(PyStr(ss.str())); + } break; + case OP_LOAD_EVAL_FN: { + frame->push(builtins->attribs["eval"_c]); + } break; + case OP_LIST_APPEND: { + pkpy::ArgList args(2); + args[1] = frame->popValue(this); // obj + args[0] = frame->__topValueN(this, -2); // list + fastCall("append"_c, std::move(args)); + } break; + case OP_STORE_FUNCTION: + { + PyVar obj = frame->popValue(this); + const _Func& fn = PyFunction_AS_C(obj); + setAttr(obj, __module__, frame->_module); + frame->f_globals()[fn->name] = obj; + } break; + case OP_BUILD_CLASS: + { + const _Str& clsName = frame->code->co_names[byte.arg].name; + PyVar clsBase = frame->popValue(this); + if(clsBase == None) clsBase = _tp_object; + __checkType(clsBase, _tp_type); + PyVar cls = newUserClassType(clsName, clsBase); + while(true){ + PyVar fn = frame->popValue(this); + if(fn == None) break; + const _Func& f = PyFunction_AS_C(fn); + setAttr(fn, __module__, frame->_module); + setAttr(cls, f->name, fn); + } + frame->f_globals()[clsName] = cls; + } break; + case OP_RETURN_VALUE: return frame->popValue(this); + case OP_PRINT_EXPR: + { + const PyVar expr = frame->topValue(this); + if(expr == None) break; + *_stdout << PyStr_AS_C(asRepr(expr)) << '\n'; + } break; + case OP_POP_TOP: frame->popValue(this); break; + case OP_BINARY_OP: + { + pkpy::ArgList args = frame->popNValuesReversed(this, 2); + frame->push(fastCall(BINARY_SPECIAL_METHODS[byte.arg], std::move(args))); + } break; + case OP_BITWISE_OP: + { + pkpy::ArgList args = frame->popNValuesReversed(this, 2); + frame->push(fastCall(BITWISE_SPECIAL_METHODS[byte.arg], std::move(args))); + } break; + case OP_COMPARE_OP: + { + pkpy::ArgList args = frame->popNValuesReversed(this, 2); + // for __ne__ we use the negation of __eq__ + int op = byte.arg == 3 ? 2 : byte.arg; + PyVar res = fastCall(CMP_SPECIAL_METHODS[op], std::move(args)); + if(op != byte.arg) res = PyBool(!PyBool_AS_C(res)); + frame->push(std::move(res)); + } break; + case OP_IS_OP: + { + bool ret_c = frame->popValue(this) == frame->popValue(this); + if(byte.arg == 1) ret_c = !ret_c; + frame->push(PyBool(ret_c)); + } break; + case OP_CONTAINS_OP: + { + PyVar rhs = frame->popValue(this); + PyVar lhs = frame->popValue(this); + bool ret_c = PyBool_AS_C(call(rhs, __contains__, {std::move(lhs)})); + if(byte.arg == 1) ret_c = !ret_c; + frame->push(PyBool(ret_c)); + } break; + case OP_UNARY_NEGATIVE: + { + PyVar obj = frame->popValue(this); + frame->push(numNegated(obj)); + } break; + case OP_UNARY_NOT: + { + PyVar obj = frame->popValue(this); + const PyVar& obj_bool = asBool(obj); + frame->push(PyBool(!PyBool_AS_C(obj_bool))); + } break; + case OP_UNARY_REF: + { + // _pointer to pointer + const _Pointer p = PyPointer_AS_C(frame->__pop()); + _Pointer up(new UserPointer(p, frame->id)); + frame->push(newObject(_tp_user_pointer, std::move(up))); + } break; + case OP_UNARY_DEREF: + { + // pointer to _pointer + PyVar obj = frame->popValue(this); + __checkType(obj, _tp_user_pointer); + frame->push(PyPointer(std::get<_Pointer>(obj->_native))); + } break; + case OP_POP_JUMP_IF_FALSE: + if(!PyBool_AS_C(asBool(frame->popValue(this)))) frame->jump(byte.arg); + break; + case OP_LOAD_NONE: frame->push(None); break; + case OP_LOAD_TRUE: frame->push(True); break; + case OP_LOAD_FALSE: frame->push(False); break; + case OP_LOAD_ELLIPSIS: frame->push(Ellipsis); break; + case OP_ASSERT: + { + PyVar expr = frame->popValue(this); + _assert(PyBool_AS_C(expr), "assertion failed"); + } break; + case OP_RAISE_ERROR: + { + _Str msg = PyStr_AS_C(asRepr(frame->popValue(this))); + _Str type = PyStr_AS_C(frame->popValue(this)); + _error(type, msg); + } break; + case OP_BUILD_LIST: + { + pkpy::ArgList items = frame->popNValuesReversed(this, byte.arg); + frame->push(PyList(items.toList())); + } break; + case OP_BUILD_MAP: + { + pkpy::ArgList items = frame->popNValuesReversed(this, byte.arg*2); + PyVar obj = call(builtins->attribs["dict"], {}); + for(int i=0; ipush(obj); + } break; + case OP_DUP_TOP: frame->push(frame->topValue(this)); break; + case OP_CALL: + { + pkpy::ArgList args = frame->popNValuesReversed(this, byte.arg); + PyVar callable = frame->popValue(this); + PyVar ret = call(std::move(callable), std::move(args), true); + if(ret == __py2py_call_signal) return ret; + frame->push(std::move(ret)); + } break; + case OP_JUMP_ABSOLUTE: frame->jump(byte.arg); break; + case OP_SAFE_JUMP_ABSOLUTE: frame->safeJump(byte.arg); break; + case OP_GOTO: { + PyVar obj = frame->popValue(this); + const _Str& label = PyStr_AS_C(obj); + auto it = frame->code->co_labels.find(label); + if(it == frame->code->co_labels.end()){ + _error("KeyError", "label '" + label + "' not found"); + } + frame->safeJump(it->second); + } break; + case OP_GET_ITER: + { + PyVar obj = frame->popValue(this); + PyVarOrNull iter_fn = getAttr(obj, __iter__, false); + if(iter_fn != nullptr){ + PyVar tmp = call(iter_fn, {obj}); + PyIter_AS_C(tmp)->var = std::move(PyPointer_AS_C(frame->__pop())); + frame->push(std::move(tmp)); + }else{ + typeError("'" + obj->getTypeName() + "' object is not iterable"); + } + } break; + case OP_FOR_ITER: + { + frame->__reportForIter(); + // __top() must be PyIter, so no need to __deref() + auto& it = PyIter_AS_C(frame->__top()); + if(it->hasNext()){ + it->var->set(this, frame, it->next()); + } + else{ + frame->safeJump(byte.arg); + } + } break; + case OP_JUMP_IF_FALSE_OR_POP: + { + const PyVar expr = frame->topValue(this); + if(asBool(expr)==False) frame->jump(byte.arg); + else frame->popValue(this); + } break; + case OP_JUMP_IF_TRUE_OR_POP: + { + const PyVar expr = frame->topValue(this); + if(asBool(expr)==True) frame->jump(byte.arg); + else frame->popValue(this); + } break; + case OP_BUILD_SLICE: + { + PyVar stop = frame->popValue(this); + PyVar start = frame->popValue(this); + _Slice s; + if(start != None) {__checkType(start, _tp_int); s.start = (int)PyInt_AS_C(start);} + if(stop != None) {__checkType(stop, _tp_int); s.stop = (int)PyInt_AS_C(stop);} + frame->push(PySlice(s)); + } break; + case OP_IMPORT_NAME: + { + const _Str& name = frame->code->co_names[byte.arg].name; + auto it = _modules.find(name); + if(it == _modules.end()){ + auto it2 = _lazyModules.find(name); + if(it2 == _lazyModules.end()){ + _error("ImportError", "module '" + name + "' not found"); + }else{ + _Code code = it2->second; + PyVar _m = newModule(name); + _exec(code, _m, {}); + frame->push(_m); + _lazyModules.erase(it2); + } + }else{ + frame->push(it->second); + } + } break; + case OP_WITH_ENTER: + { + PyVar obj = frame->popValue(this); + PyVar enter_fn = getAttr(obj, "__enter__"_c); + call(enter_fn, {}); + } break; + case OP_WITH_EXIT: + { + PyVar obj = frame->popValue(this); + PyVar exit_fn = getAttr(obj, "__exit__"_c); + call(exit_fn, {}); + } break; + default: + systemError(_Str("opcode ") + OP_NAMES[byte.op] + " is not implemented"); + break; + } + } + + if(frame->code->src->mode == EVAL_MODE || frame->code->src->mode == JSON_MODE){ + if(frame->stackSize() != 1) systemError("stack size is not 1 in EVAL_MODE/JSON_MODE"); + return frame->popValue(this); + } + + if(frame->stackSize() != 0) systemError("stack not empty in EXEC_MODE"); + return None; + } + +public: + PyVarDict _types; + PyVar None, True, False, Ellipsis; + + bool use_stdio; + std::ostream* _stdout; + std::ostream* _stderr; + + PyVar builtins; // builtins module + PyVar _main; // __main__ module + + int maxRecursionDepth = 1000; + + VM(bool use_stdio){ + this->use_stdio = use_stdio; + if(use_stdio){ + std::cout.setf(std::ios::unitbuf); + std::cerr.setf(std::ios::unitbuf); + this->_stdout = &std::cout; + this->_stderr = &std::cerr; + }else{ + this->_stdout = new _StrStream(); + this->_stderr = new _StrStream(); + } + initializeBuiltinClasses(); + + _smallIntegers.reserve(300); + for(_Int i=-5; i<=256; i++) _smallIntegers.push_back(newObject(_tp_int, i)); + } + + void keyboardInterrupt(){ + _stopFlag = true; + } + + void sleepForSecs(_Float sec){ + _Int ms = (_Int)(sec * 1000); + for(_Int i=0; iget()->id; + if(f_id == up_f_id) return it->get(); + if(f_id < up_f_id) return nullptr; + } + return nullptr; + } + + Frame* topFrame(){ + if(callstack.size() == 0) UNREACHABLE(); + return callstack.back().get(); + } + + PyVar asRepr(const PyVar& obj){ + if(obj->isType(_tp_type)) return PyStr("getName() + "'>"); + return call(obj, __repr__, {}); + } + + PyVar asJson(const PyVar& obj){ + return call(obj, __json__, {}); + } + + const PyVar& asBool(const PyVar& obj){ + if(obj == None) return False; + if(obj->_type == _tp_bool) return obj; + if(obj->_type == _tp_int) return PyBool(PyInt_AS_C(obj) != 0); + if(obj->_type == _tp_float) return PyBool(PyFloat_AS_C(obj) != 0.0); + PyVarOrNull len_fn = getAttr(obj, __len__, false); + if(len_fn != nullptr){ + PyVar ret = call(std::move(len_fn), {}); + return PyBool(PyInt_AS_C(ret) > 0); + } + return True; + } + + PyVar fastCall(const _Str& name, pkpy::ArgList&& args){ + const PyVar& obj = args[0]; + PyObject* cls = obj->_type.get(); + while(cls != None.get()) { + auto it = cls->attribs.find(name); + if(it != cls->attribs.end()){ + return call(it->second, args); + } + cls = cls->attribs[__base__].get(); + } + attributeError(obj, name); + return nullptr; + } + + PyVar call(const PyVar& _callable, pkpy::ArgList args, bool opCall=false){ + if(_callable->isType(_tp_type)){ + auto it = _callable->attribs.find(__new__); + PyVar obj; + if(it != _callable->attribs.end()){ + obj = call(it->second, args); + }else{ + obj = newObject(_callable, (_Int)-1); + PyVarOrNull init_fn = getAttr(obj, __init__, false); + if (init_fn != nullptr) call(init_fn, args); + } + return obj; + } + + const PyVar* callable = &_callable; + if((*callable)->isType(_tp_bounded_method)){ + auto& bm = PyBoundedMethod_AS_C((*callable)); + // TODO: avoid insertion here, bad performance + pkpy::ArgList new_args(args.size()+1); + new_args[0] = bm.obj; + for(int i=0; iisType(_tp_native_function)){ + const auto& f = std::get<_CppFunc>((*callable)->_native); + return f(this, args); + } else if((*callable)->isType(_tp_function)){ + const _Func& fn = PyFunction_AS_C((*callable)); + PyVarDict locals; + int i = 0; + for(const auto& name : fn->args){ + if(i < args.size()) { + locals[name] = args[i++]; + }else{ + typeError("missing positional argument '" + name + "'"); + } + } + // handle *args + if(!fn->starredArg.empty()){ + PyVarList vargs; + while(i < args.size()) vargs.push_back(args[i++]); + locals[fn->starredArg] = PyTuple(vargs); + } + // handle keyword arguments + for(const _Str& name : fn->kwArgsOrder){ + if(i < args.size()) { + locals[name] = args[i++]; + }else{ + locals[name] = fn->kwArgs[name]; + } + } + + if(i < args.size()) typeError("too many arguments"); + + auto it_m = (*callable)->attribs.find(__module__); + PyVar _module = it_m != (*callable)->attribs.end() ? it_m->second : topFrame()->_module; + if(opCall){ + __pushNewFrame(fn->code, _module, locals); + return __py2py_call_signal; + } + return _exec(fn->code, _module, locals); + } + typeError("'" + (*callable)->getTypeName() + "' object is not callable"); + return None; + } + + inline PyVar call(const PyVar& obj, const _Str& func, const pkpy::ArgList& args){ + return call(getAttr(obj, func), args); + } + + inline PyVar call(const PyVar& obj, const _Str& func, pkpy::ArgList&& args){ + return call(getAttr(obj, func), args); + } + + // repl mode is only for setting `frame->id` to 0 + virtual PyVarOrNull exec(const _Code& code, PyVar _module=nullptr){ + if(_module == nullptr) _module = _main; + try { + return _exec(code, _module, {}); + } catch (const std::exception& e) { + if(const _Error* _ = dynamic_cast(&e)){ + *_stderr << e.what() << '\n'; + }else{ + auto re = RuntimeError("UnexpectedError", e.what(), _cleanErrorAndGetSnapshots()); + *_stderr << re.what() << '\n'; + } + return nullptr; + } + } + + virtual void execAsync(const _Code& code) { + exec(code); + } + + Frame* __pushNewFrame(const _Code& code, PyVar _module, const PyVarDict& locals){ + if(code == nullptr) UNREACHABLE(); + if(callstack.size() > maxRecursionDepth){ + throw RuntimeError("RecursionError", "maximum recursion depth exceeded", _cleanErrorAndGetSnapshots()); + } + Frame* frame = new Frame(code.get(), _module, locals); + callstack.emplace_back(pkpy::unique_ptr(frame)); + return frame; + } + + PyVar _exec(const _Code& code, PyVar _module, const PyVarDict& locals){ + Frame* frame = __pushNewFrame(code, _module, locals); + if(code->mode() == SINGLE_MODE) frame->id = 0; + Frame* frameBase = frame; + PyVar ret = nullptr; + + while(true){ + ret = runFrame(frame); + if(ret != __py2py_call_signal){ + if(frame == frameBase){ // [ frameBase<- ] + break; + }else{ + callstack.pop_back(); + frame = callstack.back().get(); + frame->push(ret); + } + }else{ + frame = callstack.back().get(); // [ frameBase, newFrame<- ] + } + } + + callstack.pop_back(); + return ret; + } + + PyVar newUserClassType(_Str name, PyVar base){ + PyVar obj = newClassType(name, base); + setAttr(obj, __name__, PyStr(name)); + _types.erase(name); + return obj; + } + + PyVar newClassType(_Str name, PyVar base=nullptr) { + if(base == nullptr) base = _tp_object; + PyVar obj = pkpy::make_shared((_Int)0); + obj->setType(_tp_type); + setAttr(obj, __base__, base); + _types[name] = obj; + return obj; + } + + PyVar newObject(PyVar type, const _Value& _native) { + __checkType(type, _tp_type); + PyVar obj = pkpy::make_shared(_native); + obj->setType(type); + return obj; + } + + PyVar newModule(_Str name) { + PyVar obj = newObject(_tp_module, (_Int)-2); + setAttr(obj, __name__, PyStr(name)); + _modules[name] = obj; + return obj; + } + + void addLazyModule(_Str name, _Code code){ + _lazyModules[name] = code; + } + + PyVarOrNull getAttr(const PyVar& obj, const _Str& name, bool throw_err=true) { + PyVarDict::iterator it; + PyObject* cls; + + if(obj->isType(_tp_super)){ + const PyVar* root = &obj; + int depth = 1; + while(true){ + root = &std::get((*root)->_native); + if(!(*root)->isType(_tp_super)) break; + depth++; + } + cls = (*root)->_type.get(); + for(int i=0; iattribs[__base__].get(); + + it = (*root)->attribs.find(name); + if(it != (*root)->attribs.end()) return it->second; + }else{ + it = obj->attribs.find(name); + if(it != obj->attribs.end()) return it->second; + cls = obj->_type.get(); + } + + while(cls != None.get()) { + it = cls->attribs.find(name); + if(it != cls->attribs.end()){ + PyVar valueFromCls = it->second; + if(valueFromCls->isType(_tp_function) || valueFromCls->isType(_tp_native_function)){ + return PyBoundedMethod({obj, std::move(valueFromCls)}); + }else{ + return valueFromCls; + } + } + cls = cls->attribs[__base__].get(); + } + if(throw_err) attributeError(obj, name); + return nullptr; + } + + inline void setAttr(PyVar& obj, const _Str& name, const PyVar& value) { + if(obj->isType(_tp_super)){ + const PyVar* root = &obj; + while(true){ + root = &std::get((*root)->_native); + if(!(*root)->isType(_tp_super)) break; + } + (*root)->attribs[name] = value; + }else{ + obj->attribs[name] = value; + } + } + + inline void setAttr(PyVar& obj, const _Str& name, PyVar&& value) { + if(obj->isType(_tp_super)){ + const PyVar* root = &obj; + while(true){ + root = &std::get((*root)->_native); + if(!(*root)->isType(_tp_super)) break; + } + (*root)->attribs[name] = std::move(value); + }else{ + obj->attribs[name] = std::move(value); + } + } + + void bindMethod(_Str typeName, _Str funcName, _CppFunc fn) { + funcName.intern(); + PyVar type = _types[typeName]; + PyVar func = PyNativeFunction(fn); + setAttr(type, funcName, func); + } + + void bindMethodMulti(std::vector<_Str> typeNames, _Str funcName, _CppFunc fn) { + for(auto& typeName : typeNames){ + bindMethod(typeName, funcName, fn); + } + } + + void bindBuiltinFunc(_Str funcName, _CppFunc fn) { + bindFunc(builtins, funcName, fn); + } + + void bindFunc(PyVar module, _Str funcName, _CppFunc fn) { + funcName.intern(); + __checkType(module, _tp_module); + PyVar func = PyNativeFunction(fn); + setAttr(module, funcName, func); + } + + bool isInstance(PyVar obj, PyVar type){ + __checkType(type, _tp_type); + PyVar t = obj->_type; + while (t != None){ + if (t == type) return true; + t = t->attribs[__base__]; + } + return false; + } + + inline bool isIntOrFloat(const PyVar& obj){ + return obj->isType(_tp_int) || obj->isType(_tp_float); + } + + inline bool isIntOrFloat(const PyVar& obj1, const PyVar& obj2){ + return isIntOrFloat(obj1) && isIntOrFloat(obj2); + } + + _Float numToFloat(const PyVar& obj){ + if (obj->isType(_tp_int)){ + return (_Float)PyInt_AS_C(obj); + }else if(obj->isType(_tp_float)){ + return PyFloat_AS_C(obj); + } + UNREACHABLE(); + } + + PyVar numNegated(const PyVar& obj){ + if (obj->isType(_tp_int)){ + return PyInt(-PyInt_AS_C(obj)); + }else if(obj->isType(_tp_float)){ + return PyFloat(-PyFloat_AS_C(obj)); + } + typeError("unsupported operand type(s) for -"); + return nullptr; + } + + int normalizedIndex(int index, int size){ + if(index < 0) index += size; + if(index < 0 || index >= size){ + indexError("index out of range, " + std::to_string(index) + " not in [0, " + std::to_string(size) + ")"); + } + return index; + } + + // for quick access + PyVar _tp_object, _tp_type, _tp_int, _tp_float, _tp_bool, _tp_str; + PyVar _tp_list, _tp_tuple; + PyVar _tp_function, _tp_native_function, _tp_native_iterator, _tp_bounded_method; + PyVar _tp_slice, _tp_range, _tp_module, _tp_pointer; + PyVar _tp_user_pointer, _tp_super; + + __DEF_PY(Pointer, _Pointer, _tp_pointer) + inline _Pointer& PyPointer_AS_C(const PyVar& obj) + { + if(!obj->isType(_tp_pointer)) typeError("expected an l-value"); + return std::get<_Pointer>(obj->_native); + } + + __DEF_PY_AS_C(Int, _Int, _tp_int) + inline PyVar PyInt(_Int value) { + if(value >= -5 && value <= 256) return _smallIntegers[value + 5]; + return newObject(_tp_int, value); + } + + DEF_NATIVE(Float, _Float, _tp_float) + DEF_NATIVE(Str, _Str, _tp_str) + DEF_NATIVE(List, PyVarList, _tp_list) + DEF_NATIVE(Tuple, PyVarList, _tp_tuple) + DEF_NATIVE(Function, _Func, _tp_function) + DEF_NATIVE(NativeFunction, _CppFunc, _tp_native_function) + DEF_NATIVE(Iter, pkpy::shared_ptr<_Iterator>, _tp_native_iterator) + DEF_NATIVE(BoundedMethod, _BoundedMethod, _tp_bounded_method) + DEF_NATIVE(Range, _Range, _tp_range) + DEF_NATIVE(Slice, _Slice, _tp_slice) + + // there is only one True/False, so no need to copy them! + inline bool PyBool_AS_C(const PyVar& obj){return obj == True;} + inline const PyVar& PyBool(bool value){return value ? True : False;} + + void initializeBuiltinClasses(){ + _tp_object = pkpy::make_shared((_Int)0); + _tp_type = pkpy::make_shared((_Int)0); + + _types["object"] = _tp_object; + _types["type"] = _tp_type; + + _tp_bool = newClassType("bool"); + _tp_int = newClassType("int"); + _tp_float = newClassType("float"); + _tp_str = newClassType("str"); + _tp_list = newClassType("list"); + _tp_tuple = newClassType("tuple"); + _tp_slice = newClassType("slice"); + _tp_range = newClassType("range"); + _tp_module = newClassType("module"); + _tp_pointer = newClassType("_pointer"); + _tp_user_pointer = newClassType("pointer"); + + newClassType("NoneType"); + newClassType("ellipsis"); + + _tp_function = newClassType("function"); + _tp_native_function = newClassType("_native_function"); + _tp_native_iterator = newClassType("_native_iterator"); + _tp_bounded_method = newClassType("_bounded_method"); + _tp_super = newClassType("super"); + + this->None = newObject(_types["NoneType"], (_Int)0); + this->Ellipsis = newObject(_types["ellipsis"], (_Int)0); + this->True = newObject(_tp_bool, true); + this->False = newObject(_tp_bool, false); + this->builtins = newModule("builtins"); + this->_main = newModule("__main__"_c); + + setAttr(_tp_type, __base__, _tp_object); + _tp_type->setType(_tp_type); + setAttr(_tp_object, __base__, None); + _tp_object->setType(_tp_type); + + for (auto& [name, type] : _types) { + setAttr(type, __name__, PyStr(name)); + } + + this->__py2py_call_signal = newObject(_tp_object, (_Int)7); + + std::vector<_Str> publicTypes = {"type", "object", "bool", "int", "float", "str", "list", "tuple", "range"}; + for (auto& name : publicTypes) { + setAttr(builtins, name, _types[name]); + } + } + + _Int hash(const PyVar& obj){ + if (obj->isType(_tp_int)) return PyInt_AS_C(obj); + if (obj->isType(_tp_bool)) return PyBool_AS_C(obj) ? 1 : 0; + if (obj->isType(_tp_float)){ + _Float val = PyFloat_AS_C(obj); + return (_Int)std::hash<_Float>()(val); + } + if (obj->isType(_tp_str)) return PyStr_AS_C(obj).hash(); + if (obj->isType(_tp_type)) return (_Int)obj.get(); + if (obj->isType(_tp_tuple)) { + _Int x = 1000003; + for (const auto& item : PyTuple_AS_C(obj)) { + _Int y = hash(item); + // this is recommended by Github Copilot + // i am not sure whether it is a good idea + x = x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2)); + } + return x; + } + typeError("unhashable type: " + obj->getTypeName()); + return 0; + } + + /***** Error Reporter *****/ +private: + void _error(const _Str& name, const _Str& msg){ + throw RuntimeError(name, msg, _cleanErrorAndGetSnapshots()); + } + + std::stack<_Str> _cleanErrorAndGetSnapshots(){ + std::stack<_Str> snapshots; + while (!callstack.empty()){ + if(snapshots.size() < 8){ + snapshots.push(callstack.back()->errorSnapshot()); + } + callstack.pop_back(); + } + return snapshots; + } + +public: + void typeError(const _Str& msg){ + _error("TypeError", msg); + } + + void systemError(const _Str& msg){ + _error("SystemError", msg); + } + + void nullPointerError(){ + _error("NullPointerError", "pointer is invalid"); + } + + void zeroDivisionError(){ + _error("ZeroDivisionError", "division by zero"); + } + + void indexError(const _Str& msg){ + _error("IndexError", msg); + } + + void valueError(const _Str& msg){ + _error("ValueError", msg); + } + + void nameError(const _Str& name){ + _error("NameError", "name '" + name + "' is not defined"); + } + + void attributeError(PyVar obj, const _Str& name){ + _error("AttributeError", "type '" + obj->getTypeName() + "' has no attribute '" + name + "'"); + } + + inline void __checkType(const PyVar& obj, const PyVar& type){ + if(!obj->isType(type)) typeError("expected '" + type->getName() + "', but got '" + obj->getTypeName() + "'"); + } + + inline void __checkArgSize(const pkpy::ArgList& args, int size, bool method=false){ + if(args.size() == size) return; + if(method) typeError(args.size()>size ? "too many arguments" : "too few arguments"); + else typeError("expected " + std::to_string(size) + " arguments, but got " + std::to_string(args.size())); + } + + void _assert(bool val, const _Str& msg){ + if (!val) _error("AssertionError", msg); + } + + virtual ~VM() { + if(!use_stdio){ + delete _stdout; + delete _stderr; + } + } +}; + +/***** Pointers' Impl *****/ + +PyVar NamePointer::get(VM* vm, Frame* frame) const{ + auto it = frame->f_locals.find(name); + if(it != frame->f_locals.end()) return it->second; + it = frame->f_globals().find(name); + if(it != frame->f_globals().end()) return it->second; + it = vm->builtins->attribs.find(name); + if(it != vm->builtins->attribs.end()) return it->second; + vm->nameError(name); + return nullptr; +} + +void NamePointer::set(VM* vm, Frame* frame, PyVar val) const{ + switch(scope) { + case NAME_LOCAL: frame->f_locals[name] = std::move(val); break; + case NAME_GLOBAL: + { + if(frame->f_locals.count(name) > 0){ + frame->f_locals[name] = std::move(val); + }else{ + frame->f_globals()[name] = std::move(val); + } + } break; + default: UNREACHABLE(); + } +} + +void NamePointer::del(VM* vm, Frame* frame) const{ + switch(scope) { + case NAME_LOCAL: { + if(frame->f_locals.count(name) > 0){ + frame->f_locals.erase(name); + }else{ + vm->nameError(name); + } + } break; + case NAME_GLOBAL: + { + if(frame->f_locals.count(name) > 0){ + frame->f_locals.erase(name); + }else{ + if(frame->f_globals().count(name) > 0){ + frame->f_globals().erase(name); + }else{ + vm->nameError(name); + } + } + } break; + default: UNREACHABLE(); + } +} + +PyVar AttrPointer::get(VM* vm, Frame* frame) const{ + return vm->getAttr(obj, attr->name); +} + +void AttrPointer::set(VM* vm, Frame* frame, PyVar val) const{ + vm->setAttr(obj, attr->name, val); +} + +void AttrPointer::del(VM* vm, Frame* frame) const{ + vm->typeError("cannot delete attribute"); +} + +PyVar IndexPointer::get(VM* vm, Frame* frame) const{ + return vm->call(obj, __getitem__, {index}); +} + +void IndexPointer::set(VM* vm, Frame* frame, PyVar val) const{ + vm->call(obj, __setitem__, {index, val}); +} + +void IndexPointer::del(VM* vm, Frame* frame) const{ + vm->call(obj, __delitem__, {index}); +} + +PyVar CompoundPointer::get(VM* vm, Frame* frame) const{ + PyVarList args(pointers.size()); + for (int i = 0; i < pointers.size(); i++) { + args[i] = pointers[i]->get(vm, frame); + } + return vm->PyTuple(args); +} + +void CompoundPointer::set(VM* vm, Frame* frame, PyVar val) const{ + if(!val->isType(vm->_tp_tuple) && !val->isType(vm->_tp_list)){ + vm->typeError("only tuple or list can be unpacked"); + } + const PyVarList& args = std::get(val->_native); + if(args.size() > pointers.size()) vm->valueError("too many values to unpack"); + if(args.size() < pointers.size()) vm->valueError("not enough values to unpack"); + for (int i = 0; i < pointers.size(); i++) { + pointers[i]->set(vm, frame, args[i]); + } +} + +void CompoundPointer::del(VM* vm, Frame* frame) const{ + for (auto& ptr : pointers) ptr->del(vm, frame); +} + +PyVar UserPointer::get(VM* vm, Frame* frame) const{ + frame = vm->__findFrame(f_id); + if(frame == nullptr) vm->nullPointerError(); + return p->get(vm, frame); +} + +void UserPointer::set(VM* vm, Frame* frame, PyVar val) const{ + frame = vm->__findFrame(f_id); + if(frame == nullptr) vm->nullPointerError(); + p->set(vm, frame, val); +} + +void UserPointer::del(VM* vm, Frame* frame) const{ + vm->typeError("delete is unsupported"); +} + +/***** Frame's Impl *****/ +inline PyVar Frame::__deref_pointer(VM* vm, PyVar v){ + if(v->isType(vm->_tp_pointer)) return vm->PyPointer_AS_C(v)->get(vm, this); + return v; +} + +/***** Iterators' Impl *****/ +PyVar RangeIterator::next(){ + PyVar val = vm->PyInt(current); + current += r.step; + return val; +} + +PyVar StringIterator::next(){ + return vm->PyStr(str.u8_getitem(index++)); +} + +enum ThreadState { + THREAD_READY, + THREAD_RUNNING, + THREAD_SUSPENDED, + THREAD_FINISHED +}; + +class ThreadedVM : public VM { + std::thread* _thread = nullptr; + std::atomic _state = THREAD_READY; + _Str _sharedStr = ""_c; + + void __deleteThread(){ + if(_thread != nullptr){ + terminate(); + _thread->join(); + delete _thread; + _thread = nullptr; + } + } +public: + ThreadedVM(bool use_stdio) : VM(use_stdio) { + bindBuiltinFunc("__string_channel_call", [](VM* vm, const pkpy::ArgList& args){ + vm->__checkArgSize(args, 1); + _Str data = vm->PyStr_AS_C(args[0]); + + ThreadedVM* tvm = (ThreadedVM*)vm; + tvm->_sharedStr = data; + tvm->suspend(); + return tvm->PyStr(tvm->readJsonRpcRequest()); + }); + } + + void terminate(){ + if(_state == THREAD_RUNNING || _state == THREAD_SUSPENDED){ + keyboardInterrupt(); + while(_state != THREAD_FINISHED); + } + } + + void suspend(){ + if(_state != THREAD_RUNNING) UNREACHABLE(); + _state = THREAD_SUSPENDED; + while(_state == THREAD_SUSPENDED) _checkStopFlag(); + } + + _Str readJsonRpcRequest(){ + _Str copy = _sharedStr; + _sharedStr = ""_c; + return copy; + } + + /***** For outer use *****/ + + ThreadState getState(){ + return _state; + } + + void writeJsonrpcResponse(const char* value){ + if(_state != THREAD_SUSPENDED) UNREACHABLE(); + _sharedStr = _Str(value); + _state = THREAD_RUNNING; + } + + void execAsync(const _Code& code) override { + if(_state != THREAD_READY) UNREACHABLE(); + __deleteThread(); + _thread = new std::thread([this, code](){ + this->_state = THREAD_RUNNING; + VM::exec(code); + this->_state = THREAD_FINISHED; + }); + } + + PyVarOrNull exec(const _Code& code, PyVar _module = nullptr) override { + if(_state == THREAD_READY) return VM::exec(code, _module); + auto callstackBackup = std::move(callstack); + callstack.clear(); + PyVarOrNull ret = VM::exec(code, _module); + callstack = std::move(callstackBackup); + return ret; + } + + void resetState(){ + if(this->_state != THREAD_FINISHED) return; + this->_state = THREAD_READY; + } + + ~ThreadedVM(){ + __deleteThread(); + } +}; + + +class Compiler; + +typedef void (Compiler::*GrammarFn)(); +typedef void (Compiler::*CompilerAction)(); + +struct GrammarRule{ + GrammarFn prefix; + GrammarFn infix; + Precedence precedence; +}; + +struct Loop { + int start; + std::vector breaks; + Loop(int start) : start(start) {} +}; + +class Compiler { +public: + pkpy::unique_ptr parser; + std::stack<_Code> codes; + std::stack loops; + bool isCompilingClass = false; + VM* vm; + + emhash8::HashMap<_TokenType, GrammarRule> rules; + + _Code getCode() { + return codes.top(); + } + + CompileMode mode() { + return parser->src->mode; + } + + Loop& getLoop() { + return loops.top(); + } + + Compiler(VM* vm, const char* source, _Str filename, CompileMode mode){ + this->vm = vm; + this->parser = pkpy::make_unique( + pkpy::make_shared(source, filename, mode) + ); + +// http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ +#define METHOD(name) &Compiler::name +#define NO_INFIX nullptr, PREC_NONE + for(_TokenType i=0; i<__TOKENS_LEN; i++) rules[i] = { nullptr, NO_INFIX }; + rules[TK(".")] = { nullptr, METHOD(exprAttrib), PREC_ATTRIB }; + rules[TK("->")] = { nullptr, METHOD(exprAttribPtr), PREC_ATTRIB }; + rules[TK("(")] = { METHOD(exprGrouping), METHOD(exprCall), PREC_CALL }; + rules[TK("[")] = { METHOD(exprList), METHOD(exprSubscript), PREC_SUBSCRIPT }; + rules[TK("{")] = { METHOD(exprMap), NO_INFIX }; + rules[TK("%")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR }; + rules[TK("+")] = { nullptr, METHOD(exprBinaryOp), PREC_TERM }; + rules[TK("-")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_TERM }; + rules[TK("*")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_FACTOR }; + rules[TK("/")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR }; + rules[TK("//")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR }; + rules[TK("**")] = { nullptr, METHOD(exprBinaryOp), PREC_EXPONENT }; + rules[TK(">")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION }; + rules[TK("<")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION }; + rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_EQUALITY }; + rules[TK("!=")] = { nullptr, METHOD(exprBinaryOp), PREC_EQUALITY }; + rules[TK(">=")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION }; + rules[TK("<=")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION }; + rules[TK("in")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; + rules[TK("is")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; + rules[TK("not in")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; + rules[TK("is not")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; + rules[TK("and") ] = { nullptr, METHOD(exprAnd), PREC_LOGICAL_AND }; + rules[TK("or")] = { nullptr, METHOD(exprOr), PREC_LOGICAL_OR }; + rules[TK("not")] = { METHOD(exprUnaryOp), nullptr, PREC_UNARY }; + rules[TK("True")] = { METHOD(exprValue), NO_INFIX }; + rules[TK("False")] = { METHOD(exprValue), NO_INFIX }; + rules[TK("lambda")] = { METHOD(exprLambda), NO_INFIX }; + rules[TK("None")] = { METHOD(exprValue), NO_INFIX }; + rules[TK("...")] = { METHOD(exprValue), NO_INFIX }; + rules[TK("@id")] = { METHOD(exprName), NO_INFIX }; + rules[TK("@num")] = { METHOD(exprLiteral), NO_INFIX }; + rules[TK("@str")] = { METHOD(exprLiteral), NO_INFIX }; + rules[TK("@fstr")] = { METHOD(exprFString), NO_INFIX }; + rules[TK("?")] = { nullptr, METHOD(exprTernary), PREC_TERNARY }; + rules[TK("=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("+=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("-=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("*=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("/=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("//=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK(",")] = { nullptr, METHOD(exprComma), PREC_COMMA }; + rules[TK("<<")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_SHIFT }; + rules[TK(">>")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_SHIFT }; + rules[TK("&")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_BITWISE_AND }; + rules[TK("|")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_OR }; + rules[TK("^")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_XOR }; +#undef METHOD +#undef NO_INFIX + +#define EXPR() parsePrecedence(PREC_TERNARY) // no '=' and ',' just a simple expression +#define EXPR_TUPLE() parsePrecedence(PREC_COMMA) // no '=', but ',' is allowed +#define EXPR_ANY() parsePrecedence(PREC_ASSIGNMENT) + } + + _Str eatStringUntil(char quote) { + std::vector buff; + while (true) { + char c = parser->eatCharIncludeNewLine(); + if (c == quote) break; + if (c == '\0' || c == '\n') syntaxError("EOL while scanning string literal"); + if (c == '\\') { + switch (parser->eatCharIncludeNewLine()) { + case '"': buff.push_back('"'); break; + case '\'': buff.push_back('\''); break; + case '\\': buff.push_back('\\'); break; + case 'n': buff.push_back('\n'); break; + case 'r': buff.push_back('\r'); break; + case 't': buff.push_back('\t'); break; + case '\n': case '\r': break; + default: syntaxError("invalid escape character"); + } + } else { + buff.push_back(c); + } + } + return _Str(buff.data(), buff.size()); + } + + void eatString(char quote, bool fstr) { + _Str s = eatStringUntil(quote); + if(fstr){ + parser->setNextToken(TK("@fstr"), vm->PyStr(s)); + }else{ + parser->setNextToken(TK("@str"), vm->PyStr(s)); + } + } + + void eatNumber() { + static const std::regex pattern("^(0x)?[0-9a-f]+(\\.[0-9]+)?"); + std::smatch m; + + const char* i = parser->token_start; + while(*i != '\n' && *i != '\0') i++; + std::string s = std::string(parser->token_start, i); + + try{ + if (std::regex_search(s, m, pattern)) { + // here is m.length()-1, since the first char is eaten by lexToken() + for(int j=0; jeatChar(); + + int base = 10; + size_t size; + if (m[1].matched) base = 16; + if (m[2].matched) { + if(base == 16) syntaxError("hex literal should not contain a dot"); + parser->setNextToken(TK("@num"), vm->PyFloat(std::stod(m[0], &size))); + } else { + parser->setNextToken(TK("@num"), vm->PyInt(std::stoll(m[0], &size, base))); + } + if (size != m.length()) throw std::runtime_error("length mismatch"); + } + }catch(std::exception& _){ + syntaxError("invalid number literal"); + } + } + + // Lex the next token and set it as the next token. + void lexToken() { + parser->previous = parser->current; + parser->current = parser->nextToken(); + + //_Str _info = parser->current.info(); printf("%s\n", (const char*)_info); + + while (parser->peekChar() != '\0') { + parser->token_start = parser->current_char; + char c = parser->eatCharIncludeNewLine(); + switch (c) { + case '\'': case '"': eatString(c, false); return; + case '#': parser->skipLineComment(); break; + case '{': parser->setNextToken(TK("{")); return; + case '}': parser->setNextToken(TK("}")); return; + case ',': parser->setNextToken(TK(",")); return; + case ':': parser->setNextToken(TK(":")); return; + case ';': parser->setNextToken(TK(";")); return; + case '(': parser->setNextToken(TK("(")); return; + case ')': parser->setNextToken(TK(")")); return; + case '[': parser->setNextToken(TK("[")); return; + case ']': parser->setNextToken(TK("]")); return; + case '%': parser->setNextToken(TK("%")); return; + case '&': parser->setNextToken(TK("&")); return; + case '|': parser->setNextToken(TK("|")); return; + case '^': parser->setNextToken(TK("^")); return; + case '?': parser->setNextToken(TK("?")); return; + case '.': { + if(parser->matchChar('.')) { + if(parser->matchChar('.')) { + parser->setNextToken(TK("...")); + } else { + syntaxError("invalid token '..'"); + } + } else { + parser->setNextToken(TK(".")); + } + return; + } + case '=': parser->setNextTwoCharToken('=', TK("="), TK("==")); return; + case '+': parser->setNextTwoCharToken('=', TK("+"), TK("+=")); return; + case '>': { + if(parser->matchChar('=')) parser->setNextToken(TK(">=")); + else if(parser->matchChar('>')) parser->setNextToken(TK(">>")); + else parser->setNextToken(TK(">")); + return; + } + case '<': { + if(parser->matchChar('=')) parser->setNextToken(TK("<=")); + else if(parser->matchChar('<')) parser->setNextToken(TK("<<")); + else parser->setNextToken(TK("<")); + return; + } + case '-': { + if(parser->matchChar('=')) parser->setNextToken(TK("-=")); + else if(parser->matchChar('>')) parser->setNextToken(TK("->")); + else parser->setNextToken(TK("-")); + return; + } + case '!': + if(parser->matchChar('=')) parser->setNextToken(TK("!=")); + else syntaxError("expected '=' after '!'"); + break; + case '*': + if (parser->matchChar('*')) { + parser->setNextToken(TK("**")); // '**' + } else { + parser->setNextTwoCharToken('=', TK("*"), TK("*=")); + } + return; + case '/': + if(parser->matchChar('/')) { + parser->setNextTwoCharToken('=', TK("//"), TK("//=")); + } else { + parser->setNextTwoCharToken('=', TK("/"), TK("/=")); + } + return; + case '\r': break; // just ignore '\r' + case ' ': case '\t': parser->eatSpaces(); break; + case '\n': { + parser->setNextToken(TK("@eol")); while(parser->matchChar('\n')); + if(!parser->eatIndentation()) indentationError("unindent does not match any outer indentation level"); + return; + } + default: { + if(c == 'f'){ + if(parser->matchChar('\'')) {eatString('\'', true); return;} + if(parser->matchChar('"')) {eatString('"', true); return;} + } + if (isdigit(c)) { + eatNumber(); + } else if (parser->isNameStart(c)) { + int ret = parser->eatName(); + if(ret!=0) syntaxError("identifier is illegal, err " + std::to_string(ret)); + } else { + syntaxError("unknown character: " + std::string(1, c)); + } + return; + } + } + } + + parser->token_start = parser->current_char; + parser->setNextToken(TK("@eof")); + } + + _TokenType peek() { + return parser->current.type; + } + + bool match(_TokenType expected) { + if (peek() != expected) return false; + lexToken(); + return true; + } + + void consume(_TokenType expected) { + if (!match(expected)){ + _StrStream ss; + ss << "expected '" << TK_STR(expected) << "', but got '" << TK_STR(peek()) << "'"; + syntaxError(ss.str()); + } + } + + bool matchNewLines(bool repl_throw=false) { + bool consumed = false; + if (peek() == TK("@eol")) { + while (peek() == TK("@eol")) lexToken(); + consumed = true; + } + if (repl_throw && peek() == TK("@eof")){ + throw NeedMoreLines(isCompilingClass); + } + return consumed; + } + + bool matchEndStatement() { + if (match(TK(";"))) { + matchNewLines(); + return true; + } + if (matchNewLines() || peek() == TK("@eof")) + return true; + if (peek() == TK("@dedent")) return true; + return false; + } + + void consumeEndStatement() { + if (!matchEndStatement()) syntaxError("expected statement end"); + } + + void exprLiteral() { + PyVar value = parser->previous.value; + int index = getCode()->addConst(value); + emitCode(OP_LOAD_CONST, index); + } + + void exprFString() { + static const std::regex pattern(R"(\{(.*?)\})"); + PyVar value = parser->previous.value; + std::string s = vm->PyStr_AS_C(value).str(); + std::sregex_iterator begin(s.begin(), s.end(), pattern); + std::sregex_iterator end; + int size = 0; + int i = 0; + for(auto it = begin; it != end; it++) { + std::smatch m = *it; + if (i < m.position()) { + std::string literal = s.substr(i, m.position() - i); + emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(literal))); + size++; + } + emitCode(OP_LOAD_EVAL_FN); + emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(m[1].str()))); + emitCode(OP_CALL, 1); + size++; + i = (int)(m.position() + m.length()); + } + if (i < s.size()) { + std::string literal = s.substr(i, s.size() - i); + emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(literal))); + size++; + } + emitCode(OP_BUILD_STRING, size); + } + + void exprLambda() { + _Func func = pkpy::make_shared(); + func->name = ""; + if(!match(TK(":"))){ + __compileFunctionArgs(func); + consume(TK(":")); + } + func->code = pkpy::make_shared(parser->src, func->name); + this->codes.push(func->code); + EXPR_TUPLE(); + emitCode(OP_RETURN_VALUE); + this->codes.pop(); + emitCode(OP_LOAD_LAMBDA, getCode()->addConst(vm->PyFunction(func))); + } + + void exprAssign() { + _TokenType op = parser->previous.type; + if(op == TK("=")) { // a = (expr) + EXPR_TUPLE(); + emitCode(OP_STORE_PTR); + }else{ // a += (expr) -> a = a + (expr) + // TODO: optimization is needed for inplace operators + emitCode(OP_DUP_TOP); + EXPR(); + switch (op) { + case TK("+="): emitCode(OP_BINARY_OP, 0); break; + case TK("-="): emitCode(OP_BINARY_OP, 1); break; + case TK("*="): emitCode(OP_BINARY_OP, 2); break; + case TK("/="): emitCode(OP_BINARY_OP, 3); break; + case TK("//="): emitCode(OP_BINARY_OP, 4); break; + default: UNREACHABLE(); + } + emitCode(OP_STORE_PTR); + } + } + + void exprComma() { + int size = 1; // an expr is in the stack now + do { + EXPR(); // NOTE: "1," will fail, "1,2" will be ok + size++; + } while(match(TK(","))); + emitCode(OP_BUILD_SMART_TUPLE, size); + } + + void exprOr() { + int patch = emitCode(OP_JUMP_IF_TRUE_OR_POP); + parsePrecedence(PREC_LOGICAL_OR); + patchJump(patch); + } + + void exprAnd() { + int patch = emitCode(OP_JUMP_IF_FALSE_OR_POP); + parsePrecedence(PREC_LOGICAL_AND); + patchJump(patch); + } + + void exprTernary() { + int patch = emitCode(OP_POP_JUMP_IF_FALSE); + EXPR(); // if true + int patch2 = emitCode(OP_JUMP_ABSOLUTE); + consume(TK(":")); + patchJump(patch); + EXPR(); // if false + patchJump(patch2); + } + + void exprBinaryOp() { + _TokenType op = parser->previous.type; + parsePrecedence((Precedence)(rules[op].precedence + 1)); + + switch (op) { + case TK("+"): emitCode(OP_BINARY_OP, 0); break; + case TK("-"): emitCode(OP_BINARY_OP, 1); break; + case TK("*"): emitCode(OP_BINARY_OP, 2); break; + case TK("/"): emitCode(OP_BINARY_OP, 3); break; + case TK("//"): emitCode(OP_BINARY_OP, 4); break; + case TK("%"): emitCode(OP_BINARY_OP, 5); break; + case TK("**"): emitCode(OP_BINARY_OP, 6); break; + + case TK("<"): emitCode(OP_COMPARE_OP, 0); break; + case TK("<="): emitCode(OP_COMPARE_OP, 1); break; + case TK("=="): emitCode(OP_COMPARE_OP, 2); break; + case TK("!="): emitCode(OP_COMPARE_OP, 3); break; + case TK(">"): emitCode(OP_COMPARE_OP, 4); break; + case TK(">="): emitCode(OP_COMPARE_OP, 5); break; + case TK("in"): emitCode(OP_CONTAINS_OP, 0); break; + case TK("not in"): emitCode(OP_CONTAINS_OP, 1); break; + case TK("is"): emitCode(OP_IS_OP, 0); break; + case TK("is not"): emitCode(OP_IS_OP, 1); break; + + case TK("<<"): emitCode(OP_BITWISE_OP, 0); break; + case TK(">>"): emitCode(OP_BITWISE_OP, 1); break; + case TK("&"): emitCode(OP_BITWISE_OP, 2); break; + case TK("|"): emitCode(OP_BITWISE_OP, 3); break; + case TK("^"): emitCode(OP_BITWISE_OP, 4); break; + default: UNREACHABLE(); + } + } + + void exprUnaryOp() { + _TokenType op = parser->previous.type; + matchNewLines(); + parsePrecedence((Precedence)(PREC_UNARY + 1)); + + switch (op) { + case TK("-"): emitCode(OP_UNARY_NEGATIVE); break; + case TK("not"): emitCode(OP_UNARY_NOT); break; + case TK("&"): emitCode(OP_UNARY_REF); break; + case TK("*"): emitCode(OP_UNARY_DEREF); break; + default: UNREACHABLE(); + } + } + + void exprGrouping() { + matchNewLines(mode()==SINGLE_MODE); + EXPR_TUPLE(); + matchNewLines(mode()==SINGLE_MODE); + consume(TK(")")); + } + + void exprList() { + int _patch = emitCode(OP_NO_OP); + int _body_start = getCode()->co_code.size(); + int ARGC = 0; + do { + matchNewLines(mode()==SINGLE_MODE); + if (peek() == TK("]")) break; + EXPR(); ARGC++; + matchNewLines(mode()==SINGLE_MODE); + if(ARGC == 1 && match(TK("for"))) goto __LISTCOMP; + } while (match(TK(","))); + matchNewLines(mode()==SINGLE_MODE); + consume(TK("]")); + emitCode(OP_BUILD_LIST, ARGC); + return; + +__LISTCOMP: + int _body_end = getCode()->co_code.size(); + getCode()->co_code[_patch].op = OP_JUMP_ABSOLUTE; + getCode()->co_code[_patch].arg = _body_end; + emitCode(OP_BUILD_LIST, 0); + EXPR_FOR_VARS();consume(TK("in"));EXPR_TUPLE(); + matchNewLines(mode()==SINGLE_MODE); + + int _skipPatch = emitCode(OP_JUMP_ABSOLUTE); + int _cond_start = getCode()->co_code.size(); + if(match(TK("if"))) EXPR_TUPLE(); + int _cond_end = getCode()->co_code.size(); + patchJump(_skipPatch); + + emitCode(OP_GET_ITER); + Loop& loop = enterLoop(); + int patch = emitCode(OP_FOR_ITER); + + if(_cond_end != _cond_start) { // there is an if condition + getCode()->__moveToEnd(_cond_start, _cond_end); + int ifpatch = emitCode(OP_POP_JUMP_IF_FALSE); + getCode()->__moveToEnd(_body_start, _body_end); + emitCode(OP_LIST_APPEND); + patchJump(ifpatch); + }else{ + getCode()->__moveToEnd(_body_start, _body_end); + emitCode(OP_LIST_APPEND); + } + + emitCode(OP_JUMP_ABSOLUTE, loop.start); keepOpcodeLine(); + patchJump(patch); + exitLoop(); + matchNewLines(mode()==SINGLE_MODE); + consume(TK("]")); + } + + void exprMap() { + int size = 0; + do { + matchNewLines(mode()==SINGLE_MODE); + if (peek() == TK("}")) break; + EXPR();consume(TK(":"));EXPR(); + size++; + matchNewLines(mode()==SINGLE_MODE); + } while (match(TK(","))); + matchNewLines(); + consume(TK("}")); + emitCode(OP_BUILD_MAP, size); + } + + void exprCall() { + int ARGC = 0; + do { + matchNewLines(mode()==SINGLE_MODE); + if (peek() == TK(")")) break; + EXPR(); + ARGC++; + matchNewLines(mode()==SINGLE_MODE); + } while (match(TK(","))); + consume(TK(")")); + emitCode(OP_CALL, ARGC); + } + + void exprName() { + Token tkname = parser->previous; + int index = getCode()->addName( + tkname.str(), + codes.size()>1 ? NAME_LOCAL : NAME_GLOBAL + ); + emitCode(OP_LOAD_NAME_PTR, index); + } + + void exprAttrib() { + consume(TK("@id")); + const _Str& name = parser->previous.str(); + int index = getCode()->addName(name, NAME_ATTR); + emitCode(OP_BUILD_ATTR_PTR, index); + } + + void exprAttribPtr(){ + consume(TK("@id")); + const _Str& name = parser->previous.str(); + int index = getCode()->addName(name, NAME_ATTR); + emitCode(OP_BUILD_ATTR_PTR_PTR, index); + } + + // [:], [:b] + // [a], [a:], [a:b] + void exprSubscript() { + if(match(TK(":"))){ + emitCode(OP_LOAD_NONE); + if(match(TK("]"))){ + emitCode(OP_LOAD_NONE); + }else{ + EXPR(); + consume(TK("]")); + } + emitCode(OP_BUILD_SLICE); + }else{ + EXPR(); + if(match(TK(":"))){ + if(match(TK("]"))){ + emitCode(OP_LOAD_NONE); + }else{ + EXPR(); + consume(TK("]")); + } + emitCode(OP_BUILD_SLICE); + }else{ + consume(TK("]")); + } + } + + emitCode(OP_BUILD_INDEX_PTR); + } + + void exprValue() { + _TokenType op = parser->previous.type; + switch (op) { + case TK("None"): emitCode(OP_LOAD_NONE); break; + case TK("True"): emitCode(OP_LOAD_TRUE); break; + case TK("False"): emitCode(OP_LOAD_FALSE); break; + case TK("..."): emitCode(OP_LOAD_ELLIPSIS); break; + default: UNREACHABLE(); + } + } + + void keepOpcodeLine(){ + int i = getCode()->co_code.size() - 1; + getCode()->co_code[i].line = getCode()->co_code[i-1].line; + } + + int emitCode(Opcode opcode, int arg=-1) { + int line = parser->previous.line; + getCode()->co_code.push_back( + ByteCode{(uint8_t)opcode, arg, (uint16_t)line} + ); + return getCode()->co_code.size() - 1; + } + + inline void patchJump(int addr_index) { + int target = getCode()->co_code.size(); + getCode()->co_code[addr_index].arg = target; + } + + void compileBlockBody(){ + __compileBlockBody(&Compiler::compileStatement); + } + + void __compileBlockBody(CompilerAction action) { + consume(TK(":")); + if(!matchNewLines(mode()==SINGLE_MODE)){ + syntaxError("expected a new line after ':'"); + } + consume(TK("@indent")); + while (peek() != TK("@dedent")) { + matchNewLines(); + (this->*action)(); + matchNewLines(); + } + consume(TK("@dedent")); + } + + Token compileImportPath() { + consume(TK("@id")); + Token tkmodule = parser->previous; + int index = getCode()->addName(tkmodule.str(), NAME_GLOBAL); + emitCode(OP_IMPORT_NAME, index); + return tkmodule; + } + + // import a as b + void compileRegularImport() { + do { + Token tkmodule = compileImportPath(); + if (match(TK("as"))) { + consume(TK("@id")); + tkmodule = parser->previous; + } + int index = getCode()->addName(tkmodule.str(), NAME_GLOBAL); + emitCode(OP_STORE_NAME_PTR, index); + } while (match(TK(","))); + consumeEndStatement(); + } + + // from a import b as c, d as e + void compileFromImport() { + Token tkmodule = compileImportPath(); + consume(TK("import")); + do { + emitCode(OP_DUP_TOP); + consume(TK("@id")); + Token tkname = parser->previous; + int index = getCode()->addName(tkname.str(), NAME_GLOBAL); + emitCode(OP_BUILD_ATTR_PTR, index); + if (match(TK("as"))) { + consume(TK("@id")); + tkname = parser->previous; + } + index = getCode()->addName(tkname.str(), NAME_GLOBAL); + emitCode(OP_STORE_NAME_PTR, index); + } while (match(TK(","))); + emitCode(OP_POP_TOP); + consumeEndStatement(); + } + + void parsePrecedence(Precedence precedence) { + lexToken(); + GrammarFn prefix = rules[parser->previous.type].prefix; + if (prefix == nullptr) syntaxError(_Str("expected an expression, but got ") + TK_STR(parser->previous.type)); + (this->*prefix)(); + while (rules[peek()].precedence >= precedence) { + lexToken(); + _TokenType op = parser->previous.type; + GrammarFn infix = rules[op].infix; + if(infix == nullptr) throw std::runtime_error("(infix == nullptr) is true"); + (this->*infix)(); + } + } + + void compileIfStatement() { + matchNewLines(); + EXPR_TUPLE(); + + int ifpatch = emitCode(OP_POP_JUMP_IF_FALSE); + compileBlockBody(); + + if (match(TK("elif"))) { + int exit_jump = emitCode(OP_JUMP_ABSOLUTE); + patchJump(ifpatch); + compileIfStatement(); + patchJump(exit_jump); + } else if (match(TK("else"))) { + int exit_jump = emitCode(OP_JUMP_ABSOLUTE); + patchJump(ifpatch); + compileBlockBody(); + patchJump(exit_jump); + } else { + patchJump(ifpatch); + } + } + + Loop& enterLoop(){ + Loop lp((int)getCode()->co_code.size()); + loops.push(lp); + return loops.top(); + } + + void exitLoop(){ + Loop& lp = loops.top(); + for(int addr : lp.breaks) patchJump(addr); + loops.pop(); + } + + void compileWhileLoop() { + Loop& loop = enterLoop(); + EXPR_TUPLE(); + int patch = emitCode(OP_POP_JUMP_IF_FALSE); + compileBlockBody(); + emitCode(OP_JUMP_ABSOLUTE, loop.start); keepOpcodeLine(); + patchJump(patch); + exitLoop(); + } + + void EXPR_FOR_VARS(){ + int size = 0; + do { + consume(TK("@id")); + exprName(); size++; + } while (match(TK(","))); + if(size > 1) emitCode(OP_BUILD_SMART_TUPLE, size); + } + + void compileForLoop() { + EXPR_FOR_VARS();consume(TK("in"));EXPR_TUPLE(); + emitCode(OP_GET_ITER); + Loop& loop = enterLoop(); + int patch = emitCode(OP_FOR_ITER); + compileBlockBody(); + emitCode(OP_JUMP_ABSOLUTE, loop.start); keepOpcodeLine(); + patchJump(patch); + exitLoop(); + } + + void compileStatement() { + if (match(TK("break"))) { + if (loops.empty()) syntaxError("'break' outside loop"); + consumeEndStatement(); + int patch = emitCode(OP_SAFE_JUMP_ABSOLUTE); + getLoop().breaks.push_back(patch); + } else if (match(TK("continue"))) { + if (loops.empty()) syntaxError("'continue' not properly in loop"); + consumeEndStatement(); + emitCode(OP_JUMP_ABSOLUTE, getLoop().start); + } else if (match(TK("return"))) { + if (codes.size() == 1) + syntaxError("'return' outside function"); + if(matchEndStatement()){ + emitCode(OP_LOAD_NONE); + }else{ + EXPR_TUPLE(); + consumeEndStatement(); + } + emitCode(OP_RETURN_VALUE); + } else if (match(TK("if"))) { + compileIfStatement(); + } else if (match(TK("while"))) { + compileWhileLoop(); + } else if (match(TK("for"))) { + compileForLoop(); + } else if(match(TK("assert"))){ + EXPR(); + emitCode(OP_ASSERT); + consumeEndStatement(); + } else if(match(TK("with"))){ + EXPR(); + consume(TK("as")); + consume(TK("@id")); + Token tkname = parser->previous; + int index = getCode()->addName( + tkname.str(), + codes.size()>1 ? NAME_LOCAL : NAME_GLOBAL + ); + emitCode(OP_STORE_NAME_PTR, index); + emitCode(OP_LOAD_NAME_PTR, index); + emitCode(OP_WITH_ENTER); + compileBlockBody(); + emitCode(OP_LOAD_NAME_PTR, index); + emitCode(OP_WITH_EXIT); + } else if(match(TK("label"))){ + if(mode() != EXEC_MODE) syntaxError("'label' is only available in EXEC_MODE"); + consume(TK(".")); consume(TK("@id")); + getCode()->addLabel(parser->previous.str()); + consumeEndStatement(); + } else if(match(TK("goto"))){ + // https://entrian.com/goto/ + if(mode() != EXEC_MODE) syntaxError("'goto' is only available in EXEC_MODE"); + consume(TK(".")); consume(TK("@id")); + emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(parser->previous.str()))); + emitCode(OP_GOTO); + consumeEndStatement(); + } else if(match(TK("raise"))){ + consume(TK("@id")); // dummy exception type + emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(parser->previous.str()))); + consume(TK("("));EXPR();consume(TK(")")); + emitCode(OP_RAISE_ERROR); + consumeEndStatement(); + } else if(match(TK("del"))){ + EXPR(); + emitCode(OP_DELETE_PTR); + consumeEndStatement(); + } else if(match(TK("global"))){ + consume(TK("@id")); + getCode()->co_global_names.push_back(parser->previous.str()); + consumeEndStatement(); + } else if(match(TK("pass"))){ + consumeEndStatement(); + } else { + EXPR_ANY(); + consumeEndStatement(); + + // If last op is not an assignment, pop the result. + uint8_t lastOp = getCode()->co_code.back().op; + if( lastOp != OP_STORE_NAME_PTR && lastOp != OP_STORE_PTR){ + if(mode()==SINGLE_MODE && parser->indents.top() == 0){ + emitCode(OP_PRINT_EXPR); + } + emitCode(OP_POP_TOP); + } + } + } + + void compileClass(){ + consume(TK("@id")); + int clsNameIdx = getCode()->addName(parser->previous.str(), NAME_GLOBAL); + int superClsNameIdx = -1; + if(match(TK("("))){ + consume(TK("@id")); + superClsNameIdx = getCode()->addName(parser->previous.str(), NAME_GLOBAL); + consume(TK(")")); + } + emitCode(OP_LOAD_NONE); + isCompilingClass = true; + __compileBlockBody(&Compiler::compileFunction); + isCompilingClass = false; + + if(superClsNameIdx == -1) emitCode(OP_LOAD_NONE); + else emitCode(OP_LOAD_NAME_PTR, superClsNameIdx); + emitCode(OP_BUILD_CLASS, clsNameIdx); + } + + void __compileFunctionArgs(_Func func){ + int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs + do { + if(state == 3) syntaxError("**kwargs should be the last argument"); + matchNewLines(); + if(match(TK("*"))){ + if(state < 1) state = 1; + else syntaxError("*args should be placed before **kwargs"); + } + else if(match(TK("**"))){ + state = 3; + } + + consume(TK("@id")); + const _Str& name = parser->previous.str(); + if(func->hasName(name)) syntaxError("duplicate argument name"); + + if(state == 0 && peek() == TK("=")) state = 2; + + switch (state) + { + case 0: func->args.push_back(name); break; + case 1: func->starredArg = name; state+=1; break; + case 2: { + consume(TK("=")); + PyVarOrNull value = readLiteral(); + if(value == nullptr){ + syntaxError(_Str("expect a literal, not ") + TK_STR(parser->current.type)); + } + func->kwArgs[name] = value; + func->kwArgsOrder.push_back(name); + } break; + case 3: syntaxError("**kwargs is not supported yet"); break; + } + } while (match(TK(","))); + } + + void compileFunction(){ + if(isCompilingClass){ + if(match(TK("pass"))) return; + consume(TK("def")); + } + _Func func = pkpy::make_shared(); + consume(TK("@id")); + func->name = parser->previous.str(); + + if (match(TK("(")) && !match(TK(")"))) { + __compileFunctionArgs(func); + consume(TK(")")); + } + + func->code = pkpy::make_shared(parser->src, func->name); + this->codes.push(func->code); + compileBlockBody(); + this->codes.pop(); + emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyFunction(func))); + if(!isCompilingClass) emitCode(OP_STORE_FUNCTION); + } + + PyVarOrNull readLiteral(){ + if(match(TK("-"))){ + consume(TK("@num")); + PyVar val = parser->previous.value; + return vm->numNegated(val); + } + if(match(TK("@num"))) return parser->previous.value; + if(match(TK("@str"))) return parser->previous.value; + if(match(TK("True"))) return vm->PyBool(true); + if(match(TK("False"))) return vm->PyBool(false); + if(match(TK("None"))) return vm->None; + if(match(TK("..."))) return vm->Ellipsis; + return nullptr; + } + + void compileTopLevelStatement() { + if (match(TK("class"))) { + compileClass(); + } else if (match(TK("def"))) { + compileFunction(); + } else if (match(TK("import"))) { + compileRegularImport(); + } else if (match(TK("from"))) { + compileFromImport(); + } else { + compileStatement(); + } + } + + _Code __fillCode(){ + _Code code = pkpy::make_shared(parser->src, _Str("")); + codes.push(code); + + // Lex initial tokens. current <-- next. + lexToken(); + lexToken(); + matchNewLines(); + + if(mode()==EVAL_MODE) { + EXPR_TUPLE(); + consume(TK("@eof")); + return code; + }else if(mode()==JSON_MODE){ + PyVarOrNull value = readLiteral(); + if(value != nullptr) emitCode(OP_LOAD_CONST, code->addConst(value)); + else if(match(TK("{"))) exprMap(); + else if(match(TK("["))) exprList(); + else syntaxError("expect a JSON object or array"); + consume(TK("@eof")); + return code; + } + + while (!match(TK("@eof"))) { + compileTopLevelStatement(); + matchNewLines(); + } + return code; + } + + /***** Error Reporter *****/ + _Str getLineSnapshot(){ + int lineno = parser->current_line; + if(parser->peekChar() == '\n') lineno--; + return parser->src->snapshot(lineno); + } + + void syntaxError(_Str msg){ + throw CompileError("SyntaxError", msg, getLineSnapshot()); + } + + void indentationError(_Str msg){ + throw CompileError("IndentationError", msg, getLineSnapshot()); + } + + void unexpectedError(_Str msg){ + throw CompileError("UnexpectedError", msg, getLineSnapshot()); + } +}; + +_Code compile(VM* vm, const char* source, _Str filename, CompileMode mode=EXEC_MODE, bool noThrow=true) { + Compiler compiler(vm, source, filename, mode); + if(!noThrow) return compiler.__fillCode(); + try{ + return compiler.__fillCode(); + }catch(std::exception& e){ + if(const _Error* _ = dynamic_cast(&e)){ + (*vm->_stderr) << e.what() << '\n'; + }else{ + auto ce = CompileError("UnexpectedError", e.what(), compiler.getLineSnapshot()); + (*vm->_stderr) << ce.what() << '\n'; + } + return nullptr; + } +} + + +enum InputResult { + NEED_MORE_LINES = 0, + EXEC_DONE = 1, + EXEC_SKIPPED = 2, +}; + +class REPL { +protected: + int need_more_lines = 0; + std::string buffer; + VM* vm; + bool exited = false; + + void _exit(){ + exited = true; + exit(0); + } +public: + REPL(VM* vm) : vm(vm){ + (*vm->_stdout) << ("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ")\n"); + (*vm->_stdout) << ("https://github.com/blueloveTH/pocketpy" "\n"); + (*vm->_stdout) << ("Type \"exit()\" to exit." "\n"); + } + + bool is_need_more_lines() const { + return need_more_lines; + } + + InputResult input(std::string line){ + if(exited) return EXEC_SKIPPED; + if(need_more_lines){ + buffer += line; + buffer += '\n'; + int n = buffer.size(); + if(n>=need_more_lines){ + for(int i=buffer.size()-need_more_lines; i", SINGLE_MODE); + if(code == nullptr) return EXEC_SKIPPED; + vm->execAsync(code); + return EXEC_DONE; + }catch(NeedMoreLines& ne){ + buffer += line; + buffer += '\n'; + need_more_lines = ne.isClassDef ? 3 : 2; + return NEED_MORE_LINES; + } + } +}; + + +#define BIND_NUM_ARITH_OPT(name, op) \ + _vm->bindMethodMulti({"int","float"}, #name, [](VM* vm, const pkpy::ArgList& args){ \ + if(!vm->isIntOrFloat(args[0], args[1])) \ + vm->typeError("unsupported operand type(s) for " #op ); \ + if(args[0]->isType(vm->_tp_int) && args[1]->isType(vm->_tp_int)){ \ + return vm->PyInt(vm->PyInt_AS_C(args[0]) op vm->PyInt_AS_C(args[1])); \ + }else{ \ + return vm->PyFloat(vm->numToFloat(args[0]) op vm->numToFloat(args[1])); \ + } \ + }); + +#define BIND_NUM_LOGICAL_OPT(name, op, is_eq) \ + _vm->bindMethodMulti({"int","float"}, #name, [](VM* vm, const pkpy::ArgList& args){ \ + if(!vm->isIntOrFloat(args[0], args[1])){ \ + if constexpr(is_eq) return vm->PyBool(args[0] == args[1]); \ + vm->typeError("unsupported operand type(s) for " #op ); \ + } \ + return vm->PyBool(vm->numToFloat(args[0]) op vm->numToFloat(args[1])); \ + }); + + +void __initializeBuiltinFunctions(VM* _vm) { + BIND_NUM_ARITH_OPT(__add__, +) + BIND_NUM_ARITH_OPT(__sub__, -) + BIND_NUM_ARITH_OPT(__mul__, *) + + BIND_NUM_LOGICAL_OPT(__lt__, <, false) + BIND_NUM_LOGICAL_OPT(__le__, <=, false) + BIND_NUM_LOGICAL_OPT(__gt__, >, false) + BIND_NUM_LOGICAL_OPT(__ge__, >=, false) + BIND_NUM_LOGICAL_OPT(__eq__, ==, true) + +#undef BIND_NUM_ARITH_OPT +#undef BIND_NUM_LOGICAL_OPT + + _vm->bindBuiltinFunc("print", [](VM* vm, const pkpy::ArgList& args) { + _StrStream ss; + for(int i=0; iPyStr_AS_C(vm->asStr(args[i])) << " "; + } + (*vm->_stdout) << ss.str() << '\n'; + return vm->None; + }); + + _vm->bindBuiltinFunc("super", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 0); + auto it = vm->topFrame()->f_locals.find("self"_c); + if(it == vm->topFrame()->f_locals.end()) vm->typeError("super() can only be called in a class method"); + return vm->newObject(vm->_tp_super, it->second); + }); + + _vm->bindBuiltinFunc("eval", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + const _Str& expr = vm->PyStr_AS_C(args[0]); + _Code code = compile(vm, expr.c_str(), "", EVAL_MODE, false); + return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals); + }); + + _vm->bindBuiltinFunc("isinstance", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 2); + return vm->PyBool(vm->isInstance(args[0], args[1])); + }); + + _vm->bindBuiltinFunc("repr", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->asRepr(args[0]); + }); + + _vm->bindBuiltinFunc("hash", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyInt(vm->hash(args[0])); + }); + + _vm->bindBuiltinFunc("chr", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + _Int i = vm->PyInt_AS_C(args[0]); + if (i < 0 || i > 128) vm->valueError("chr() arg not in range(128)"); + return vm->PyStr(std::string(1, (char)i)); + }); + + _vm->bindBuiltinFunc("ord", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + _Str s = vm->PyStr_AS_C(args[0]); + if (s.size() != 1) vm->typeError("ord() expected an ASCII character"); + return vm->PyInt((_Int)s[0]); + }); + + _vm->bindBuiltinFunc("globals", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 0); + const auto& d = vm->topFrame()->f_globals(); + PyVar obj = vm->call(vm->builtins->attribs["dict"], {}); + for (const auto& [k, v] : d) { + vm->call(obj, __setitem__, {vm->PyStr(k), v}); + } + return obj; + }); + + _vm->bindBuiltinFunc("locals", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 0); + const auto& d = vm->topFrame()->f_locals; + PyVar obj = vm->call(vm->builtins->attribs["dict"], {}); + for (const auto& [k, v] : d) { + vm->call(obj, __setitem__, {vm->PyStr(k), v}); + } + return obj; + }); + + _vm->bindBuiltinFunc("dir", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + std::vector<_Str> names; + for (auto& [k, _] : args[0]->attribs) names.push_back(k); + for (auto& [k, _] : args[0]->_type->attribs) { + if (k.str().find("__") == 0) continue; + if (std::find(names.begin(), names.end(), k) == names.end()) names.push_back(k); + } + PyVarList ret; + for (const auto& name : names) ret.push_back(vm->PyStr(name)); + return vm->PyList(ret); + }); + + _vm->bindMethod("object", "__repr__", [](VM* vm, const pkpy::ArgList& args) { + PyVar _self = args[0]; + _Str s = "<" + _self->getTypeName() + " object at " + std::to_string((uintptr_t)_self.get()) + ">"; + return vm->PyStr(s); + }); + + _vm->bindMethod("type", "__new__", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return args[0]->_type; + }); + + _vm->bindMethod("range", "__new__", [](VM* vm, const pkpy::ArgList& args) { + _Range r; + switch (args.size()) { + case 1: r.stop = vm->PyInt_AS_C(args[0]); break; + case 2: r.start = vm->PyInt_AS_C(args[0]); r.stop = vm->PyInt_AS_C(args[1]); break; + case 3: r.start = vm->PyInt_AS_C(args[0]); r.stop = vm->PyInt_AS_C(args[1]); r.step = vm->PyInt_AS_C(args[2]); break; + default: vm->typeError("expected 1-3 arguments, but got " + std::to_string(args.size())); + } + return vm->PyRange(r); + }); + + _vm->bindMethod("range", "__iter__", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkType(args[0], vm->_tp_range); + _Iterator* iter = new RangeIterator(vm, args[0]); + return vm->PyIter(pkpy::shared_ptr<_Iterator>(iter)); + }); + + _vm->bindMethod("NoneType", "__repr__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyStr("None"); + }); + + _vm->bindMethod("NoneType", "__json__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyStr("null"); + }); + + _vm->bindMethod("NoneType", "__eq__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyBool(args[0] == args[1]); + }); + + _vm->bindMethodMulti({"int", "float"}, "__truediv__", [](VM* vm, const pkpy::ArgList& args) { + if(!vm->isIntOrFloat(args[0], args[1])) + vm->typeError("unsupported operand type(s) for " "/" ); + _Float rhs = vm->numToFloat(args[1]); + if (rhs == 0) vm->zeroDivisionError(); + return vm->PyFloat(vm->numToFloat(args[0]) / rhs); + }); + + _vm->bindMethodMulti({"int", "float"}, "__pow__", [](VM* vm, const pkpy::ArgList& args) { + if(!vm->isIntOrFloat(args[0], args[1])) + vm->typeError("unsupported operand type(s) for " "**" ); + if(args[0]->isType(vm->_tp_int) && args[1]->isType(vm->_tp_int)){ + return vm->PyInt((_Int)round(pow(vm->PyInt_AS_C(args[0]), vm->PyInt_AS_C(args[1])))); + }else{ + return vm->PyFloat((_Float)pow(vm->numToFloat(args[0]), vm->numToFloat(args[1]))); + } + }); + + /************ PyInt ************/ + _vm->bindMethod("int", "__new__", [](VM* vm, const pkpy::ArgList& args) { + if(args.size() == 0) return vm->PyInt(0); + vm->__checkArgSize(args, 1); + if (args[0]->isType(vm->_tp_int)) return args[0]; + if (args[0]->isType(vm->_tp_float)) return vm->PyInt((_Int)vm->PyFloat_AS_C(args[0])); + if (args[0]->isType(vm->_tp_bool)) return vm->PyInt(vm->PyBool_AS_C(args[0]) ? 1 : 0); + if (args[0]->isType(vm->_tp_str)) { + const _Str& s = vm->PyStr_AS_C(args[0]); + try{ + _Int val = std::stoll(s.str()); + return vm->PyInt(val); + }catch(std::invalid_argument&){ + vm->valueError("invalid literal for int(): '" + s + "'"); + } + } + vm->typeError("int() argument must be a int, float, bool or str"); + return vm->None; + }); + + _vm->bindMethod("int", "__floordiv__", [](VM* vm, const pkpy::ArgList& args) { + if(!args[0]->isType(vm->_tp_int) || !args[1]->isType(vm->_tp_int)) + vm->typeError("unsupported operand type(s) for " "//" ); + _Int rhs = vm->PyInt_AS_C(args[1]); + if(rhs == 0) vm->zeroDivisionError(); + return vm->PyInt(vm->PyInt_AS_C(args[0]) / rhs); + }); + + _vm->bindMethod("int", "__mod__", [](VM* vm, const pkpy::ArgList& args) { + if(!args[0]->isType(vm->_tp_int) || !args[1]->isType(vm->_tp_int)) + vm->typeError("unsupported operand type(s) for " "%" ); + _Int rhs = vm->PyInt_AS_C(args[1]); + if(rhs == 0) vm->zeroDivisionError(); + return vm->PyInt(vm->PyInt_AS_C(args[0]) % rhs); + }); + + _vm->bindMethod("int", "__repr__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyStr(std::to_string(vm->PyInt_AS_C(args[0]))); + }); + + _vm->bindMethod("int", "__json__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyStr(std::to_string((int)vm->PyInt_AS_C(args[0]))); + }); + +#define __INT_BITWISE_OP(name,op) \ + _vm->bindMethod("int", #name, [](VM* vm, const pkpy::ArgList& args) { \ + if(!args[0]->isType(vm->_tp_int) || !args[1]->isType(vm->_tp_int)) \ + vm->typeError("unsupported operand type(s) for " #op ); \ + return vm->PyInt(vm->PyInt_AS_C(args[0]) op vm->PyInt_AS_C(args[1])); \ + }); + + __INT_BITWISE_OP(__lshift__, <<) + __INT_BITWISE_OP(__rshift__, >>) + __INT_BITWISE_OP(__and__, &) + __INT_BITWISE_OP(__or__, |) + __INT_BITWISE_OP(__xor__, ^) + +#undef __INT_BITWISE_OP + + _vm->bindMethod("int", "__xor__", [](VM* vm, const pkpy::ArgList& args) { + if(!args[0]->isType(vm->_tp_int) || !args[1]->isType(vm->_tp_int)) + vm->typeError("unsupported operand type(s) for " "^" ); + return vm->PyInt(vm->PyInt_AS_C(args[0]) ^ vm->PyInt_AS_C(args[1])); + }); + + /************ PyFloat ************/ + _vm->bindMethod("float", "__new__", [](VM* vm, const pkpy::ArgList& args) { + if(args.size() == 0) return vm->PyFloat(0.0); + vm->__checkArgSize(args, 1); + if (args[0]->isType(vm->_tp_int)) return vm->PyFloat((_Float)vm->PyInt_AS_C(args[0])); + if (args[0]->isType(vm->_tp_float)) return args[0]; + if (args[0]->isType(vm->_tp_bool)) return vm->PyFloat(vm->PyBool_AS_C(args[0]) ? 1.0 : 0.0); + if (args[0]->isType(vm->_tp_str)) { + const _Str& s = vm->PyStr_AS_C(args[0]); + if(s == "inf") return vm->PyFloat(INFINITY); + if(s == "-inf") return vm->PyFloat(-INFINITY); + try{ + _Float val = std::stod(s.str()); + return vm->PyFloat(val); + }catch(std::invalid_argument&){ + vm->valueError("invalid literal for float(): '" + s + "'"); + } + } + vm->typeError("float() argument must be a int, float, bool or str"); + return vm->None; + }); + + _vm->bindMethod("float", "__repr__", [](VM* vm, const pkpy::ArgList& args) { + _Float val = vm->PyFloat_AS_C(args[0]); + if(std::isinf(val) || std::isnan(val)) return vm->PyStr(std::to_string(val)); + _StrStream ss; + ss << std::setprecision(std::numeric_limits<_Float>::max_digits10-1) << val; + std::string s = ss.str(); + if(std::all_of(s.begin()+1, s.end(), isdigit)) s += ".0"; + return vm->PyStr(s); + }); + + _vm->bindMethod("float", "__json__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyStr(std::to_string((float)vm->PyFloat_AS_C(args[0]))); + }); + + /************ PyString ************/ + _vm->bindMethod("str", "__new__", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->asStr(args[0]); + }); + + _vm->bindMethod("str", "__add__", [](VM* vm, const pkpy::ArgList& args) { + if(!args[0]->isType(vm->_tp_str) || !args[1]->isType(vm->_tp_str)) + vm->typeError("unsupported operand type(s) for " "+" ); + const _Str& lhs = vm->PyStr_AS_C(args[0]); + const _Str& rhs = vm->PyStr_AS_C(args[1]); + return vm->PyStr(lhs + rhs); + }); + + _vm->bindMethod("str", "__len__", [](VM* vm, const pkpy::ArgList& args) { + const _Str& _self = vm->PyStr_AS_C(args[0]); + return vm->PyInt(_self.u8_length()); + }); + + _vm->bindMethod("str", "__contains__", [](VM* vm, const pkpy::ArgList& args) { + const _Str& _self = vm->PyStr_AS_C(args[0]); + const _Str& _other = vm->PyStr_AS_C(args[1]); + return vm->PyBool(_self.str().find(_other.str()) != _Str::npos); + }); + + _vm->bindMethod("str", "__str__", [](VM* vm, const pkpy::ArgList& args) { + return args[0]; // str is immutable + }); + + _vm->bindMethod("str", "__iter__", [](VM* vm, const pkpy::ArgList& args) { + _Iterator* iter = new StringIterator(vm, args[0]); + return vm->PyIter(pkpy::shared_ptr<_Iterator>(iter)); + }); + + _vm->bindMethod("str", "__repr__", [](VM* vm, const pkpy::ArgList& args) { + const _Str& _self = vm->PyStr_AS_C(args[0]); + return vm->PyStr(_self.__escape(true)); + }); + + _vm->bindMethod("str", "__json__", [](VM* vm, const pkpy::ArgList& args) { + const _Str& _self = vm->PyStr_AS_C(args[0]); + return vm->PyStr(_self.__escape(false)); + }); + + _vm->bindMethod("str", "__eq__", [](VM* vm, const pkpy::ArgList& args) { + if(args[0]->isType(vm->_tp_str) && args[1]->isType(vm->_tp_str)) + return vm->PyBool(vm->PyStr_AS_C(args[0]) == vm->PyStr_AS_C(args[1])); + return vm->PyBool(args[0] == args[1]); // fallback + }); + + _vm->bindMethod("str", "__getitem__", [](VM* vm, const pkpy::ArgList& args) { + const _Str& _self (vm->PyStr_AS_C(args[0])); + + if(args[1]->isType(vm->_tp_slice)){ + _Slice s = vm->PySlice_AS_C(args[1]); + s.normalize(_self.u8_length()); + return vm->PyStr(_self.u8_substr(s.start, s.stop)); + } + + int _index = (int)vm->PyInt_AS_C(args[1]); + _index = vm->normalizedIndex(_index, _self.u8_length()); + return vm->PyStr(_self.u8_getitem(_index)); + }); + + _vm->bindMethod("str", "__gt__", [](VM* vm, const pkpy::ArgList& args) { + const _Str& _self (vm->PyStr_AS_C(args[0])); + const _Str& _obj (vm->PyStr_AS_C(args[1])); + return vm->PyBool(_self > _obj); + }); + + _vm->bindMethod("str", "__lt__", [](VM* vm, const pkpy::ArgList& args) { + const _Str& _self (vm->PyStr_AS_C(args[0])); + const _Str& _obj (vm->PyStr_AS_C(args[1])); + return vm->PyBool(_self < _obj); + }); + + _vm->bindMethod("str", "upper", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1, true); + const _Str& _self (vm->PyStr_AS_C(args[0])); + _StrStream ss; + for(auto c : _self.str()) ss << (char)toupper(c); + return vm->PyStr(ss.str()); + }); + + _vm->bindMethod("str", "lower", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1, true); + const _Str& _self (vm->PyStr_AS_C(args[0])); + _StrStream ss; + for(auto c : _self.str()) ss << (char)tolower(c); + return vm->PyStr(ss.str()); + }); + + _vm->bindMethod("str", "replace", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 3, true); + const _Str& _self = vm->PyStr_AS_C(args[0]); + const _Str& _old = vm->PyStr_AS_C(args[1]); + const _Str& _new = vm->PyStr_AS_C(args[2]); + std::string _copy = _self.str(); + // replace all occurences of _old with _new in _copy + size_t pos = 0; + while ((pos = _copy.find(_old.str(), pos)) != std::string::npos) { + _copy.replace(pos, _old.str().length(), _new.str()); + pos += _new.str().length(); + } + return vm->PyStr(_copy); + }); + + _vm->bindMethod("str", "startswith", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 2, true); + const _Str& _self = vm->PyStr_AS_C(args[0]); + const _Str& _prefix = vm->PyStr_AS_C(args[1]); + return vm->PyBool(_self.str().find(_prefix.str()) == 0); + }); + + _vm->bindMethod("str", "endswith", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 2, true); + const _Str& _self = vm->PyStr_AS_C(args[0]); + const _Str& _suffix = vm->PyStr_AS_C(args[1]); + return vm->PyBool(_self.str().rfind(_suffix.str()) == _self.str().length() - _suffix.str().length()); + }); + + _vm->bindMethod("str", "join", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 2, true); + const _Str& _self = vm->PyStr_AS_C(args[0]); + PyVarList* _list; + if(args[1]->isType(vm->_tp_list)){ + _list = &vm->PyList_AS_C(args[1]); + }else if(args[1]->isType(vm->_tp_tuple)){ + _list = &vm->PyTuple_AS_C(args[1]); + }else{ + vm->typeError("can only join a list or tuple"); + } + _StrStream ss; + for(int i = 0; i < _list->size(); i++){ + if(i > 0) ss << _self; + ss << vm->PyStr_AS_C(vm->asStr(_list->operator[](i))); + } + return vm->PyStr(ss.str()); + }); + + /************ PyList ************/ + _vm->bindMethod("list", "__iter__", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkType(args[0], vm->_tp_list); + _Iterator* iter = new VectorIterator(vm, args[0]); + return vm->PyIter(pkpy::shared_ptr<_Iterator>(iter)); + }); + + _vm->bindMethod("list", "append", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 2, true); + PyVarList& _self = vm->PyList_AS_C(args[0]); + _self.push_back(args[1]); + return vm->None; + }); + + _vm->bindMethod("list", "insert", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 3, true); + PyVarList& _self = vm->PyList_AS_C(args[0]); + int _index = (int)vm->PyInt_AS_C(args[1]); + if(_index < 0) _index += _self.size(); + if(_index < 0) _index = 0; + if(_index > _self.size()) _index = _self.size(); + _self.insert(_self.begin() + _index, args[2]); + return vm->None; + }); + + _vm->bindMethod("list", "clear", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1, true); + vm->PyList_AS_C(args[0]).clear(); + return vm->None; + }); + + _vm->bindMethod("list", "copy", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1, true); + return vm->PyList(vm->PyList_AS_C(args[0])); + }); + + _vm->bindMethod("list", "pop", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1, true); + PyVarList& _self = vm->PyList_AS_C(args[0]); + if(_self.empty()) vm->indexError("pop from empty list"); + PyVar ret = _self.back(); + _self.pop_back(); + return ret; + }); + + _vm->bindMethod("list", "__add__", [](VM* vm, const pkpy::ArgList& args) { + const PyVarList& _self = vm->PyList_AS_C(args[0]); + const PyVarList& _obj = vm->PyList_AS_C(args[1]); + PyVarList _new_list = _self; + _new_list.insert(_new_list.end(), _obj.begin(), _obj.end()); + return vm->PyList(_new_list); + }); + + _vm->bindMethod("list", "__len__", [](VM* vm, const pkpy::ArgList& args) { + const PyVarList& _self = vm->PyList_AS_C(args[0]); + return vm->PyInt(_self.size()); + }); + + _vm->bindMethod("list", "__getitem__", [](VM* vm, const pkpy::ArgList& args) { + const PyVarList& _self = vm->PyList_AS_C(args[0]); + + if(args[1]->isType(vm->_tp_slice)){ + _Slice s = vm->PySlice_AS_C(args[1]); + s.normalize(_self.size()); + PyVarList _new_list; + for(size_t i = s.start; i < s.stop; i++) + _new_list.push_back(_self[i]); + return vm->PyList(_new_list); + } + + int _index = (int)vm->PyInt_AS_C(args[1]); + _index = vm->normalizedIndex(_index, _self.size()); + return _self[_index]; + }); + + _vm->bindMethod("list", "__setitem__", [](VM* vm, const pkpy::ArgList& args) { + PyVarList& _self = vm->PyList_AS_C(args[0]); + int _index = (int)vm->PyInt_AS_C(args[1]); + _index = vm->normalizedIndex(_index, _self.size()); + _self[_index] = args[2]; + return vm->None; + }); + + _vm->bindMethod("list", "__delitem__", [](VM* vm, const pkpy::ArgList& args) { + PyVarList& _self = vm->PyList_AS_C(args[0]); + int _index = (int)vm->PyInt_AS_C(args[1]); + _index = vm->normalizedIndex(_index, _self.size()); + _self.erase(_self.begin() + _index); + return vm->None; + }); + + /************ PyTuple ************/ + _vm->bindMethod("tuple", "__new__", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + PyVarList _list = vm->PyList_AS_C(vm->call(vm->builtins->attribs["list"], args)); + return vm->PyTuple(_list); + }); + + _vm->bindMethod("tuple", "__iter__", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkType(args[0], vm->_tp_tuple); + _Iterator* iter = new VectorIterator(vm, args[0]); + return vm->PyIter(pkpy::shared_ptr<_Iterator>(iter)); + }); + + _vm->bindMethod("tuple", "__len__", [](VM* vm, const pkpy::ArgList& args) { + const PyVarList& _self = vm->PyTuple_AS_C(args[0]); + return vm->PyInt(_self.size()); + }); + + _vm->bindMethod("tuple", "__getitem__", [](VM* vm, const pkpy::ArgList& args) { + const PyVarList& _self = vm->PyTuple_AS_C(args[0]); + int _index = (int)vm->PyInt_AS_C(args[1]); + _index = vm->normalizedIndex(_index, _self.size()); + return _self[_index]; + }); + + /************ PyBool ************/ + _vm->bindMethod("bool", "__repr__", [](VM* vm, const pkpy::ArgList& args) { + bool val = vm->PyBool_AS_C(args[0]); + return vm->PyStr(val ? "True" : "False"); + }); + + _vm->bindMethod("bool", "__json__", [](VM* vm, const pkpy::ArgList& args) { + bool val = vm->PyBool_AS_C(args[0]); + return vm->PyStr(val ? "true" : "false"); + }); + + _vm->bindMethod("bool", "__eq__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyBool(args[0] == args[1]); + }); + + _vm->bindMethod("bool", "__xor__", [](VM* vm, const pkpy::ArgList& args) { + bool _self = vm->PyBool_AS_C(args[0]); + bool _obj = vm->PyBool_AS_C(args[1]); + return vm->PyBool(_self ^ _obj); + }); + + _vm->bindMethod("ellipsis", "__repr__", [](VM* vm, const pkpy::ArgList& args) { + return vm->PyStr("Ellipsis"); + }); + + _vm->bindMethod("_native_function", "__call__", [](VM* vm, const pkpy::ArgList& args) { + const _CppFunc& _self = vm->PyNativeFunction_AS_C(args[0]); + return _self(vm, args.subList(1)); + }); + + _vm->bindMethod("function", "__call__", [](VM* vm, const pkpy::ArgList& args) { + return vm->call(args[0], args.subList(1)); + }); + + _vm->bindMethod("_bounded_method", "__call__", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkType(args[0], vm->_tp_bounded_method); + const _BoundedMethod& _self = vm->PyBoundedMethod_AS_C(args[0]); + pkpy::ArgList newArgs(args.size()); + newArgs[0] = _self.obj; + for(int i = 1; i < args.size(); i++) newArgs[i] = args[i]; + return vm->call(_self.method, newArgs); + }); +} + +#ifdef _WIN32 +#define __EXPORT __declspec(dllexport) +#elif __APPLE__ +#define __EXPORT __attribute__((visibility("default"))) __attribute__((used)) +#else +#define __EXPORT +#endif + + +void __addModuleTime(VM* vm){ + PyVar mod = vm->newModule("time"); + vm->bindFunc(mod, "time", [](VM* vm, const pkpy::ArgList& args) { + auto now = std::chrono::high_resolution_clock::now(); + return vm->PyFloat(std::chrono::duration_cast(now.time_since_epoch()).count() / 1000000.0); + }); + + vm->bindFunc(mod, "sleep", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + if(!vm->isIntOrFloat(args[0])){ + vm->typeError("time.sleep() argument must be int or float"); + } + double sec = vm->numToFloat(args[0]); + vm->sleepForSecs(sec); + return vm->None; + }); +} + +void __addModuleSys(VM* vm){ + PyVar mod = vm->newModule("sys"); + vm->bindFunc(mod, "getrefcount", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyInt(args[0].use_count()); + }); + + vm->bindFunc(mod, "getrecursionlimit", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 0); + return vm->PyInt(vm->maxRecursionDepth); + }); + + vm->bindFunc(mod, "setrecursionlimit", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + vm->maxRecursionDepth = (int)vm->PyInt_AS_C(args[0]); + return vm->None; + }); + + vm->setAttr(mod, "version", vm->PyStr(PK_VERSION)); +} + +void __addModuleJson(VM* vm){ + PyVar mod = vm->newModule("json"); + vm->bindFunc(mod, "loads", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + const _Str& expr = vm->PyStr_AS_C(args[0]); + _Code code = compile(vm, expr.c_str(), "", JSON_MODE, false); + return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals); + }); + + vm->bindFunc(mod, "dumps", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->asJson(args[0]); + }); +} + +void __addModuleMath(VM* vm){ + PyVar mod = vm->newModule("math"); + vm->setAttr(mod, "pi", vm->PyFloat(3.14159265358979323846)); + vm->setAttr(mod, "e", vm->PyFloat(2.7182818284590452354)); + + vm->bindFunc(mod, "log", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyFloat(log(vm->numToFloat(args[0]))); + }); + + vm->bindFunc(mod, "log10", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyFloat(log10(vm->numToFloat(args[0]))); + }); + + vm->bindFunc(mod, "log2", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyFloat(log2(vm->numToFloat(args[0]))); + }); + + vm->bindFunc(mod, "sin", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyFloat(sin(vm->numToFloat(args[0]))); + }); + + vm->bindFunc(mod, "cos", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyFloat(cos(vm->numToFloat(args[0]))); + }); + + vm->bindFunc(mod, "tan", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 1); + return vm->PyFloat(tan(vm->numToFloat(args[0]))); + }); + + vm->bindFunc(mod, "isclose", [](VM* vm, const pkpy::ArgList& args) { + vm->__checkArgSize(args, 2); + _Float a = vm->numToFloat(args[0]); + _Float b = vm->numToFloat(args[1]); + return vm->PyBool(fabs(a - b) < 1e-6); + }); +} + +class _PkExported{ +public: + virtual ~_PkExported() = default; + virtual void* get() = 0; +}; + +static std::vector<_PkExported*> _pkLookupTable; + +template +class PkExported : public _PkExported{ + T* _ptr; +public: + template + PkExported(Args&&... args) { + _ptr = new T(std::forward(args)...); + _pkLookupTable.push_back(this); + } + + ~PkExported() override { delete _ptr; } + void* get() override { return _ptr; } + operator T*() { return _ptr; } +}; + +#define pkpy_allocate(T, ...) *(new PkExported(__VA_ARGS__)) + + +extern "C" { + __EXPORT + /// Delete a class pointer allocated by `pkpy_xxx_xxx`. + /// It can be `VM*`, `REPL*`, `ThreadedVM*`, `char*`, etc. + /// + /// !!! + /// If the pointer is not allocated by `pkpy_xxx_xxx`, the behavior is undefined. + /// For char*, you can also use trivial `delete` in your language. + /// !!! + void pkpy_delete(void* p){ + for(int i = 0; i < _pkLookupTable.size(); i++){ + if(_pkLookupTable[i]->get() == p){ + delete _pkLookupTable[i]; + _pkLookupTable.erase(_pkLookupTable.begin() + i); + return; + } + } + free(p); + } + + __EXPORT + /// Run a given source on a virtual machine. + /// + /// Return `true` if there is no compile error. + bool pkpy_vm_exec(VM* vm, const char* source){ + _Code code = compile(vm, source, "main.py"); + if(code == nullptr) return false; + vm->exec(code); + return true; + } + + __EXPORT + /// Get a global variable of a virtual machine. + /// + /// Return a json representing the result. + /// If the variable is not found, return `nullptr`. + char* pkpy_vm_get_global(VM* vm, const char* name){ + auto it = vm->_main->attribs.find(name); + if(it == vm->_main->attribs.end()) return nullptr; + _Str _json = vm->PyStr_AS_C(vm->asJson(it->second)); + return strdup(_json.c_str()); + } + + __EXPORT + /// Evaluate an expression. + /// + /// Return a json representing the result. + /// If there is any error, return `nullptr`. + char* pkpy_vm_eval(VM* vm, const char* source){ + _Code code = compile(vm, source, "", EVAL_MODE); + if(code == nullptr) return nullptr; + PyVarOrNull ret = vm->exec(code); + if(ret == nullptr) return nullptr; + _Str _json = vm->PyStr_AS_C(vm->asJson(ret)); + return strdup(_json.c_str()); + } + + __EXPORT + /// Create a REPL, using the given virtual machine as the backend. + REPL* pkpy_new_repl(VM* vm){ + return pkpy_allocate(REPL, vm); + } + + __EXPORT + /// Input a source line to an interactive console. + /// + /// Return `0` if need more lines, + /// `1` if execution happened, + /// `2` if execution skipped (compile error or empty input). + int pkpy_repl_input(REPL* r, const char* line){ + return r->input(line); + } + + __EXPORT + /// Add a source module into a virtual machine. + /// + /// Return `true` if there is no complie error. + bool pkpy_vm_add_module(VM* vm, const char* name, const char* source){ + // compile the module but don't execute it + _Code code = compile(vm, source, name + _Str(".py")); + if(code == nullptr) return false; + vm->addLazyModule(name, code); + return true; + } + + void __vm_init(VM* vm){ + __initializeBuiltinFunctions(vm); + __addModuleSys(vm); + __addModuleTime(vm); + __addModuleJson(vm); + __addModuleMath(vm); + + _Code code = compile(vm, __BUILTINS_CODE, ""); + if(code == nullptr) exit(1); + vm->_exec(code, vm->builtins, {}); + pkpy_vm_add_module(vm, "random", __RANDOM_CODE); + pkpy_vm_add_module(vm, "os", __OS_CODE); + } + + __EXPORT + /// Create a virtual machine. + VM* pkpy_new_vm(bool use_stdio){ + VM* vm = pkpy_allocate(VM, use_stdio); + __vm_init(vm); + return vm; + } + + __EXPORT + /// Create a virtual machine that supports asynchronous execution. + ThreadedVM* pkpy_new_tvm(bool use_stdio){ + ThreadedVM* vm = pkpy_allocate(ThreadedVM, use_stdio); + __vm_init(vm); + return vm; + } + + __EXPORT + /// Read the standard output and standard error as string of a virtual machine. + /// The `vm->use_stdio` should be `false`. + /// After this operation, both stream will be cleared. + /// + /// Return a json representing the result. + char* pkpy_vm_read_output(VM* vm){ + if(vm->use_stdio) return nullptr; + _StrStream* s_out = dynamic_cast<_StrStream*>(vm->_stdout); + _StrStream* s_err = dynamic_cast<_StrStream*>(vm->_stderr); + _Str _stdout = s_out->str(); + _Str _stderr = s_err->str(); + _StrStream ss; + ss << '{' << "\"stdout\": " << _stdout.__escape(false); + ss << ", "; + ss << "\"stderr\": " << _stderr.__escape(false) << '}'; + s_out->str(""); + s_err->str(""); + return strdup(ss.str().c_str()); + } + + __EXPORT + /// Get the current state of a threaded virtual machine. + /// + /// Return `0` for `THREAD_READY`, + /// `1` for `THREAD_RUNNING`, + /// `2` for `THREAD_SUSPENDED`, + /// `3` for `THREAD_FINISHED`. + int pkpy_tvm_get_state(ThreadedVM* vm){ + return vm->getState(); + } + + __EXPORT + /// Set the state of a threaded virtual machine to `THREAD_READY`. + /// The current state should be `THREAD_FINISHED`. + void pkpy_tvm_reset_state(ThreadedVM* vm){ + vm->resetState(); + } + + __EXPORT + /// Read the current JSONRPC request from shared string buffer. + char* pkpy_tvm_read_jsonrpc_request(ThreadedVM* vm){ + _Str s = vm->readJsonRpcRequest(); + return strdup(s.c_str()); + } + + __EXPORT + /// Write a JSONRPC response to shared string buffer. + void pkpy_tvm_write_jsonrpc_response(ThreadedVM* vm, const char* value){ + vm->writeJsonrpcResponse(value); + } + + __EXPORT + /// Emit a KeyboardInterrupt signal to stop a running threaded virtual machine. + void pkpy_tvm_terminate(ThreadedVM* vm){ + vm->terminate(); + } + + __EXPORT + /// Run a given source on a threaded virtual machine. + /// The excution will be started in a new thread. + /// + /// Return `true` if there is no compile error. + bool pkpy_tvm_exec_async(VM* vm, const char* source){ + // although this is a method of VM, it's only used in ThreadedVM + _Code code = compile(vm, source, "main.py"); + if(code == nullptr) return false; + vm->execAsync(code); + return true; + } +} + +#endif // POCKETPY_H \ No newline at end of file diff --git a/plugins/flutter/windows/.gitignore b/plugins/flutter/windows/.gitignore new file mode 100644 index 00000000..b3eb2be1 --- /dev/null +++ b/plugins/flutter/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/plugins/flutter/windows/CMakeLists.txt b/plugins/flutter/windows/CMakeLists.txt new file mode 100644 index 00000000..252418cd --- /dev/null +++ b/plugins/flutter/windows/CMakeLists.txt @@ -0,0 +1,23 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "pocketpy") +project(${PROJECT_NAME} LANGUAGES CXX) + +# Invoke the build for native code shared with the other target platforms. +# This can be changed to accomodate different builds. +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared") + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(pocketpy_bundled_libraries + # Defined in ../src/CMakeLists.txt. + # This can be changed to accomodate different builds. + $ + PARENT_SCOPE +) diff --git a/plugins/unity/.gitattributes b/plugins/unity/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/plugins/unity/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/plugins/unity/.gitignore b/plugins/unity/.gitignore new file mode 100644 index 00000000..58cbc825 --- /dev/null +++ b/plugins/unity/.gitignore @@ -0,0 +1,72 @@ +# This .gitignore file should be placed at the root of your Unity project directory +# +# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore +# +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ + +# MemoryCaptures can get excessive in size. +# They also could contain extremely sensitive data +/[Mm]emoryCaptures/ + +# Recordings can get excessive in size +/[Rr]ecordings/ + +# Uncomment this line if you wish to ignore the asset store tools plugin +# /[Aa]ssets/AssetStoreTools* + +# Autogenerated Jetbrains Rider plugin +/[Aa]ssets/Plugins/Editor/JetBrains* + +# Visual Studio cache directory +.vs/ + +# Gradle cache directory +.gradle/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity3D generated file on crash reports +sysinfo.txt + +# Builds +*.apk +*.aab +*.unitypackage +*.app + +# Crashlytics generated file +crashlytics-build.properties + +# Packed Addressables +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* + +# Temporary auto-generated Android Assets +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* diff --git a/plugins/unity/.vsconfig b/plugins/unity/.vsconfig new file mode 100644 index 00000000..d70cd98b --- /dev/null +++ b/plugins/unity/.vsconfig @@ -0,0 +1,6 @@ +{ + "version": "1.0", + "components": [ + "Microsoft.VisualStudio.Workload.ManagedGame" + ] +} diff --git a/plugins/unity/Assets/PocketPy.meta b/plugins/unity/Assets/PocketPy.meta new file mode 100644 index 00000000..9db3231f --- /dev/null +++ b/plugins/unity/Assets/PocketPy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f79d8d6c437bed14ea26999ed42480ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/JsonRpcServer.cs b/plugins/unity/Assets/PocketPy/JsonRpcServer.cs new file mode 100644 index 00000000..76d6cf09 --- /dev/null +++ b/plugins/unity/Assets/PocketPy/JsonRpcServer.cs @@ -0,0 +1,11 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace pkpy +{ + public abstract class JsonRpcServer + { + + } +} diff --git a/plugins/unity/Assets/PocketPy/JsonRpcServer.cs.meta b/plugins/unity/Assets/PocketPy/JsonRpcServer.cs.meta new file mode 100644 index 00000000..b112e499 --- /dev/null +++ b/plugins/unity/Assets/PocketPy/JsonRpcServer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d5146910c27a3446a31cc75c6329ca3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/Plugins.meta b/plugins/unity/Assets/PocketPy/Plugins.meta new file mode 100644 index 00000000..1f5c064d --- /dev/null +++ b/plugins/unity/Assets/PocketPy/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fc49b939791e4e945a69697947f29502 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/Plugins/Android.meta b/plugins/unity/Assets/PocketPy/Plugins/Android.meta new file mode 100644 index 00000000..90f5d6a0 --- /dev/null +++ b/plugins/unity/Assets/PocketPy/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2e4ac82becb1112459f68bde13f50c43 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/Plugins/Windows.meta b/plugins/unity/Assets/PocketPy/Plugins/Windows.meta new file mode 100644 index 00000000..e27691ba --- /dev/null +++ b/plugins/unity/Assets/PocketPy/Plugins/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1cf5d7bc926f7c74b9073bcbcee9662c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/Plugins/Windows/pocketpy.dll b/plugins/unity/Assets/PocketPy/Plugins/Windows/pocketpy.dll new file mode 100644 index 00000000..e3d9ebc4 Binary files /dev/null and b/plugins/unity/Assets/PocketPy/Plugins/Windows/pocketpy.dll differ diff --git a/plugins/unity/Assets/PocketPy/Plugins/iOS.meta b/plugins/unity/Assets/PocketPy/Plugins/iOS.meta new file mode 100644 index 00000000..78fdb067 --- /dev/null +++ b/plugins/unity/Assets/PocketPy/Plugins/iOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2655e1286acb58b41957aa850819d14c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/Tests.meta b/plugins/unity/Assets/PocketPy/Tests.meta new file mode 100644 index 00000000..80a2b279 --- /dev/null +++ b/plugins/unity/Assets/PocketPy/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6f4d4dc1e7467d948a999f59400a2fdd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/Tests/A.cs b/plugins/unity/Assets/PocketPy/Tests/A.cs new file mode 100644 index 00000000..e5a1ca36 --- /dev/null +++ b/plugins/unity/Assets/PocketPy/Tests/A.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using pkpy; + +public class A : MonoBehaviour +{ + // Start is called before the first frame update + void Start() + { + var vm = new pkpy.VM(); + vm.exec("print('Hello world')"); + PyOutput o = vm.read_output(); + Debug.Log(o.stdout.Trim()); + //Debug.Log(o.stderr); + } + + // Update is called once per frame + void Update() + { + + } +} diff --git a/plugins/unity/Assets/PocketPy/Tests/A.cs.meta b/plugins/unity/Assets/PocketPy/Tests/A.cs.meta new file mode 100644 index 00000000..3e7dbf20 --- /dev/null +++ b/plugins/unity/Assets/PocketPy/Tests/A.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05ebca85532bc4545a222a343a5b9dba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/PocketPy/pocketpy.cs b/plugins/unity/Assets/PocketPy/pocketpy.cs new file mode 100644 index 00000000..993a6e3e --- /dev/null +++ b/plugins/unity/Assets/PocketPy/pocketpy.cs @@ -0,0 +1,207 @@ +using System; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace pkpy +{ + public static class Bindings + { +#if UNITY_IOS + private const string _libName = "__Internal"; +#else + private const string _libName = "pocketpy"; +#endif + [DllImport(_libName)] + internal static extern void pkpy_delete(IntPtr p); + [DllImport(_libName)] + internal static extern IntPtr pkpy_new_repl(IntPtr vm); + [DllImport(_libName)] + internal static extern int pkpy_repl_input(IntPtr r, string line); + [DllImport(_libName)] + internal static extern IntPtr pkpy_new_tvm(bool use_stdio); + [DllImport(_libName)] + internal static extern bool pkpy_tvm_exec_async(IntPtr vm, string source); + [DllImport(_libName)] + internal static extern int pkpy_tvm_get_state(IntPtr vm); + [DllImport(_libName)] + internal static extern string pkpy_tvm_read_jsonrpc_request(IntPtr vm); + [DllImport(_libName)] + internal static extern void pkpy_tvm_reset_state(IntPtr vm); + [DllImport(_libName)] + internal static extern void pkpy_tvm_terminate(IntPtr vm); + [DllImport(_libName)] + internal static extern void pkpy_tvm_write_jsonrpc_response(IntPtr vm, string value); + [DllImport(_libName)] + internal static extern IntPtr pkpy_new_vm(bool use_stdio); + [DllImport(_libName)] + internal static extern bool pkpy_vm_add_module(IntPtr vm, string name, string source); + [DllImport(_libName)] + internal static extern string pkpy_vm_eval(IntPtr vm, string source); + [DllImport(_libName)] + internal static extern bool pkpy_vm_exec(IntPtr vm, string source); + [DllImport(_libName)] + internal static extern string pkpy_vm_get_global(IntPtr vm, string name); + [DllImport(_libName)] + internal static extern string pkpy_vm_read_output(IntPtr vm); + } +} + +namespace pkpy +{ + public struct PyOutput + { + public string stdout; + public string stderr; + + public PyOutput(string stdout, string stderr) + { + this.stdout = stdout; + this.stderr = stderr; + } + } + + public class VM + { + public IntPtr pointer { get; private set; } + + public VM() + { + if(this is ThreadedVM) + { + pointer = Bindings.pkpy_new_tvm(false); + } + else + { + pointer = Bindings.pkpy_new_vm(false); + } + } + + public PyOutput read_output() + { + var _o = Bindings.pkpy_vm_read_output(pointer); + return JsonUtility.FromJson(_o); + } + + public void dispose() + { + Bindings.pkpy_delete(pointer); + } + + /// + /// Add a source module into a virtual machine. Return `true` if there is no complie error. + /// + public bool add_module(string name, string source) + { + return Bindings.pkpy_vm_add_module(pointer, name, source); + } + + /// + /// Evaluate an expression. Return a json representing the result. If there is any error, return `nullptr`. + /// + public string eval(string source) + { + return Bindings.pkpy_vm_eval(pointer, source); + } + + /// + /// Run a given source on a virtual machine. Return `true` if there is no compile error. + /// + public bool exec(string source) + { + return Bindings.pkpy_vm_exec(pointer, source); + } + + /// + /// Get a global variable of a virtual machine. Return a json representing the result. If the variable is not found, return `nullptr`. + /// + public string get_global(string name) + { + return Bindings.pkpy_vm_get_global(pointer, name); + } + + } + + public enum ThreadState + { + ready = 0, + running, + suspended, + finished + } + + public class ThreadedVM : VM + { + public ThreadState state => (ThreadState)Bindings.pkpy_tvm_get_state(pointer); + + /// + /// Run a given source on a threaded virtual machine. The excution will be started in a new thread. Return `true` if there is no compile error. + /// + public bool exec_async(string source) + { + return Bindings.pkpy_tvm_exec_async(pointer, source); + } + + /// + /// Read the current JSONRPC request from shared string buffer. + /// + public string read_jsonrpc_request() + { + return Bindings.pkpy_tvm_read_jsonrpc_request(pointer); + } + + /// + /// Set the state of a threaded virtual machine to `THREAD_READY`. The current state should be `THREAD_FINISHED`. + /// + public void reset_state() + { + Bindings.pkpy_tvm_reset_state(pointer); + } + + /// + /// Emit a KeyboardInterrupt signal to stop a running threaded virtual machine. + /// + public void terminate() + { + Bindings.pkpy_tvm_terminate(pointer); + } + + /// + /// Write a JSONRPC response to shared string buffer. + /// + public void write_jsonrpc_response(string value) + { + Bindings.pkpy_tvm_write_jsonrpc_response(pointer, value); + } + + } +} + +namespace pkpy +{ + public class REPL + { + public IntPtr pointer { get; private set; } + public VM vm { get; private set; } + + public REPL(VM vm) + { + this.vm = vm; + pointer = Bindings.pkpy_new_repl(vm.pointer); + } + + public void dispose() + { + Bindings.pkpy_delete(pointer); + } + + /// + /// Input a source line to an interactive console. Return `0` if need more lines, `1` if execution happened, `2` if execution skipped (compile error or empty input). + /// + public int input(string line) + { + return Bindings.pkpy_repl_input(pointer, line); + } + + } +} + diff --git a/plugins/unity/Assets/PocketPy/pocketpy.cs.meta b/plugins/unity/Assets/PocketPy/pocketpy.cs.meta new file mode 100644 index 00000000..2d0c124f --- /dev/null +++ b/plugins/unity/Assets/PocketPy/pocketpy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd15753450212f045aa08d30ba3cca6e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/Scenes.meta b/plugins/unity/Assets/Scenes.meta new file mode 100644 index 00000000..7fe8e109 --- /dev/null +++ b/plugins/unity/Assets/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 131a6b21c8605f84396be9f6751fb6e3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/Assets/Scenes/SampleScene.unity b/plugins/unity/Assets/Scenes/SampleScene.unity new file mode 100644 index 00000000..a2644f13 --- /dev/null +++ b/plugins/unity/Assets/Scenes/SampleScene.unity @@ -0,0 +1,222 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &519420028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 519420032} + - component: {fileID: 519420031} + - component: {fileID: 519420029} + - component: {fileID: 519420033} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &519420029 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 +--- !u!20 &519420031 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 0 + m_HDR: 1 + m_AllowMSAA: 0 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &519420032 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &519420033 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 05ebca85532bc4545a222a343a5b9dba, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/plugins/unity/Assets/Scenes/SampleScene.unity.meta b/plugins/unity/Assets/Scenes/SampleScene.unity.meta new file mode 100644 index 00000000..c1e3c88e --- /dev/null +++ b/plugins/unity/Assets/Scenes/SampleScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2cda990e2423bbf4892e6590ba056729 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/plugins/unity/LICENSE b/plugins/unity/LICENSE new file mode 100644 index 00000000..bde60ceb --- /dev/null +++ b/plugins/unity/LICENSE @@ -0,0 +1,165 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/plugins/unity/Packages/manifest.json b/plugins/unity/Packages/manifest.json new file mode 100644 index 00000000..9e02b62c --- /dev/null +++ b/plugins/unity/Packages/manifest.json @@ -0,0 +1,45 @@ +{ + "dependencies": { + "com.unity.collab-proxy": "1.15.18", + "com.unity.feature.2d": "1.0.0", + "com.unity.ide.rider": "3.0.15", + "com.unity.ide.visualstudio": "2.0.16", + "com.unity.ide.vscode": "1.2.5", + "com.unity.test-framework": "1.1.31", + "com.unity.textmeshpro": "3.0.6", + "com.unity.timeline": "1.6.4", + "com.unity.ugui": "1.0.0", + "com.unity.visualscripting": "1.7.8", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/plugins/unity/Packages/packages-lock.json b/plugins/unity/Packages/packages-lock.json new file mode 100644 index 00000000..4ea1eb71 --- /dev/null +++ b/plugins/unity/Packages/packages-lock.json @@ -0,0 +1,483 @@ +{ + "dependencies": { + "com.unity.2d.animation": { + "version": "7.0.6", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "6.0.3", + "com.unity.2d.sprite": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.common": { + "version": "6.0.3", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.2d.sprite": "1.0.0", + "com.unity.mathematics": "1.1.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.burst": "1.5.1" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.path": { + "version": "5.0.2", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.2d.pixel-perfect": { + "version": "5.0.1", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.2d.psdimporter": { + "version": "6.0.4", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.animation": "7.0.6", + "com.unity.2d.common": "6.0.3", + "com.unity.2d.sprite": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.sprite": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.2d.spriteshape": { + "version": "7.0.4", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.1.0", + "com.unity.2d.common": "6.0.3", + "com.unity.2d.path": "5.0.2", + "com.unity.modules.physics2d": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.tilemap": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.2d.tilemap.extras": { + "version": "2.2.3", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.tilemap": "1.0.0", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.ugui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.burst": { + "version": "1.6.6", + "depth": 3, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1" + }, + "url": "https://packages.unity.com" + }, + "com.unity.collab-proxy": { + "version": "1.15.18", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.services.core": "1.0.1" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.feature.2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.2d.animation": "7.0.6", + "com.unity.2d.pixel-perfect": "5.0.1", + "com.unity.2d.psdimporter": "6.0.4", + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.spriteshape": "7.0.4", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.2d.tilemap.extras": "2.2.3" + } + }, + "com.unity.ide.rider": { + "version": "3.0.15", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.16", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.9" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.vscode": { + "version": "1.2.5", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.mathematics": { + "version": "1.2.6", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.0.2", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.services.core": { + "version": "1.4.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.nuget.newtonsoft-json": "3.0.2", + "com.unity.modules.androidjni": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.test-framework": { + "version": "1.1.31", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.textmeshpro": { + "version": "3.0.6", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.timeline": { + "version": "1.6.4", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.director": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ugui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.visualscripting": { + "version": "1.7.8", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.uielementsnative": "1.0.0" + } + }, + "com.unity.modules.uielementsnative": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +} diff --git a/plugins/unity/ProjectSettings/AudioManager.asset b/plugins/unity/ProjectSettings/AudioManager.asset new file mode 100644 index 00000000..27287fec --- /dev/null +++ b/plugins/unity/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/plugins/unity/ProjectSettings/ClusterInputManager.asset b/plugins/unity/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 00000000..e7886b26 --- /dev/null +++ b/plugins/unity/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/plugins/unity/ProjectSettings/DynamicsManager.asset b/plugins/unity/ProjectSettings/DynamicsManager.asset new file mode 100644 index 00000000..72d14303 --- /dev/null +++ b/plugins/unity/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_DefaultMaxDepenetrationVelocity: 10 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 diff --git a/plugins/unity/ProjectSettings/EditorBuildSettings.asset b/plugins/unity/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 00000000..82ab0f59 --- /dev/null +++ b/plugins/unity/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/SampleScene.unity + guid: 2cda990e2423bbf4892e6590ba056729 + m_configObjects: {} diff --git a/plugins/unity/ProjectSettings/EditorSettings.asset b/plugins/unity/ProjectSettings/EditorSettings.asset new file mode 100644 index 00000000..fa3ed494 --- /dev/null +++ b/plugins/unity/ProjectSettings/EditorSettings.asset @@ -0,0 +1,40 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 0 + m_DefaultBehaviorMode: 1 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 4 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp + m_ProjectGenerationRootNamespace: + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_CachingShaderPreprocessor: 1 + m_PrefabModeAllowAutoSave: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_GameObjectNamingDigits: 1 + m_GameObjectNamingScheme: 0 + m_AssetNamingUsesSpace: 1 + m_UseLegacyProbeSampleCount: 0 + m_SerializeInlineMappingsOnOneLine: 1 + m_DisableCookiesInLightmapper: 1 + m_AssetPipelineMode: 1 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 + m_CacheServerEnableAuth: 0 + m_CacheServerEnableTls: 0 diff --git a/plugins/unity/ProjectSettings/GraphicsSettings.asset b/plugins/unity/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 00000000..c165afb2 --- /dev/null +++ b/plugins/unity/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,64 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_VideoShadersIncludeMode: 2 + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_DefaultRenderingLayerMask: 1 + m_LogWhenShaderIsCompiled: 0 diff --git a/plugins/unity/ProjectSettings/InputManager.asset b/plugins/unity/ProjectSettings/InputManager.asset new file mode 100644 index 00000000..b16147e9 --- /dev/null +++ b/plugins/unity/ProjectSettings/InputManager.asset @@ -0,0 +1,487 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: joystick button 8 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: backspace + altNegativeButton: + altPositiveButton: joystick button 9 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Reset + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Next + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page down + altNegativeButton: + altPositiveButton: joystick button 5 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Previous + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page up + altNegativeButton: + altPositiveButton: joystick button 4 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Validate + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Persistent + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: right shift + altNegativeButton: + altPositiveButton: joystick button 2 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Multiplier + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: joystick button 3 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 6 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 5 + joyNum: 0 diff --git a/plugins/unity/ProjectSettings/MemorySettings.asset b/plugins/unity/ProjectSettings/MemorySettings.asset new file mode 100644 index 00000000..5b5facec --- /dev/null +++ b/plugins/unity/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: {} diff --git a/plugins/unity/ProjectSettings/NavMeshAreas.asset b/plugins/unity/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 00000000..ad2654e0 --- /dev/null +++ b/plugins/unity/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,93 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/plugins/unity/ProjectSettings/NetworkManager.asset b/plugins/unity/ProjectSettings/NetworkManager.asset new file mode 100644 index 00000000..5dc6a831 --- /dev/null +++ b/plugins/unity/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/plugins/unity/ProjectSettings/PackageManagerSettings.asset b/plugins/unity/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 00000000..b3a65dda --- /dev/null +++ b/plugins/unity/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,44 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreReleasePackages: 0 + m_EnablePackageDependencies: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Capabilities: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/plugins/unity/ProjectSettings/Physics2DSettings.asset b/plugins/unity/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 00000000..6cfcddaa --- /dev/null +++ b/plugins/unity/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_SimulationMode: 0 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/plugins/unity/ProjectSettings/PresetManager.asset b/plugins/unity/ProjectSettings/PresetManager.asset new file mode 100644 index 00000000..67a94dae --- /dev/null +++ b/plugins/unity/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/plugins/unity/ProjectSettings/ProjectSettings.asset b/plugins/unity/ProjectSettings/ProjectSettings.asset new file mode 100644 index 00000000..b4292fd2 --- /dev/null +++ b/plugins/unity/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,669 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 23 + productGUID: 7ab46e5e256078e45bcef14110d75a23 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: pocketpy-unity + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1920 + defaultScreenHeight: 1080 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 1 + androidBlitType: 0 + androidResizableWindow: 0 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 0 + vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 + useHDRDisplay: 0 + D3DHDRBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: + Standalone: com.DefaultCompany.2DProject + buildNumber: + Standalone: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 1 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 22 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 11.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 11.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + macOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + shaderPrecisionModel: 0 + clonedFromGUID: 10ad67313f4034357812315f3c407484 + templatePackageId: com.unity.template.2d@6.1.1 + templateDefaultScene: Assets/Scenes/SampleScene.unity + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomProguardFile: 0 + AndroidTargetArchitectures: 1 + AndroidTargetDevices: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + chromeosInputEmulation: 1 + AndroidMinifyWithR8: 0 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: Switch + m_GraphicsJobs: 0 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 0 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 0 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 0 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: AndroidPlayer + m_APIs: 150000000b000000 + m_Automatic: 1 + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetNormalMapEncoding: [] + m_BuildTargetDefaultTextureCompressionFormat: + - m_BuildTarget: Android + m_Format: 3 + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + bluetoothUsageDescription: + switchNMETAOverride: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchUseGOLDLinker: 0 + switchLTOSetting: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + switchUseNewStyleFilepaths: 0 + switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 + switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: bi9UOuSpM2Tlh01vOzwvSikHFswuzleh + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLWasmArithmeticExceptions: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLDecompressionFallback: 0 + scriptingDefineSymbols: {} + additionalCompilerArguments: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + enableRoslynAnalyzers: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 1 + assemblyVersionValidation: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: 2D_BuiltInRenderer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: 2D_BuiltInRenderer + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + vcxProjDefaultLanguage: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: {} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + apiCompatibilityLevel: 6 + activeInputHandler: 0 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: + organizationId: + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + playerDataPath: + forceSRGBBlit: 1 + virtualTexturingSupportEnabled: 0 diff --git a/plugins/unity/ProjectSettings/ProjectVersion.txt b/plugins/unity/ProjectSettings/ProjectVersion.txt new file mode 100644 index 00000000..4cb04600 --- /dev/null +++ b/plugins/unity/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2021.3.6f1 +m_EditorVersionWithRevision: 2021.3.6f1 (7da38d85baf6) diff --git a/plugins/unity/ProjectSettings/QualitySettings.asset b/plugins/unity/ProjectSettings/QualitySettings.asset new file mode 100644 index 00000000..bcd67065 --- /dev/null +++ b/plugins/unity/ProjectSettings/QualitySettings.asset @@ -0,0 +1,239 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 255 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Lumin: 5 + GameCoreScarlett: 5 + GameCoreXboxOne: 5 + Nintendo Switch: 5 + PS4: 5 + PS5: 5 + Stadia: 5 + Standalone: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/plugins/unity/ProjectSettings/SceneTemplateSettings.json b/plugins/unity/ProjectSettings/SceneTemplateSettings.json new file mode 100644 index 00000000..6f3e60fd --- /dev/null +++ b/plugins/unity/ProjectSettings/SceneTemplateSettings.json @@ -0,0 +1,167 @@ +{ + "templatePinStates": [], + "dependencyTypeInfos": [ + { + "userAdded": false, + "type": "UnityEngine.AnimationClip", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.Animations.AnimatorController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.AnimatorOverrideController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.Audio.AudioMixerController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.ComputeShader", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Cubemap", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.GameObject", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.LightingDataAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": false + }, + { + "userAdded": false, + "type": "UnityEngine.LightingSettings", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Material", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.MonoScript", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicMaterial", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicsMaterial2D", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.VolumeProfile", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.SceneAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": false + }, + { + "userAdded": false, + "type": "UnityEngine.Shader", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.ShaderVariantCollection", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Texture", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Texture2D", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Timeline.TimelineAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + } + ], + "defaultDependencyTypeInfo": { + "userAdded": false, + "type": "", + "ignore": false, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + "newSceneOverride": 0 +} \ No newline at end of file diff --git a/plugins/unity/ProjectSettings/TagManager.asset b/plugins/unity/ProjectSettings/TagManager.asset new file mode 100644 index 00000000..1c92a784 --- /dev/null +++ b/plugins/unity/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/plugins/unity/ProjectSettings/TimeManager.asset b/plugins/unity/ProjectSettings/TimeManager.asset new file mode 100644 index 00000000..558a017e --- /dev/null +++ b/plugins/unity/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/plugins/unity/ProjectSettings/UnityConnectSettings.asset b/plugins/unity/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 00000000..6125b308 --- /dev/null +++ b/plugins/unity/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/plugins/unity/ProjectSettings/VFXManager.asset b/plugins/unity/ProjectSettings/VFXManager.asset new file mode 100644 index 00000000..46f38e16 --- /dev/null +++ b/plugins/unity/ProjectSettings/VFXManager.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 + m_CompiledVersion: 0 + m_RuntimeVersion: 0 diff --git a/plugins/unity/ProjectSettings/VersionControlSettings.asset b/plugins/unity/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 00000000..dca28814 --- /dev/null +++ b/plugins/unity/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/plugins/unity/ProjectSettings/XRSettings.asset b/plugins/unity/ProjectSettings/XRSettings.asset new file mode 100644 index 00000000..482590c1 --- /dev/null +++ b/plugins/unity/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/plugins/unity/ProjectSettings/boot.config b/plugins/unity/ProjectSettings/boot.config new file mode 100644 index 00000000..e69de29b diff --git a/plugins/unity/README.md b/plugins/unity/README.md new file mode 100644 index 00000000..01d3dc98 --- /dev/null +++ b/plugins/unity/README.md @@ -0,0 +1,2 @@ +# pocketpy-unity +