From 5fe61c2e1181357216cc224dd4a74d95091ed483 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 5 Dec 2022 21:43:12 +0800 Subject: [PATCH] up add unity package update gitignore up Update CHANGELOG.md --- .gitignore | 145 +- LICENSE | 426 +- plugins/flutter/.gitignore | 30 + plugins/flutter/.metadata | 36 + plugins/flutter/CHANGELOG.md | 3 + plugins/flutter/LICENSE | 165 + plugins/flutter/README.md | 201 + plugins/flutter/analysis_options.yaml | 4 + plugins/flutter/android/.gitignore | 9 + plugins/flutter/android/build.gradle | 59 + plugins/flutter/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + plugins/flutter/example/.gitignore | 44 + plugins/flutter/example/README.md | 6 + plugins/flutter/example/analysis_options.yaml | 29 + plugins/flutter/example/android/.gitignore | 13 + .../flutter/example/android/app/build.gradle | 71 + .../android/app/src/debug/AndroidManifest.xml | 8 + .../android/app/src/main/AndroidManifest.xml | 34 + .../example/pocketpy_example/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 8 + plugins/flutter/example/android/build.gradle | 31 + .../flutter/example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/example/android/settings.gradle | 11 + plugins/flutter/example/ios/.gitignore | 34 + .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 1 + .../example/ios/Flutter/Release.xcconfig | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 481 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 + .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + plugins/flutter/example/ios/Runner/Info.plist | 51 + .../ios/Runner/Runner-Bridging-Header.h | 1 + plugins/flutter/example/lib/main.dart | 113 + plugins/flutter/example/pubspec.lock | 175 + plugins/flutter/example/pubspec.yaml | 98 + plugins/flutter/example/windows/.gitignore | 17 + .../flutter/example/windows/CMakeLists.txt | 101 + .../example/windows/flutter/CMakeLists.txt | 104 + .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 24 + .../example/windows/runner/CMakeLists.txt | 39 + .../flutter/example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 61 + .../example/windows/runner/flutter_window.h | 33 + .../flutter/example/windows/runner/main.cpp | 43 + .../flutter/example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../flutter/example/windows/runner/utils.cpp | 64 + .../flutter/example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 + .../example/windows/runner/win32_window.h | 98 + plugins/flutter/ios/Classes/pocketpy.cpp | 3 + plugins/flutter/ios/pocketpy.podspec | 33 + plugins/flutter/lib/jsonrpc.dart | 146 + plugins/flutter/lib/pocketpy.dart | 188 + plugins/flutter/pubspec.yaml | 29 + plugins/flutter/src/CMakeLists.txt | 23 + plugins/flutter/src/pocketpy.cpp | 1 + plugins/flutter/src/pocketpy.h | 6989 +++++++++++++++++ plugins/flutter/windows/.gitignore | 17 + plugins/flutter/windows/CMakeLists.txt | 23 + plugins/unity/.gitattributes | 2 + plugins/unity/.gitignore | 72 + plugins/unity/.vsconfig | 6 + plugins/unity/Assets/PocketPy.meta | 8 + .../unity/Assets/PocketPy/JsonRpcServer.cs | 11 + .../Assets/PocketPy/JsonRpcServer.cs.meta | 11 + plugins/unity/Assets/PocketPy/Plugins.meta | 8 + .../Assets/PocketPy/Plugins/Android.meta | 8 + .../Assets/PocketPy/Plugins/Windows.meta | 8 + .../PocketPy/Plugins/Windows/pocketpy.dll | Bin 0 -> 653312 bytes .../unity/Assets/PocketPy/Plugins/iOS.meta | 8 + plugins/unity/Assets/PocketPy/Tests.meta | 8 + plugins/unity/Assets/PocketPy/Tests/A.cs | 23 + plugins/unity/Assets/PocketPy/Tests/A.cs.meta | 11 + plugins/unity/Assets/PocketPy/pocketpy.cs | 207 + .../unity/Assets/PocketPy/pocketpy.cs.meta | 11 + plugins/unity/Assets/Scenes.meta | 8 + plugins/unity/Assets/Scenes/SampleScene.unity | 222 + .../Assets/Scenes/SampleScene.unity.meta | 7 + plugins/unity/LICENSE | 165 + plugins/unity/Packages/manifest.json | 45 + plugins/unity/Packages/packages-lock.json | 483 ++ .../unity/ProjectSettings/AudioManager.asset | 19 + .../ProjectSettings/ClusterInputManager.asset | 6 + .../ProjectSettings/DynamicsManager.asset | 37 + .../ProjectSettings/EditorBuildSettings.asset | 11 + .../ProjectSettings/EditorSettings.asset | 40 + .../ProjectSettings/GraphicsSettings.asset | 64 + .../unity/ProjectSettings/InputManager.asset | 487 ++ .../ProjectSettings/MemorySettings.asset | 35 + .../unity/ProjectSettings/NavMeshAreas.asset | 93 + .../ProjectSettings/NetworkManager.asset | 8 + .../PackageManagerSettings.asset | 44 + .../ProjectSettings/Physics2DSettings.asset | 56 + .../unity/ProjectSettings/PresetManager.asset | 7 + .../ProjectSettings/ProjectSettings.asset | 669 ++ .../unity/ProjectSettings/ProjectVersion.txt | 2 + .../ProjectSettings/QualitySettings.asset | 239 + .../SceneTemplateSettings.json | 167 + .../unity/ProjectSettings/TagManager.asset | 43 + .../unity/ProjectSettings/TimeManager.asset | 9 + .../UnityConnectSettings.asset | 35 + .../unity/ProjectSettings/VFXManager.asset | 14 + .../VersionControlSettings.asset | 8 + .../unity/ProjectSettings/XRSettings.asset | 10 + plugins/unity/ProjectSettings/boot.config | 0 plugins/unity/README.md | 2 + 150 files changed, 14227 insertions(+), 444 deletions(-) create mode 100644 plugins/flutter/.gitignore create mode 100644 plugins/flutter/.metadata create mode 100644 plugins/flutter/CHANGELOG.md create mode 100644 plugins/flutter/LICENSE create mode 100644 plugins/flutter/README.md create mode 100644 plugins/flutter/analysis_options.yaml create mode 100644 plugins/flutter/android/.gitignore create mode 100644 plugins/flutter/android/build.gradle create mode 100644 plugins/flutter/android/settings.gradle create mode 100644 plugins/flutter/android/src/main/AndroidManifest.xml create mode 100644 plugins/flutter/example/.gitignore create mode 100644 plugins/flutter/example/README.md create mode 100644 plugins/flutter/example/analysis_options.yaml create mode 100644 plugins/flutter/example/android/.gitignore create mode 100644 plugins/flutter/example/android/app/build.gradle create mode 100644 plugins/flutter/example/android/app/src/debug/AndroidManifest.xml create mode 100644 plugins/flutter/example/android/app/src/main/AndroidManifest.xml create mode 100644 plugins/flutter/example/android/app/src/main/kotlin/com/example/pocketpy_example/MainActivity.kt create mode 100644 plugins/flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 plugins/flutter/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 plugins/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 plugins/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 plugins/flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 plugins/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 plugins/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 plugins/flutter/example/android/app/src/main/res/values-night/styles.xml create mode 100644 plugins/flutter/example/android/app/src/main/res/values/styles.xml create mode 100644 plugins/flutter/example/android/app/src/profile/AndroidManifest.xml create mode 100644 plugins/flutter/example/android/build.gradle create mode 100644 plugins/flutter/example/android/gradle.properties create mode 100644 plugins/flutter/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 plugins/flutter/example/android/settings.gradle create mode 100644 plugins/flutter/example/ios/.gitignore create mode 100644 plugins/flutter/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 plugins/flutter/example/ios/Flutter/Debug.xcconfig create mode 100644 plugins/flutter/example/ios/Flutter/Release.xcconfig create mode 100644 plugins/flutter/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 plugins/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 plugins/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 plugins/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 plugins/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 plugins/flutter/example/ios/Runner/AppDelegate.swift create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 plugins/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 plugins/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 plugins/flutter/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 plugins/flutter/example/ios/Runner/Info.plist create mode 100644 plugins/flutter/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 plugins/flutter/example/lib/main.dart create mode 100644 plugins/flutter/example/pubspec.lock create mode 100644 plugins/flutter/example/pubspec.yaml create mode 100644 plugins/flutter/example/windows/.gitignore create mode 100644 plugins/flutter/example/windows/CMakeLists.txt create mode 100644 plugins/flutter/example/windows/flutter/CMakeLists.txt create mode 100644 plugins/flutter/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 plugins/flutter/example/windows/flutter/generated_plugin_registrant.h create mode 100644 plugins/flutter/example/windows/flutter/generated_plugins.cmake create mode 100644 plugins/flutter/example/windows/runner/CMakeLists.txt create mode 100644 plugins/flutter/example/windows/runner/Runner.rc create mode 100644 plugins/flutter/example/windows/runner/flutter_window.cpp create mode 100644 plugins/flutter/example/windows/runner/flutter_window.h create mode 100644 plugins/flutter/example/windows/runner/main.cpp create mode 100644 plugins/flutter/example/windows/runner/resource.h create mode 100644 plugins/flutter/example/windows/runner/resources/app_icon.ico create mode 100644 plugins/flutter/example/windows/runner/runner.exe.manifest create mode 100644 plugins/flutter/example/windows/runner/utils.cpp create mode 100644 plugins/flutter/example/windows/runner/utils.h create mode 100644 plugins/flutter/example/windows/runner/win32_window.cpp create mode 100644 plugins/flutter/example/windows/runner/win32_window.h create mode 100644 plugins/flutter/ios/Classes/pocketpy.cpp create mode 100644 plugins/flutter/ios/pocketpy.podspec create mode 100644 plugins/flutter/lib/jsonrpc.dart create mode 100644 plugins/flutter/lib/pocketpy.dart create mode 100644 plugins/flutter/pubspec.yaml create mode 100644 plugins/flutter/src/CMakeLists.txt create mode 100644 plugins/flutter/src/pocketpy.cpp create mode 100644 plugins/flutter/src/pocketpy.h create mode 100644 plugins/flutter/windows/.gitignore create mode 100644 plugins/flutter/windows/CMakeLists.txt create mode 100644 plugins/unity/.gitattributes create mode 100644 plugins/unity/.gitignore create mode 100644 plugins/unity/.vsconfig create mode 100644 plugins/unity/Assets/PocketPy.meta create mode 100644 plugins/unity/Assets/PocketPy/JsonRpcServer.cs create mode 100644 plugins/unity/Assets/PocketPy/JsonRpcServer.cs.meta create mode 100644 plugins/unity/Assets/PocketPy/Plugins.meta create mode 100644 plugins/unity/Assets/PocketPy/Plugins/Android.meta create mode 100644 plugins/unity/Assets/PocketPy/Plugins/Windows.meta create mode 100644 plugins/unity/Assets/PocketPy/Plugins/Windows/pocketpy.dll create mode 100644 plugins/unity/Assets/PocketPy/Plugins/iOS.meta create mode 100644 plugins/unity/Assets/PocketPy/Tests.meta create mode 100644 plugins/unity/Assets/PocketPy/Tests/A.cs create mode 100644 plugins/unity/Assets/PocketPy/Tests/A.cs.meta create mode 100644 plugins/unity/Assets/PocketPy/pocketpy.cs create mode 100644 plugins/unity/Assets/PocketPy/pocketpy.cs.meta create mode 100644 plugins/unity/Assets/Scenes.meta create mode 100644 plugins/unity/Assets/Scenes/SampleScene.unity create mode 100644 plugins/unity/Assets/Scenes/SampleScene.unity.meta create mode 100644 plugins/unity/LICENSE create mode 100644 plugins/unity/Packages/manifest.json create mode 100644 plugins/unity/Packages/packages-lock.json create mode 100644 plugins/unity/ProjectSettings/AudioManager.asset create mode 100644 plugins/unity/ProjectSettings/ClusterInputManager.asset create mode 100644 plugins/unity/ProjectSettings/DynamicsManager.asset create mode 100644 plugins/unity/ProjectSettings/EditorBuildSettings.asset create mode 100644 plugins/unity/ProjectSettings/EditorSettings.asset create mode 100644 plugins/unity/ProjectSettings/GraphicsSettings.asset create mode 100644 plugins/unity/ProjectSettings/InputManager.asset create mode 100644 plugins/unity/ProjectSettings/MemorySettings.asset create mode 100644 plugins/unity/ProjectSettings/NavMeshAreas.asset create mode 100644 plugins/unity/ProjectSettings/NetworkManager.asset create mode 100644 plugins/unity/ProjectSettings/PackageManagerSettings.asset create mode 100644 plugins/unity/ProjectSettings/Physics2DSettings.asset create mode 100644 plugins/unity/ProjectSettings/PresetManager.asset create mode 100644 plugins/unity/ProjectSettings/ProjectSettings.asset create mode 100644 plugins/unity/ProjectSettings/ProjectVersion.txt create mode 100644 plugins/unity/ProjectSettings/QualitySettings.asset create mode 100644 plugins/unity/ProjectSettings/SceneTemplateSettings.json create mode 100644 plugins/unity/ProjectSettings/TagManager.asset create mode 100644 plugins/unity/ProjectSettings/TimeManager.asset create mode 100644 plugins/unity/ProjectSettings/UnityConnectSettings.asset create mode 100644 plugins/unity/ProjectSettings/VFXManager.asset create mode 100644 plugins/unity/ProjectSettings/VersionControlSettings.asset create mode 100644 plugins/unity/ProjectSettings/XRSettings.asset create mode 100644 plugins/unity/ProjectSettings/boot.config create mode 100644 plugins/unity/README.md 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 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e3d9ebc44429bc1a5c7631917dad3d7740c0ac38 GIT binary patch literal 653312 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

fV^5pP60|BV1z9bkd_Fed|bk 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�i5wr9jey>1e7h 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%0AEf?3NUNXJ@=O(}G*z-%`+^?c}&-F-0r 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

i-uo*vMe;l|748{-C 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⁡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 + } + + 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 +