diff --git a/ReadMe.md b/ReadMe.md index 10efdce8..74e23676 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -209,6 +209,7 @@ The Flipper and its community wouldn't be as rich as it is without your contribu | ICM42688 Air Mouse | ![GPIO Badge] | [by nminaylov](https://github.com/flipperdevices/flipperzero-good-faps/pull/83/files) | read more details in [original repo](https://github.com/flipperdevices/flipperzero-good-faps/pull/83/files) | ![None Badge] | | WS2812B LED Tester | ![GPIO Badge] | [by jamisonderek](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/gpio/ws2812b_tester) | read more details in [original repo](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/gpio/ws2812b_tester) | ![None Badge] | | W5500 Ethernet Tester | ![GPIO Badge] | [by karasevia](https://github.com/karasevia/finik_eth) | [with fixes by arag0re](https://github.com/arag0re/fz-eth-troubleshooter/tree/hexUiFix) read more details in original repo | ![None Badge] | +| Simultaneous UHF RFID | ![GPIO Badge] | [by haffnerriley](https://github.com/haffnerriley/Simultaneous-UHF-RFID-FlipperZero) | read more details in original repo | [![Author Badge]](https://lab.flipper.net/apps/simultaneous_rfid_reader) | | IR Remote | ![IR Badge] | [by Hong5489](https://github.com/Hong5489/ir_remote) | improvements [by friebel](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/535) - Hold Option, RAW support [by d4ve10](https://github.com/d4ve10/ir_remote/tree/infrared_hold_option) | ![None Badge] | | IR Intervalometer | ![IR Badge] | [by Nitepone](https://github.com/Nitepone/flipper-intervalometer) | | [![UFW Badge]](https://lab.flipper.net/apps/sony_intervalometer) | | IR Xbox Controller | ![IR Badge] | [by gebeto](https://github.com/gebeto/flipper-xbox-controller) | | [![Author Badge]](https://lab.flipper.net/apps/xbox_controller) | @@ -263,6 +264,7 @@ The Flipper and its community wouldn't be as rich as it is without your contribu | DCF77 Clock Sppof | ![Tools Badge] | [by molodos](https://github.com/molodos/dcf77-clock-spoof) | fork of [dcf77-clock-sync](https://github.com/mdaskalov/dcf77-clock-sync) | ![None Badge] | | Quac! Remote | ![Tools Badge] | [by rdefeo](https://github.com/rdefeo/quac) | Various fixes by @Willy-JL | [![Author Badge]](https://lab.flipper.net/apps/quac) | | Key Copier | ![Tools Badge] | [by zinongli](https://github.com/zinongli/KeyCopier) | | ![None Badge] | +| uPython | ![Tools Badge] | [by ofabel](https://github.com/ofabel/mp-flipper) | read more details in original repo | [![Author Badge]](https://lab.flipper.net/apps/upython) | | USB HID Autofire | ![USB Badge] | [by pbek](https://github.com/pbek/usb_hid_autofire) | | ![None Badge] | | USB Consumer Control | ![USB Badge] | [by WithSecureLabs](https://github.com/WithSecureLabs/usb-consumer-control/tree/main) | | ![None Badge] | | HID File Transfer | ![USB Badge] | [by Kavakuo](https://github.com/Kavakuo/HID-File-Transfer) | Get client app in [original repo](https://github.com/Kavakuo/HID-File-Transfer) | ![None Badge] | diff --git a/non_catalog_apps/mp_flipper/.gitignore b/non_catalog_apps/mp_flipper/.gitignore new file mode 100644 index 00000000..69da3480 --- /dev/null +++ b/non_catalog_apps/mp_flipper/.gitignore @@ -0,0 +1,10 @@ +/dist/ +/venv/ +/flipperzero/__init__.py +.vscode +.clang-format +.clangd +.editorconfig +.env +.ufbt +__pycache__ \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/CHANGELOG.md b/non_catalog_apps/mp_flipper/CHANGELOG.md new file mode 100644 index 00000000..63455614 --- /dev/null +++ b/non_catalog_apps/mp_flipper/CHANGELOG.md @@ -0,0 +1,130 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.3.0] + +### Added + +* Simple ADC support for the `flipperzero` module: + * Read raw value. + * Read voltage. +* Simple PWM support for the `flipperzero` module: + * Start a signal. + * Stop a signal. + * Check the status. +* Infrared support for the `flipperzero` module: + * Receive a signal. + * Transmit a signal. + * Check the status. +* Reset used GPIO pins upon script termination. +* Improved GPIO related functions to prevent user errors. +* Published [Python package on PyPI](https://pypi.org/project/flipperzero/) for code completion support. + +### Changed + +* The GPIO init function `flipperzero.gpio_init_pin` returns a boolean value. + +## [1.2.0] - 2024-09-05 + +### Added + +* Constants for all musical notes from C0 up to B8. +* Constants for minimum and maximum speaker volumes. +* Simple GPIO support for the `flipperzero` module: + * Initialize a pin. + * Read from a pin. + * Write to a pin. + * Handle interrupts. + +### Fixed + +* Message box alignment parameters `h` and `v` are now correctly evaluated. + +## [1.1.0] - 2024-08-28 + +### Added + +* Display splash screen upon application start. +* API documentation on [GitHub pages](https://ofabel.github.io/mp-flipper/). + +## [1.0.0] - 2024-08-22 + +### Added + +* First stable release on the [application catalog](https://github.com/flipperdevices/flipper-application-catalog). + +### Changed + +* Application ID is now `upython` + +## [0.5.0-beta.1] - 2024-08-04 + +### Added + +* Message dialog support. +* Update to the latest 0.104.0 firmware. + +### Removed + +* Disabled various Python builtins to shrink binary size. + +## [0.4.0-beta.1] - 2024-04-14 + +### Added + +* [Library](https://github.com/ofabel/mp-flipper/tree/lib) to include in the [firmware repository](https://github.com/ofabel/flipperzero-firmware). +* All generated files from the build prozess are now [part of the repository](https://github.com/ofabel/mp-flipper/tree/lib-release). +* Enabled split heap support for MicroPython: + * The runtime can allocate and free heap memory. + * Allows to start the Python process with small heap. +* Enabled scheduler support (required for interrupt handling). +* Enabled support for module `__init__` functions. +* Stabilized `flipperzero` module API: + * Canvas support has now a proper implementation. + * Interrupts from buttons are supported. + +## [0.3.0-alpha.1] - 2024-04-04 + +### Added + +* Floating point support +* Extend `flipperzero` module with support for: + * Speaker, set volume and frequency + * Canvas, very wacky implementation + +## [0.2.0-alpha.1] - 2024-04-03 + +### Added + +* Support for external imports +* Python `time` module support +* Python `random` module support +* Basic `flipperzero` module with support for: + * Vibration + * LED + * Backlight +* Some test Python scripts + +## [0.1.0-alpha.1] - 2024-04-01 + +### Added + +* Basic build setup +* Minimal working example + +[Unreleased]: https://github.com/ofabel/mp-flipper/compare/v1.3.0...HEAD +[1.3.0]: https://github.com/ofabel/mp-flipper/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/ofabel/mp-flipper/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/ofabel/mp-flipper/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/ofabel/mp-flipper/compare/v0.5.0-beta.1...v1.0.0 +[0.5.0-beta.1]: https://github.com/ofabel/mp-flipper/compare/v0.4.0-beta.1...v0.5.0-beta.1 +[0.4.0-beta.1]: https://github.com/ofabel/mp-flipper/compare/v0.3.0-alpha.1...v0.4.0-beta.1 +[0.3.0-alpha.1]: https://github.com/ofabel/mp-flipper/compare/v0.2.0-alpha.1...v0.3.0-alpha.1 +[0.2.0-alpha.1]: https://github.com/ofabel/mp-flipper/compare/v0.1.0-alpha.1...v0.2.0-alpha.1 +[0.1.0-alpha.1]: https://github.com/ofabel/mp-flipper/releases/tag/v0.1.0-alpha.1 diff --git a/non_catalog_apps/mp_flipper/LICENSE.txt b/non_catalog_apps/mp_flipper/LICENSE.txt new file mode 100644 index 00000000..06e2b588 --- /dev/null +++ b/non_catalog_apps/mp_flipper/LICENSE.txt @@ -0,0 +1,22 @@ +MIT License +=========== + +Copyright (c) 2024 Oliver Fabel + +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. \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/Makefile b/non_catalog_apps/mp_flipper/Makefile new file mode 100644 index 00000000..8ec41b71 --- /dev/null +++ b/non_catalog_apps/mp_flipper/Makefile @@ -0,0 +1,33 @@ +.PHONY: update +update: + git submodule update --remote lib/micropython && git add lib/micropython + +.PHONY: build-fap +build-fap: update + ufbt build + +.PHONY: launch +launch: build-fap + ufbt launch + +.PHONY: clean +clean: + ufbt -c + +.PHONY: build-pages +build-pages: + rm -rf ./dist/pages ./flipperzero/__init__.py + cat ./flipperzero/_*.py > ./flipperzero/__init__.py + source venv/bin/activate && sphinx-build docs/pages dist/pages + +.PHONY: publish-pages +publish-pages: build-pages + ./publish.sh pages + +.PHONY: build-python +build-python: + source venv/bin/activate && hatch build + +.PHONY: publish-python +publish-python: build-python + source venv/bin/activate && hatch publish dist/python/* diff --git a/non_catalog_apps/mp_flipper/README.md b/non_catalog_apps/mp_flipper/README.md new file mode 100644 index 00000000..eae5e055 --- /dev/null +++ b/non_catalog_apps/mp_flipper/README.md @@ -0,0 +1,64 @@ +![License](https://img.shields.io/github/license/ofabel/mp-flipper) +![Version](https://img.shields.io/github/v/tag/ofabel/mp-flipper) +![](https://img.shields.io/github/issues/ofabel/mp-flipper) + +# MicroPython Flipper Zero + +The application is now available on the official [Flipper Lab](https://lab.flipper.net/apps/upython). +For more information on how to programm your Flipper with Python, check out the [documentation](https://ofabel.github.io/mp-flipper/) on GitHub pages. + +This branch contains the [FAP](https://developer.flipper.net/flipperzero/doxygen/apps_on_sd_card.html) version of the [MicroPython](https://micropython.org/) support for the famous [Flipper Zero](https://flipperzero.one/) gadget. +The results of the preceding research phase is still available in the [poc](https://github.com/ofabel/mp-flipper/tree/poc) branch. +The [lib](https://github.com/ofabel/mp-flipper/tree/lib) branch of this repository contains just the MicroPython library. +The progress of further research on what can be achieved when moving functionality to the firmware can be found in the [fork of the original firmware](https://github.com/ofabel/flipperzero-firmware/tree/ofa/micropython). + +## Usage + +Just place your Python files somewhere on the SD card (e.g. by using the [qFlipper](https://flipperzero.one/downloads) app). + +The application just starts with an open file browser: + +![](./assets/file-browser.png) + +Here you can select any Python file to compile and execute from the SD card: + +![](./assets/tic-tac-toe.png) + +## Disclaimer + +This FAP version requires about 80 kB from SRAM to start (needed for the Python runtime and compiler). +Due to memory fragmentation it's possible, that the application crashes when you start it. +If this happens, just try again (the crash doesn't harm your device). + +Sadly, REPL support is only available in fhe [firmware fork](https://github.com/ofabel/flipperzero-firmware/tree/ofa/micropython) version. + +## Setup and Build + +This section is only relevant, if you want to build the FAP on your own. + +### Requirements + +* [Git](https://git-scm.com/) +* [Make](https://www.gnu.org/software/make/) +* [uFBT](https://pypi.org/project/ufbt/) available in your `PATH` (or you have to adjust the [Makefile](./Makefile)) +* [Flipper Zero](https://flipperzero.one/) + +### Setup + +```bash +git clone --recurse-submodules git@github.com:ofabel/mp-flipper.git +``` + +### Build + +Just open a terminal and run the Makefile targets: + +```bash +make build +``` + +You can also build an launch the application on the attached Flipper Zero device in one command: + +```bash +make launch +``` diff --git a/non_catalog_apps/mp_flipper/application.fam b/non_catalog_apps/mp_flipper/application.fam new file mode 100644 index 00000000..3231b3e8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/application.fam @@ -0,0 +1,60 @@ +App( + appid="upython", + name="uPython", + apptype=FlipperAppType.EXTERNAL, + entry_point="upython", + stack_size=4 * 1024, + fap_category="Tools", + fap_version="1.3", + fap_description="Compile and execute MicroPython scripts", + fap_icon="icon.png", + fap_icon_assets="images", + fap_author="Oliver Fabel", + fap_file_assets="examples", + fap_weburl="https://github.com/ofabel/mp-flipper", + sources=[ + "*.c*", + "!./lib/micropython", + "!./lib/micropython-port", + "!./docs/pages", + "!./flipperzero", + "!./venv", + "!./dist", + ], + fap_private_libs=[ + Lib( + name="micropython", + cflags=[ + "-Wno-error", + "-w", + # + # required for floating point support + # + "-mcpu=cortex-m4", + "-mfloat-abi=hard", + "-mfpu=fpv4-sp-d16", + "-mthumb", + "-fsingle-precision-constant", + "-fno-math-errno", + ], + cincludes=["."] + ), + Lib( + name="micropython-port", + cflags=[ + "-Wno-error", + "-w", + # + # required for floating point support + # + "-mcpu=cortex-m4", + "-mfloat-abi=hard", + "-mfpu=fpv4-sp-d16", + "-mthumb", + "-fsingle-precision-constant", + "-fno-math-errno", + ], + cincludes=["."] + ), + ] +) diff --git a/non_catalog_apps/mp_flipper/assets/file-browser.png b/non_catalog_apps/mp_flipper/assets/file-browser.png new file mode 100644 index 00000000..2c95d6fa Binary files /dev/null and b/non_catalog_apps/mp_flipper/assets/file-browser.png differ diff --git a/non_catalog_apps/mp_flipper/assets/qflipper.png b/non_catalog_apps/mp_flipper/assets/qflipper.png new file mode 100644 index 00000000..30d0ccca Binary files /dev/null and b/non_catalog_apps/mp_flipper/assets/qflipper.png differ diff --git a/non_catalog_apps/mp_flipper/assets/tic-tac-toe.png b/non_catalog_apps/mp_flipper/assets/tic-tac-toe.png new file mode 100644 index 00000000..eea89b1f Binary files /dev/null and b/non_catalog_apps/mp_flipper/assets/tic-tac-toe.png differ diff --git a/non_catalog_apps/mp_flipper/docs/CHANGELOG.md b/non_catalog_apps/mp_flipper/docs/CHANGELOG.md new file mode 100644 index 00000000..b66195ad --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/CHANGELOG.md @@ -0,0 +1,24 @@ +## 1.3 + +* Added simple ADC support: read value and voltage. +* Added simple PWM support: start, stop, check status. +* Added infrared support: receive and transmit a signal, check status. +* Added success indicator to GPIO init function. +* Reset used GPIO pins upon script termination. +* Improved GPIO related functions to prevent user errors. +* Published Python package on PyPI for code completion support. + +## 1.2 + +* Added simple GPIO support: initialize, read, write, interrupts. +* Added constants for musical note frequencies from C0 up to B8. +* Some minor fixes in the dialog functions. + +## 1.1 + +* Display splash screen upon application start. +* API documentation available on GitHub pages. + +## 1.0 + +* Initial stable release. diff --git a/non_catalog_apps/mp_flipper/docs/README.md b/non_catalog_apps/mp_flipper/docs/README.md new file mode 100644 index 00000000..fd88d4fb --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/README.md @@ -0,0 +1,15 @@ +# MicroPython + +MicroPython is a version of the popular Python programming language, especially tailored for the hardware requirements of small microcontrollers, like the Flipper Zero. +Instead of supporting the full standard library, only a subset is implemented, carefully selected with the requirements of a microcontroller environment in mind. +But this should not limit your creativity and ability to create great applications at all. + +## API + +Visit the repository website on GitHub or scan the QR code on the welcome screen. + +## Disclaimer + +Running MicroPython is a heavy task for the Flipper. +This does sometimes lead to an _Out of Memory_ error. +**This doesn't harm your Flipper.** diff --git a/non_catalog_apps/mp_flipper/docs/file-browser.png b/non_catalog_apps/mp_flipper/docs/file-browser.png new file mode 100644 index 00000000..2c95d6fa Binary files /dev/null and b/non_catalog_apps/mp_flipper/docs/file-browser.png differ diff --git a/non_catalog_apps/mp_flipper/docs/pages/assets/adc_circuit.svg b/non_catalog_apps/mp_flipper/docs/pages/assets/adc_circuit.svg new file mode 100644 index 00000000..5613cbcd --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/assets/adc_circuit.svg @@ -0,0 +1,846 @@ + + +Flipper Zero +Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +13 + + + + + + + + + + + + + + +4 + + + + + + + + + + + + + + +5 + + + + + + + + + + + + + + +7 + + + + + + + + + + + + + + +8 + + + + + + + + + + + + + + +6 + + + + + + + + + + + + + + +10 + + + + + + + + + + + + + + +17 + + + + + + + + + + + + + + +15 + + + + + + + + + + + + + + +9 + + + + + + + + + + + + + + +14 + + + + + + + + + + + + + + +11 + + + + + + + + + + + + + + +2 + + + + + + + + + + + + + + +16 + + + + + + + + + + + + + + +12 + + + + + + + + + + + + + + +18 + + + + + + + + + + + + + + +3 + + + + + + + + + + + + + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +S1 + + + + + + + + + + + + + +J1 + + + + + + + + + + + + + +R2 + + + + + + + + + + + + + +3.3k + + + + + + + + + + + + + +R1 + + + + + + + + + + + + + +10k + + + + + + + + + + + + + + + + + + + + + + + + + + +GND + + + + + + + + + + + + + +C1 + + + + + + + + + + + + + +3V3 + + + + + + + + + + + + + diff --git a/non_catalog_apps/mp_flipper/docs/pages/assets/favicon.png b/non_catalog_apps/mp_flipper/docs/pages/assets/favicon.png new file mode 100644 index 00000000..3ac49131 Binary files /dev/null and b/non_catalog_apps/mp_flipper/docs/pages/assets/favicon.png differ diff --git a/non_catalog_apps/mp_flipper/docs/pages/assets/gpio_interrupt_circuit.svg b/non_catalog_apps/mp_flipper/docs/pages/assets/gpio_interrupt_circuit.svg new file mode 100644 index 00000000..ee834581 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/assets/gpio_interrupt_circuit.svg @@ -0,0 +1,1024 @@ + + +Flipper Zero +Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +5 + + + + + + + + + + + + + + +2 + + + + + + + + + + + + + + +1 + + + + + + + + + + + + + + +17 + + + + + + + + + + + + + + +15 + + + + + + + + + + + + + + +11 + + + + + + + + + + + + + + +18 + + + + + + + + + + + + + + +12 + + + + + + + + + + + + + + +8 + + + + + + + + + + + + + + +16 + + + + + + + + + + + + + + +9 + + + + + + + + + + + + + + +13 + + + + + + + + + + + + + + +7 + + + + + + + + + + + + + + +6 + + + + + + + + + + + + + + +4 + + + + + + + + + + + + + + +10 + + + + + + + + + + + + + + +3 + + + + + + + + + + + + + + +14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +R2 + + + + + + + + + + + + + +220 + + + + + + + + + + + + + +J1 + + + + + + + + + + + + + +S2 + + + + + + + + + + + + + +R1 + + + + + + + + + + + + + +1k + + + + + + + + + + + + + +S1 + + + + + + + + + + + + + +LED1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +C1 + + + + + + + + + + + + + +GND + + + + + + + + + + + + + +C0 + + + + + + + + + + + + + +A7 + + + + + + + + + + + + + +GND + + + + + + + + + + + + + diff --git a/non_catalog_apps/mp_flipper/docs/pages/assets/logo.png b/non_catalog_apps/mp_flipper/docs/pages/assets/logo.png new file mode 100644 index 00000000..8a4a9e90 Binary files /dev/null and b/non_catalog_apps/mp_flipper/docs/pages/assets/logo.png differ diff --git a/non_catalog_apps/mp_flipper/docs/pages/assets/pwm_circuit.svg b/non_catalog_apps/mp_flipper/docs/pages/assets/pwm_circuit.svg new file mode 100644 index 00000000..45fe2ad7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/assets/pwm_circuit.svg @@ -0,0 +1,772 @@ + + +Flipper Zero +Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +17 + + + + + + + + + + + + + + +9 + + + + + + + + + + + + + + +3 + + + + + + + + + + + + + + +14 + + + + + + + + + + + + + + +6 + + + + + + + + + + + + + + +13 + + + + + + + + + + + + + + +5 + + + + + + + + + + + + + + +15 + + + + + + + + + + + + + + +2 + + + + + + + + + + + + + + +16 + + + + + + + + + + + + + + +4 + + + + + + + + + + + + + + +8 + + + + + + + + + + + + + + +10 + + + + + + + + + + + + + + +18 + + + + + + + + + + + + + + +12 + + + + + + + + + + + + + + +11 + + + + + + + + + + + + + + +1 + + + + + + + + + + + + + + +7 + + + + + + + + + + + + + +R1 + + + + + + + + + + + + + +1k + + + + + + + + + + + + + +LED1 + + + + + + + + + + + + + +J1 + + + + + + + + + + + + + + + + + + +A7 + + + + + + + + + + + + + +GND + + + + + + + + + + + + + diff --git a/non_catalog_apps/mp_flipper/docs/pages/assets/pwm_signal.txt b/non_catalog_apps/mp_flipper/docs/pages/assets/pwm_signal.txt new file mode 100644 index 00000000..24bb45d7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/assets/pwm_signal.txt @@ -0,0 +1,5 @@ +────┐ ┌──┐ ┌────┐ ┌──┐ ┌────┐ ┌ ⋅ ⋅ ⋅ + │ │ │ │ │ │ │ │ │ │ + └───────┘ └────┘ └──────┘ └──────────────┘ └──────┘ + + 40 | 70 |20| 40 | 40 | 60 |20| 140 | 40 | 60 | ⋅ ⋅ ⋅ diff --git a/non_catalog_apps/mp_flipper/docs/pages/changelog.rst b/non_catalog_apps/mp_flipper/docs/pages/changelog.rst new file mode 100644 index 00000000..d29e3da8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/changelog.rst @@ -0,0 +1,2 @@ +.. include:: ../../CHANGELOG.md + :parser: myst_parser.sphinx_ \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/docs/pages/conf.py b/non_catalog_apps/mp_flipper/docs/pages/conf.py new file mode 100644 index 00000000..76fe9ed4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/conf.py @@ -0,0 +1,63 @@ +import datetime +import pathlib +import sys + +base = pathlib.Path(__file__).parent.parent.parent +root = base.__str__() +flipperzero = base.joinpath('flipperzero').__str__() +now = datetime.datetime.now() + +sys.path.append(root) +sys.path.append(flipperzero) + +project = 'uPython' +copyright = str(now.year) + ', Oliver Fabel' +author = 'Oliver Fabel' +release = '1.1.0' +version = '1.1' +language = 'en' + +extensions = [ + 'sphinx.ext.autodoc', + 'myst_parser' +] +source_suffix = { + '.rst': 'restructuredtext', + '.md': 'markdown' +} + +templates_path = [ + 'templates' +] +exclude_patterns = [] +include_patterns = [ + '**' +] + +html_theme = 'alabaster' +html_theme_options = { + 'show_powered_by': False, + 'extra_nav_links': { + 'Source Code': 'https://www.github.com/ofabel/mp-flipper', + 'Bugtracker': 'https://www.github.com/ofabel/mp-flipper/issues', + 'Releases': 'https://lab.flipper.net/apps/upython' + + } +} +html_scaled_image_link = False +html_copy_source = False +html_show_copyright = False +html_show_sphinx = False +html_static_path = [ + 'static' +] +html_logo = 'assets/logo.png' +html_favicon = 'assets/favicon.png' + +autodoc_default_options = { + 'member-order': 'bysource', +} + +add_module_names = True + +maximum_signature_line_length = 50 diff --git a/non_catalog_apps/mp_flipper/docs/pages/examples.rst b/non_catalog_apps/mp_flipper/docs/pages/examples.rst new file mode 100644 index 00000000..ce11da16 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/examples.rst @@ -0,0 +1,75 @@ +Examples +======== + +This page contains a few examples. +See more on `GitHub `_. + +Speaker +------- + +.. literalinclude:: ../../examples/flipperzero_speaker_test.py + :language: python + +For details, see the :ref:`reference-speaker` section on the :doc:`reference` page. + +Input +----- + +.. literalinclude:: ../../examples/flipperzero_draw_on_input_test.py + :language: python + +For details, see the :ref:`reference-input` section on the :doc:`reference` page. + +Interrupts +---------- + +.. literalinclude:: ../../examples/flipperzero_gpio_interrupt_test.py + :language: python + +This example drives an external LED upon interrupts: A rising edge on ``C0`` sets the pin ``A7`` to high, a rising edge on ``C1`` sets the pin ``A7`` to low. +The following schematic circuit diagram shows the hardware setup for this example: + +.. figure:: ./assets/gpio_interrupt_circuit.svg + :width: 90% + + Hardware setup for the GPIO interrupt example. + +For details, see the :ref:`reference-gpio` section on the :doc:`reference` page. + +ADC +--- + +.. literalinclude:: ../../examples/flipperzero_adc_test.py + :language: python + +This example uses a voltage divider with the 3.3 V source from pin 9. The switch ``S1`` changes the input voltage on ``C1`` between 0 and about 0.8 V. + +.. figure:: ./assets/adc_circuit.svg + :width: 90% + + Hardware setup for the ADC example. + +For details, see the :ref:`reference-adc` section on the :doc:`reference` page. + +PWM +--- + +.. literalinclude:: ../../examples/flipperzero_pwm_test.py + :language: python + +This example drives an LED connected to pin ``A7`` and ``GND`` using a PWM signal with two different frequency and duty cycle settings. + +.. figure:: ./assets/pwm_circuit.svg + :width: 90% + + Hardware setup for the PWM example. + +For details, see the :ref:`reference-pwm` section on the :doc:`reference` page. + +Infrared +-------- + +.. literalinclude:: ../../examples/flipperzero_infrared_test.py + :language: python + +For details, see the :ref:`reference-infrared` section on the :doc:`reference` page. diff --git a/non_catalog_apps/mp_flipper/docs/pages/features.md b/non_catalog_apps/mp_flipper/docs/pages/features.md new file mode 100644 index 00000000..23e4e24e --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/features.md @@ -0,0 +1,70 @@ +# Features + +Adding Python support to the Flipper Zero platform was only possible by rigorously sorting unnecessary language features. +So here is a detailed list of all supported and unsupported Python language features. + +## Supported + +The following features are enabled and supported by the interpreter: + +* Garbage collector is enabled. +* The `__file__` constant. +* Import of external files from the SD card. +* Read and write files from and to the SD card. +* The `time` module. +* The `random` module. +* The `float` data type. +* Support for [decorator](https://docs.python.org/3/glossary.html#term-decorator) functions. +* The `setattr` function. +* The `filter` function. +* The `reversed` function. +* The `min` and `max` function. +* Module-level `__init__` imports. + +## Unsupported + +The following features are disabled and _not_ supported by the interpreter: + +* Finaliser calls in the garbage collector (e.g. `__del__`). +* The `__doc__` constants. +* Source code line numbers in exceptions. +* Support for a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop). +* The `cmath` module. +* The `complex` data type. +* Support for multiple inheritance. +* Module-level `__getattr__` support according to [PEP 562](https://peps.python.org/pep-0562/). +* Support for the descriptors `__get__`, `__set__` and `__delete__`. +* Coroutines with `async` and `await` functions. +* The `:=` assign expression. +* Non-standard `.pend_throw()` method for generators. +* Support for `bytes.hex` and `bytes.fromhex`. +* Support for unicode characters. +* The string functions `.center`, `.count`, `.partition`, `.rpartition` and `.splitlines`. +* The `%` string formatting operator (use `.format` instead). +* The `bytearray` data type. +* The `memoryview` data type. +* The `slice` object. +* The `frozenset` object. +* The `property` decorator. +* The `range` function with `start`, `stop` and `step` attributes. +* The `next` function with a second argument. +* The `round` function with integers. +* All special methods for user classes (e.g. `__imul__`). +* The `enumerate` function. +* The `compile` function. +* Support for `eval`, `exec` and `execfile` functions. +* The `NotImplemented` special constant. +* The `input` function. +* The `pow` function with 3 integer arguments. +* The `help` function. +* The `micropython` module. +* The `array` module. +* The `collections` module. +* The `struct` module. +* The `gc` module. +* The `sys` module. +* The `select` module. +* The `json` module. + +This list of unsupported features is not set in stone. +If you miss support for one particular feature, feel free to [open an issue](https://github.com/ofabel/mp-flipper/issues) or make a pull request. diff --git a/non_catalog_apps/mp_flipper/docs/pages/index.rst b/non_catalog_apps/mp_flipper/docs/pages/index.rst new file mode 100644 index 00000000..d08e4997 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/index.rst @@ -0,0 +1,51 @@ +.. toctree:: + :hidden: + :maxdepth: 2 + + quickstart + reference + features + examples + roadmap + Changelog + License + +MicroPython on Flipper Zero +=========================== + +.. image:: https://img.shields.io/github/license/ofabel/mp-flipper + :alt: License + +.. image:: https://img.shields.io/github/v/tag/ofabel/mp-flipper + :alt: Version + +.. image:: https://img.shields.io/github/issues/ofabel/mp-flipper + :alt: Issues + +A `MicroPython `_ port for the famous `Flipper Zero `_. +No need to learn C: Use your favourite programming language to create apps, games and scripts. + +Features +-------- + +* Support for basic language constructs like functions, classes, loops, ... +* Access the Flipper's hardware: buttons, speaker, LED, GPIO, ADC, PWM ... +* No custom firmware required, so no risk to brick your Flipper. + +A complete list can be found in the :doc:`features ` section. + +How to Start +------------ + +1. Install the application from the `Flipper Lab `_ on your Flipper device. +2. Write some Python code or use one of the provided `examples `_. +3. Use the `qFlipper `_ application to upload the code to your Flipper's SD card. +4. Use the **uPython** application on your Flipper to execute your Python script. + +Checkout the :doc:`reference ` section for an in-depth API documentation. + +License +------- + +The uPython application is published under the :doc:`MIT ` license. + diff --git a/non_catalog_apps/mp_flipper/docs/pages/license.rst b/non_catalog_apps/mp_flipper/docs/pages/license.rst new file mode 100644 index 00000000..0d3f46c3 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/license.rst @@ -0,0 +1 @@ +.. include:: ../../LICENSE.txt \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/docs/pages/quickstart.md b/non_catalog_apps/mp_flipper/docs/pages/quickstart.md new file mode 100644 index 00000000..acb98436 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/quickstart.md @@ -0,0 +1 @@ +# Quickstart diff --git a/non_catalog_apps/mp_flipper/docs/pages/reference.rst b/non_catalog_apps/mp_flipper/docs/pages/reference.rst new file mode 100644 index 00000000..38c7e8b4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/reference.rst @@ -0,0 +1,418 @@ +Reference +========= + +This page contains the API documentation of the ``flipperzero`` module and some built-in functions. +The module is also available as a Python package on `PyPI `_. +Install it in your local development environment if you need code completion support inside your IDE. + +Vibration +--------- + +Control the vibration motor of your Flipper. + +.. autofunction:: flipperzero.vibro_set + +.. _reference-light: + +Light +----- + +Control the RGB LED and display backlight of your Flipper. + +Constants +~~~~~~~~~ + +.. autodata:: flipperzero.LIGHT_RED +.. autodata:: flipperzero.LIGHT_GREEN +.. autodata:: flipperzero.LIGHT_BLUE +.. autodata:: flipperzero.LIGHT_BACKLIGHT + +Functions +~~~~~~~~~ + +.. autofunction:: flipperzero.light_set +.. autofunction:: flipperzero.light_blink_start +.. autofunction:: flipperzero.light_blink_set_color +.. autofunction:: flipperzero.light_blink_stop + +.. _reference-speaker: + +Speaker +------- + +Full control over the built-in speaker module. + +Musical Notes +~~~~~~~~~~~~~ + +.. + for octave in range(9): + for name in ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B']: + print(f'.. autodata:: flipperzero.SPEAKER_NOTE_{name}{octave}') + +.. autodata:: flipperzero.SPEAKER_NOTE_C0 +.. autodata:: flipperzero.SPEAKER_NOTE_CS0 +.. autodata:: flipperzero.SPEAKER_NOTE_D0 +.. autodata:: flipperzero.SPEAKER_NOTE_DS0 +.. autodata:: flipperzero.SPEAKER_NOTE_E0 +.. autodata:: flipperzero.SPEAKER_NOTE_F0 +.. autodata:: flipperzero.SPEAKER_NOTE_FS0 +.. autodata:: flipperzero.SPEAKER_NOTE_G0 +.. autodata:: flipperzero.SPEAKER_NOTE_GS0 +.. autodata:: flipperzero.SPEAKER_NOTE_A0 +.. autodata:: flipperzero.SPEAKER_NOTE_AS0 +.. autodata:: flipperzero.SPEAKER_NOTE_B0 +.. autodata:: flipperzero.SPEAKER_NOTE_C1 +.. autodata:: flipperzero.SPEAKER_NOTE_CS1 +.. autodata:: flipperzero.SPEAKER_NOTE_D1 +.. autodata:: flipperzero.SPEAKER_NOTE_DS1 +.. autodata:: flipperzero.SPEAKER_NOTE_E1 +.. autodata:: flipperzero.SPEAKER_NOTE_F1 +.. autodata:: flipperzero.SPEAKER_NOTE_FS1 +.. autodata:: flipperzero.SPEAKER_NOTE_G1 +.. autodata:: flipperzero.SPEAKER_NOTE_GS1 +.. autodata:: flipperzero.SPEAKER_NOTE_A1 +.. autodata:: flipperzero.SPEAKER_NOTE_AS1 +.. autodata:: flipperzero.SPEAKER_NOTE_B1 +.. autodata:: flipperzero.SPEAKER_NOTE_C2 +.. autodata:: flipperzero.SPEAKER_NOTE_CS2 +.. autodata:: flipperzero.SPEAKER_NOTE_D2 +.. autodata:: flipperzero.SPEAKER_NOTE_DS2 +.. autodata:: flipperzero.SPEAKER_NOTE_E2 +.. autodata:: flipperzero.SPEAKER_NOTE_F2 +.. autodata:: flipperzero.SPEAKER_NOTE_FS2 +.. autodata:: flipperzero.SPEAKER_NOTE_G2 +.. autodata:: flipperzero.SPEAKER_NOTE_GS2 +.. autodata:: flipperzero.SPEAKER_NOTE_A2 +.. autodata:: flipperzero.SPEAKER_NOTE_AS2 +.. autodata:: flipperzero.SPEAKER_NOTE_B2 +.. autodata:: flipperzero.SPEAKER_NOTE_C3 +.. autodata:: flipperzero.SPEAKER_NOTE_CS3 +.. autodata:: flipperzero.SPEAKER_NOTE_D3 +.. autodata:: flipperzero.SPEAKER_NOTE_DS3 +.. autodata:: flipperzero.SPEAKER_NOTE_E3 +.. autodata:: flipperzero.SPEAKER_NOTE_F3 +.. autodata:: flipperzero.SPEAKER_NOTE_FS3 +.. autodata:: flipperzero.SPEAKER_NOTE_G3 +.. autodata:: flipperzero.SPEAKER_NOTE_GS3 +.. autodata:: flipperzero.SPEAKER_NOTE_A3 +.. autodata:: flipperzero.SPEAKER_NOTE_AS3 +.. autodata:: flipperzero.SPEAKER_NOTE_B3 +.. autodata:: flipperzero.SPEAKER_NOTE_C4 +.. autodata:: flipperzero.SPEAKER_NOTE_CS4 +.. autodata:: flipperzero.SPEAKER_NOTE_D4 +.. autodata:: flipperzero.SPEAKER_NOTE_DS4 +.. autodata:: flipperzero.SPEAKER_NOTE_E4 +.. autodata:: flipperzero.SPEAKER_NOTE_F4 +.. autodata:: flipperzero.SPEAKER_NOTE_FS4 +.. autodata:: flipperzero.SPEAKER_NOTE_G4 +.. autodata:: flipperzero.SPEAKER_NOTE_GS4 +.. autodata:: flipperzero.SPEAKER_NOTE_A4 +.. autodata:: flipperzero.SPEAKER_NOTE_AS4 +.. autodata:: flipperzero.SPEAKER_NOTE_B4 +.. autodata:: flipperzero.SPEAKER_NOTE_C5 +.. autodata:: flipperzero.SPEAKER_NOTE_CS5 +.. autodata:: flipperzero.SPEAKER_NOTE_D5 +.. autodata:: flipperzero.SPEAKER_NOTE_DS5 +.. autodata:: flipperzero.SPEAKER_NOTE_E5 +.. autodata:: flipperzero.SPEAKER_NOTE_F5 +.. autodata:: flipperzero.SPEAKER_NOTE_FS5 +.. autodata:: flipperzero.SPEAKER_NOTE_G5 +.. autodata:: flipperzero.SPEAKER_NOTE_GS5 +.. autodata:: flipperzero.SPEAKER_NOTE_A5 +.. autodata:: flipperzero.SPEAKER_NOTE_AS5 +.. autodata:: flipperzero.SPEAKER_NOTE_B5 +.. autodata:: flipperzero.SPEAKER_NOTE_C6 +.. autodata:: flipperzero.SPEAKER_NOTE_CS6 +.. autodata:: flipperzero.SPEAKER_NOTE_D6 +.. autodata:: flipperzero.SPEAKER_NOTE_DS6 +.. autodata:: flipperzero.SPEAKER_NOTE_E6 +.. autodata:: flipperzero.SPEAKER_NOTE_F6 +.. autodata:: flipperzero.SPEAKER_NOTE_FS6 +.. autodata:: flipperzero.SPEAKER_NOTE_G6 +.. autodata:: flipperzero.SPEAKER_NOTE_GS6 +.. autodata:: flipperzero.SPEAKER_NOTE_A6 +.. autodata:: flipperzero.SPEAKER_NOTE_AS6 +.. autodata:: flipperzero.SPEAKER_NOTE_B6 +.. autodata:: flipperzero.SPEAKER_NOTE_C7 +.. autodata:: flipperzero.SPEAKER_NOTE_CS7 +.. autodata:: flipperzero.SPEAKER_NOTE_D7 +.. autodata:: flipperzero.SPEAKER_NOTE_DS7 +.. autodata:: flipperzero.SPEAKER_NOTE_E7 +.. autodata:: flipperzero.SPEAKER_NOTE_F7 +.. autodata:: flipperzero.SPEAKER_NOTE_FS7 +.. autodata:: flipperzero.SPEAKER_NOTE_G7 +.. autodata:: flipperzero.SPEAKER_NOTE_GS7 +.. autodata:: flipperzero.SPEAKER_NOTE_A7 +.. autodata:: flipperzero.SPEAKER_NOTE_AS7 +.. autodata:: flipperzero.SPEAKER_NOTE_B7 +.. autodata:: flipperzero.SPEAKER_NOTE_C8 +.. autodata:: flipperzero.SPEAKER_NOTE_CS8 +.. autodata:: flipperzero.SPEAKER_NOTE_D8 +.. autodata:: flipperzero.SPEAKER_NOTE_DS8 +.. autodata:: flipperzero.SPEAKER_NOTE_E8 +.. autodata:: flipperzero.SPEAKER_NOTE_F8 +.. autodata:: flipperzero.SPEAKER_NOTE_FS8 +.. autodata:: flipperzero.SPEAKER_NOTE_G8 +.. autodata:: flipperzero.SPEAKER_NOTE_GS8 +.. autodata:: flipperzero.SPEAKER_NOTE_A8 +.. autodata:: flipperzero.SPEAKER_NOTE_AS8 +.. autodata:: flipperzero.SPEAKER_NOTE_B8 + +Volume +~~~~~~ + +.. autodata:: flipperzero.SPEAKER_VOLUME_MIN +.. autodata:: flipperzero.SPEAKER_VOLUME_MAX + +Functions +~~~~~~~~~ + +.. autofunction:: flipperzero.speaker_start +.. autofunction:: flipperzero.speaker_set_volume +.. autofunction:: flipperzero.speaker_stop + +.. _reference-input: + +Input +----- + +Make your application interactive with full control over the Flipper's hardware buttons. + +Buttons +~~~~~~~ + +.. autodata:: flipperzero.INPUT_BUTTON_UP +.. autodata:: flipperzero.INPUT_BUTTON_DOWN +.. autodata:: flipperzero.INPUT_BUTTON_RIGHT +.. autodata:: flipperzero.INPUT_BUTTON_LEFT +.. autodata:: flipperzero.INPUT_BUTTON_OK +.. autodata:: flipperzero.INPUT_BUTTON_BACK + +Events +~~~~~~ + +.. autodata:: flipperzero.INPUT_TYPE_PRESS +.. autodata:: flipperzero.INPUT_TYPE_RELEASE +.. autodata:: flipperzero.INPUT_TYPE_SHORT +.. autodata:: flipperzero.INPUT_TYPE_LONG +.. autodata:: flipperzero.INPUT_TYPE_REPEAT + +Functions +~~~~~~~~~ + +.. autodecorator:: flipperzero.on_input + +.. _reference-canvas: + +Canvas +------ + +Write text and draw dots and shapes on the the display. + +Basics +~~~~~~ + +.. autofunction:: flipperzero.canvas_update +.. autofunction:: flipperzero.canvas_clear +.. autofunction:: flipperzero.canvas_width +.. autofunction:: flipperzero.canvas_height + +Colors +~~~~~~ + +.. autodata:: flipperzero.COLOR_BLACK +.. autodata:: flipperzero.COLOR_WHITE +.. autofunction:: flipperzero.canvas_set_color + +Alignment +~~~~~~~~~ + +.. autodata:: flipperzero.ALIGN_BEGIN +.. autodata:: flipperzero.ALIGN_END +.. autodata:: flipperzero.ALIGN_CENTER +.. autofunction:: flipperzero.canvas_set_text_align + +Texts +~~~~~ + +.. autodata:: flipperzero.FONT_PRIMARY +.. autodata:: flipperzero.FONT_SECONDARY +.. autofunction:: flipperzero.canvas_set_font +.. autofunction:: flipperzero.canvas_set_text + +Shapes +~~~~~~ + +.. autofunction:: flipperzero.canvas_draw_dot +.. autofunction:: flipperzero.canvas_draw_box +.. autofunction:: flipperzero.canvas_draw_frame +.. autofunction:: flipperzero.canvas_draw_line +.. autofunction:: flipperzero.canvas_draw_circle +.. autofunction:: flipperzero.canvas_draw_disc + +Dialog +------ + +Display message dialogs on the display for user infos and confirm actions. + +.. autofunction:: flipperzero.dialog_message_set_header +.. autofunction:: flipperzero.dialog_message_set_text +.. autofunction:: flipperzero.dialog_message_set_button +.. autofunction:: flipperzero.dialog_message_show + +.. _reference-gpio: + +GPIO +---- + +Access to the GPIO pins of your Flipper. + +Pins +~~~~ + +.. autodata:: flipperzero.GPIO_PIN_PC0 +.. autodata:: flipperzero.GPIO_PIN_PC1 +.. autodata:: flipperzero.GPIO_PIN_PC3 +.. autodata:: flipperzero.GPIO_PIN_PB2 +.. autodata:: flipperzero.GPIO_PIN_PB3 +.. autodata:: flipperzero.GPIO_PIN_PA4 +.. autodata:: flipperzero.GPIO_PIN_PA6 +.. autodata:: flipperzero.GPIO_PIN_PA7 + +Modes +~~~~~ + +.. autodata:: flipperzero.GPIO_MODE_INPUT +.. autodata:: flipperzero.GPIO_MODE_OUTPUT_PUSH_PULL +.. autodata:: flipperzero.GPIO_MODE_OUTPUT_OPEN_DRAIN +.. autodata:: flipperzero.GPIO_MODE_ANALOG +.. autodata:: flipperzero.GPIO_MODE_INTERRUPT_RISE +.. autodata:: flipperzero.GPIO_MODE_INTERRUPT_FALL + +Pull +~~~~ + +.. autodata:: flipperzero.GPIO_PULL_NO +.. autodata:: flipperzero.GPIO_PULL_UP +.. autodata:: flipperzero.GPIO_PULL_DOWN + +Speed +~~~~~ + +.. autodata:: flipperzero.GPIO_SPEED_LOW +.. autodata:: flipperzero.GPIO_SPEED_MEDIUM +.. autodata:: flipperzero.GPIO_SPEED_HIGH +.. autodata:: flipperzero.GPIO_SPEED_VERY_HIGH + +Functions +~~~~~~~~~ + +.. autofunction:: flipperzero.gpio_init_pin +.. autofunction:: flipperzero.gpio_deinit_pin +.. autofunction:: flipperzero.gpio_set_pin +.. autofunction:: flipperzero.gpio_get_pin +.. autodecorator:: flipperzero.on_gpio + +.. _reference-adc: + +ADC +--- + +Read analog values from selected GPIO pins: + +* :const:`flipperzero.GPIO_PIN_PC0` +* :const:`flipperzero.GPIO_PIN_PC1` +* :const:`flipperzero.GPIO_PIN_PC3` +* :const:`flipperzero.GPIO_PIN_PA4` +* :const:`flipperzero.GPIO_PIN_PA6` +* :const:`flipperzero.GPIO_PIN_PA7` + +The corresponding pin must be initialized in the analog mode: + +.. code-block:: + + import flipperzero as f0 + + f0.gpio_init_pin(f0.GPIO_PIN_PC0, f0.GPIO_MODE_ANALOG) + +This configures the pin as ADC input with the following settings: + +* Reference voltage is set to 2.048 V. +* Clock speed is at 64 MHz in synchronous mode. +* Oversample rate is set to 64. + +`This default configuration is best for relatively high impedance circuits with slowly or or not changing signals.` + +Functions +~~~~~~~~~ + +.. autofunction:: flipperzero.adc_read_pin_value +.. autofunction:: flipperzero.adc_read_pin_voltage + +.. _reference-pwm: + +PWM +--- + +Output a PWM signal on selected GPIO pins: + +* :const:`flipperzero.GPIO_PIN_PA4` +* :const:`flipperzero.GPIO_PIN_PA7` + +Functions +~~~~~~~~~ + +.. autofunction:: flipperzero.pwm_start +.. autofunction:: flipperzero.pwm_stop +.. autofunction:: flipperzero.pwm_is_running + +.. _reference-infrared: + +Infrared +-------- + +Send and receive infrared signals. + +Signal Format +~~~~~~~~~~~~~ + +The format to represent infrared signals uses a simple list of integers. +Each value represents the duration between two signal edges in microseconds. +Since this is a digital signal, there are only two levels: `high` and `low`. +The timing list always starts with a `high` level. + +.. literalinclude:: ./assets/pwm_signal.txt + :language: text + +.. hint:: + + This is equal to the raw signal format of the `IR file `_ specification. + +Functions +~~~~~~~~~ + +.. autofunction:: flipperzero.infrared_receive +.. autofunction:: flipperzero.infrared_transmit +.. autofunction:: flipperzero.infrared_is_busy + +Built-In +-------- + +The functions in this section are `not` part of the ``flipperzero`` module. +They're members of the global namespace instead. + +.. py:function:: print(*objects, sep=' ', end='\n', file=None, flush=False) -> None + + The standard Python `print `_ function. + + :param objects: The objects to print (mostly a single string). + :param sep: The separator to use between the objects. + :param end: The line terminator character to use. + + .. versionadded:: 1.0.0 + + .. attention:: + + This function prints to the internal log buffer. + Check out the `Flipper Zero docs `_ on how to reveal them in the CLI interface. diff --git a/non_catalog_apps/mp_flipper/docs/pages/roadmap.md b/non_catalog_apps/mp_flipper/docs/pages/roadmap.md new file mode 100644 index 00000000..cd026503 --- /dev/null +++ b/non_catalog_apps/mp_flipper/docs/pages/roadmap.md @@ -0,0 +1,22 @@ +# Roadmap + +This is the roadmap for the application. +Here you can see what to expect from future releases. + +## Next Release + +* Improved `print` function. +* UART +* I2C +* Maybe USB HID + +## Planned + +* I2C +* UART +* USB HID +* Subghz +* Bluetooth +* ... + +Feel free to [file an issue](https://www.github.com/ofabel/mp-flipper/issues), if you miss any particular feature. diff --git a/non_catalog_apps/mp_flipper/docs/tic-tac-toe.png b/non_catalog_apps/mp_flipper/docs/tic-tac-toe.png new file mode 100644 index 00000000..eea89b1f Binary files /dev/null and b/non_catalog_apps/mp_flipper/docs/tic-tac-toe.png differ diff --git a/non_catalog_apps/mp_flipper/docs/welcome.png b/non_catalog_apps/mp_flipper/docs/welcome.png new file mode 100644 index 00000000..c88d484c Binary files /dev/null and b/non_catalog_apps/mp_flipper/docs/welcome.png differ diff --git a/non_catalog_apps/mp_flipper/examples/dict_test.py b/non_catalog_apps/mp_flipper/examples/dict_test.py new file mode 100644 index 00000000..4884a994 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/dict_test.py @@ -0,0 +1,9 @@ +d = dict() + +for i in range(1,100): + d[str(i)] = i + +for k,v in d.items(): + val = '{0} = {1}'.format(k, v) + + print(val) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_adc_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_adc_test.py new file mode 100644 index 00000000..91821296 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_adc_test.py @@ -0,0 +1,20 @@ +import flipperzero as f0 +import time + +f0.gpio_init_pin(f0.GPIO_PIN_PC1, f0.GPIO_MODE_ANALOG) + +for _ in range(1,1000): + raw_value = f0.adc_read_pin_value(f0.GPIO_PIN_PC1) + raw_voltage = f0.adc_read_pin_voltage(f0.GPIO_PIN_PC1) + + value = '{value} #'.format(value=raw_value) + voltage = '{value} mV'.format(value=raw_voltage) + + f0.canvas_clear() + + f0.canvas_set_text(10, 32, value) + f0.canvas_set_text(70, 32, voltage) + + f0.canvas_update() + + time.sleep_ms(10) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_canvas_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_canvas_test.py new file mode 100644 index 00000000..0607d92e --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_canvas_test.py @@ -0,0 +1,33 @@ +import time +import flipperzero + +color = False + +def draw_action(): + print('on draw') + + global color + + for x in range(0, 128): + color = not color + + for y in range(0, 64): + flipperzero.canvas_set_color(flipperzero.CANVAS_BLACK if color else flipperzero.CANVAS_WHITE) + flipperzero.canvas_draw_dot(x, y) + + color = not color + + color = not color + + flipperzero.canvas_set_text(64, 32, "Test") + + flipperzero.canvas_update() + +print('start') + +draw_action() + +for _ in range(1, 5): + time.sleep(1) + + draw_action() diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_dialog_message_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_dialog_message_test.py new file mode 100644 index 00000000..0f44881d --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_dialog_message_test.py @@ -0,0 +1,9 @@ +import flipperzero as f0 + +f0.dialog_message_set_header('Important',64, 12) +f0.dialog_message_set_text('Shutdown?', 64, 24) +f0.dialog_message_set_button('Yes', f0.INPUT_BUTTON_LEFT) +f0.dialog_message_set_button('No', f0.INPUT_BUTTON_RIGHT) + +while f0.dialog_message_show() is not f0.INPUT_BUTTON_LEFT: + pass diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_draw_on_input_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_draw_on_input_test.py new file mode 100644 index 00000000..3ca26972 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_draw_on_input_test.py @@ -0,0 +1,12 @@ +import time +import flipperzero + +@flipperzero.on_input +def on_input(button, type): + flipperzero.canvas_clear() + flipperzero.canvas_set_color(flipperzero.CANVAS_BLACK) + flipperzero.canvas_set_text(64, 32, '{button} - {type}'.format(button=button, type=type)) + flipperzero.canvas_update() + +for _ in range(1,1000): + time.sleep_ms(10) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_input_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_input_test.py new file mode 100644 index 00000000..cbd80eec --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_input_test.py @@ -0,0 +1,12 @@ +import flipperzero as f0 +import time + +f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_OUTPUT_PUSH_PULL) +f0.gpio_init_pin(f0.GPIO_PIN_PC1, f0.GPIO_MODE_INPUT, f0.GPIO_PULL_UP, f0.GPIO_SPEED_HIGH) + +for _ in range(0,15): + state = f0.gpio_get_pin(f0.GPIO_PIN_PC1) + + f0.gpio_set_pin(f0.GPIO_PIN_PA7, state) + + time.sleep(1) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_interrupt_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_interrupt_test.py new file mode 100644 index 00000000..39ed1219 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_interrupt_test.py @@ -0,0 +1,22 @@ +import flipperzero as f0 +import time + +# init pins +f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_OUTPUT_PUSH_PULL) +f0.gpio_init_pin(f0.GPIO_PIN_PC0, f0.GPIO_MODE_INTERRUPT_RISE, f0.GPIO_PULL_UP, f0.GPIO_SPEED_VERY_HIGH) +f0.gpio_init_pin(f0.GPIO_PIN_PC1, f0.GPIO_MODE_INTERRUPT_RISE, f0.GPIO_PULL_UP, f0.GPIO_SPEED_VERY_HIGH) + +@f0.on_gpio +def on_gpio(pin): + if pin == f0.GPIO_PIN_PC0: + f0.gpio_set_pin(f0.GPIO_PIN_PA7, True) + if pin == f0.GPIO_PIN_PC1: + f0.gpio_set_pin(f0.GPIO_PIN_PA7, False) + +for _ in range(1, 1500): + time.sleep_ms(10) + +# reset pins +f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_ANALOG) +f0.gpio_init_pin(f0.GPIO_PIN_PC0, f0.GPIO_MODE_ANALOG) +f0.gpio_init_pin(f0.GPIO_PIN_PC1, f0.GPIO_MODE_ANALOG) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_test.py new file mode 100644 index 00000000..c0539982 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_gpio_test.py @@ -0,0 +1,12 @@ +import flipperzero as f0 +import time + +f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_OUTPUT_PUSH_PULL) + +f0.gpio_set_pin(f0.GPIO_PIN_PA7, True) +time.sleep(1) +f0.gpio_set_pin(f0.GPIO_PIN_PA7, False) +time.sleep(1) +f0.gpio_set_pin(f0.GPIO_PIN_PA7, True) +time.sleep(1) +f0.gpio_set_pin(f0.GPIO_PIN_PA7, False) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_infrared_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_infrared_test.py new file mode 100644 index 00000000..f4be5409 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_infrared_test.py @@ -0,0 +1,10 @@ +import flipperzero as f0 +import time + +# receive IR signal +signal = f0.infrared_receive() + +time.sleep(3) + +# transmit received signal +f0.infrared_transmit(signal) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_input_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_input_test.py new file mode 100644 index 00000000..6a23f3f3 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_input_test.py @@ -0,0 +1,9 @@ +import time +import flipperzero + +@flipperzero.on_input +def on_input(button, type): + print('{button} - {type}'.format(button=button, type=type)) + +for _ in range(1,1000): + time.sleep_ms(10) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_light_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_light_test.py new file mode 100644 index 00000000..dea29723 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_light_test.py @@ -0,0 +1,32 @@ +import time +import flipperzero + +is_red = True + +for i in range(0, 25, 1): + brightness = i * 10 + is_red = not is_red + + flipperzero.light_set(flipperzero.LIGHT_RED, brightness if is_red else 0) + flipperzero.light_set(flipperzero.LIGHT_GREEN, brightness if not is_red else 0) + flipperzero.light_set(flipperzero.LIGHT_BLUE, 0) + flipperzero.light_set(flipperzero.LIGHT_BACKLIGHT, brightness) + + time.sleep_ms(200) + +flipperzero.light_set(flipperzero.LIGHT_RED, 0) +flipperzero.light_set(flipperzero.LIGHT_GREEN, 0) +flipperzero.light_set(flipperzero.LIGHT_BLUE, 0) +flipperzero.light_set(flipperzero.LIGHT_BACKLIGHT, 0) + +time.sleep_ms(500) + +flipperzero.light_blink_start(flipperzero.LIGHT_RED, 200, 100, 200) + +time.sleep(1) + +flipperzero.light_blink_set_color(flipperzero.LIGHT_BLUE) + +time.sleep(1) + +flipperzero.light_blink_stop() diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_pwm_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_pwm_test.py new file mode 100644 index 00000000..e61b0a95 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_pwm_test.py @@ -0,0 +1,8 @@ +import flipperzero as f0 +import time + +f0.pwm_start(f0.GPIO_PIN_PA7, 4, 50) +time.sleep(3) + +f0.pwm_start(f0.GPIO_PIN_PA7, 1, 25) +time.sleep(3) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_speaker_note_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_speaker_note_test.py new file mode 100644 index 00000000..7c8e7e68 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_speaker_note_test.py @@ -0,0 +1,25 @@ +import time +import flipperzero as f0 + +def play_frequency(frequency: float): + volume = 0.8 + + f0.speaker_start(frequency, volume) + + for _ in range(0, 150): + volume *= 0.9945679 + + f0.speaker_set_volume(volume) + + time.sleep_ms(1) + + f0.speaker_stop() + +play_frequency(f0.SPEAKER_NOTE_C5) +play_frequency(f0.SPEAKER_NOTE_D5) +play_frequency(f0.SPEAKER_NOTE_E5) +play_frequency(f0.SPEAKER_NOTE_F5) +play_frequency(f0.SPEAKER_NOTE_G5) +play_frequency(f0.SPEAKER_NOTE_A5) +play_frequency(f0.SPEAKER_NOTE_B5) +play_frequency(f0.SPEAKER_NOTE_C6) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_speaker_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_speaker_test.py new file mode 100644 index 00000000..80072fe4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_speaker_test.py @@ -0,0 +1,25 @@ +import time +import flipperzero + +def play_frequency(frequency: float): + volume = 0.8 + + flipperzero.speaker_start(frequency, volume) + + for _ in range(0, 150): + volume *= 0.9945679 + + flipperzero.speaker_set_volume(volume) + + time.sleep_ms(1) + + flipperzero.speaker_stop() + +play_frequency(100.0) +play_frequency(200.0) +play_frequency(300.0) +play_frequency(500.0) +play_frequency(800.0) +play_frequency(1300.0) +play_frequency(2100.0) +play_frequency(3400.0) diff --git a/non_catalog_apps/mp_flipper/examples/flipperzero_vibro_test.py b/non_catalog_apps/mp_flipper/examples/flipperzero_vibro_test.py new file mode 100644 index 00000000..629b6f81 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/flipperzero_vibro_test.py @@ -0,0 +1,8 @@ +import time +import flipperzero + +flipperzero.vibro_set(True) + +time.sleep_ms(125) + +flipperzero.vibro_set(False) diff --git a/non_catalog_apps/mp_flipper/examples/gc_test.py b/non_catalog_apps/mp_flipper/examples/gc_test.py new file mode 100644 index 00000000..2ac04c42 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/gc_test.py @@ -0,0 +1,3 @@ +import gc + +gc.collect() diff --git a/non_catalog_apps/mp_flipper/examples/greeter_test.py b/non_catalog_apps/mp_flipper/examples/greeter_test.py new file mode 100644 index 00000000..de0fd3d2 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/greeter_test.py @@ -0,0 +1,13 @@ +class Greeter: + def __init__(self, name: str): + self._name = name + + def __str__(self): + return 'hello {name}!'.format(name=self._name) + + def __repr__(self): + return self.__str__() + +world_greeter = Greeter('world') + +print(world_greeter) diff --git a/non_catalog_apps/mp_flipper/examples/hello_test.py b/non_catalog_apps/mp_flipper/examples/hello_test.py new file mode 100644 index 00000000..665c637a --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/hello_test.py @@ -0,0 +1 @@ +print("hello world!") diff --git a/non_catalog_apps/mp_flipper/examples/import_error_test.py b/non_catalog_apps/mp_flipper/examples/import_error_test.py new file mode 100644 index 00000000..e7d62215 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/import_error_test.py @@ -0,0 +1 @@ +import unknown_module \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/examples/import_test.py b/non_catalog_apps/mp_flipper/examples/import_test.py new file mode 100644 index 00000000..7e4cb5ad --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/import_test.py @@ -0,0 +1,5 @@ +from greeter_test import Greeter + +buddy = Greeter('buddy') + +print(buddy) diff --git a/non_catalog_apps/mp_flipper/examples/infrared_signal_viewer.py b/non_catalog_apps/mp_flipper/examples/infrared_signal_viewer.py new file mode 100644 index 00000000..19fcf632 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/infrared_signal_viewer.py @@ -0,0 +1,94 @@ +import flipperzero as f0 +import time + +terminate = False +index = 0 +signal = [] + +def draw_signal(): + global index + global signal + + f0.canvas_clear() + + level = False if index % 2 else True + x = 0 + y_low = 32 + y_high = 40 + y_level = y_low + + for i in range(100): + i += index + + if i > len(signal): + break + + duration = int(signal[i] / 100) + + if level: + f0.canvas_draw_line(x, y_low, x, y_high) + y_level = y_high + else: + f0.canvas_draw_line(x, y_high, x, y_low) + y_level = y_low + + f0.canvas_draw_line(x, y_level, x + duration, y_level) + + x += duration + + level = not level + + if x > f0.canvas_width(): + break + + f0.canvas_update() + +@f0.on_input +def on_input(button, type): + global terminate + global index + global signal + + # terminate upon back button press + if button == f0.INPUT_BUTTON_BACK and type == f0.INPUT_TYPE_SHORT: + terminate = True + + return + + # transmit signal upon ok button + if button == f0.INPUT_BUTTON_OK and type == f0.INPUT_TYPE_SHORT and len(signal) > 0: + f0.infrared_transmit(signal) + + return + + # scroll left upon button left + if button == f0.INPUT_BUTTON_LEFT and type == f0.INPUT_TYPE_SHORT: + index -= 1 if index > 0 else 0 + + draw_signal() + + return + + # scroll right upon button left + if button == f0.INPUT_BUTTON_RIGHT and type == f0.INPUT_TYPE_SHORT: + index += 1 + index %= len(signal) + + draw_signal() + + return + +f0.canvas_set_text(10, 32, 'Waiting for IR signal ...') +f0.canvas_update() + +# receive signal +signal = f0.infrared_receive() + +if len(signal): + draw_signal() +else: + terminate = True + +# wait upon termination +while not terminate: + time.sleep_ms(1) diff --git a/non_catalog_apps/mp_flipper/examples/overflow_test.py b/non_catalog_apps/mp_flipper/examples/overflow_test.py new file mode 100644 index 00000000..88bdba2c --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/overflow_test.py @@ -0,0 +1 @@ +data = list(range(0,9999)) diff --git a/non_catalog_apps/mp_flipper/examples/raise_test.py b/non_catalog_apps/mp_flipper/examples/raise_test.py new file mode 100644 index 00000000..59b54fa8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/raise_test.py @@ -0,0 +1 @@ +raise Exception('something went wrong') \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/examples/rand_test.py b/non_catalog_apps/mp_flipper/examples/rand_test.py new file mode 100644 index 00000000..03e47102 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/rand_test.py @@ -0,0 +1,7 @@ +from random import randint, seed + +seed(None) + +for limit in range(2,100): + value = randint(2, limit) + print(value) diff --git a/non_catalog_apps/mp_flipper/examples/sleep_test.py b/non_catalog_apps/mp_flipper/examples/sleep_test.py new file mode 100644 index 00000000..27b02cf8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/sleep_test.py @@ -0,0 +1,3 @@ +import time + +time.sleep(5) diff --git a/non_catalog_apps/mp_flipper/examples/tic_tac_toe.py b/non_catalog_apps/mp_flipper/examples/tic_tac_toe.py new file mode 100644 index 00000000..be239bf2 --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/tic_tac_toe.py @@ -0,0 +1,75 @@ +import time +import flipperzero as f0 + +def init_grid(): + return [ + [' ', ' ', ' '], + [' ', ' ', ' '], + [' ', ' ', ' '], + ] + +m_exit = False + +m_grid = init_grid() + +m_x = 1 +m_y = 1 + +m_is_cross = True + +@f0.on_input +def input_handler(button, type): + global m_exit + global m_grid + global m_x + global m_y + global m_is_cross + + if button == f0.INPUT_BUTTON_BACK and type == f0.INPUT_TYPE_LONG: + m_exit = True + elif button == f0.INPUT_BUTTON_BACK and type == f0.INPUT_TYPE_SHORT: + m_grid = init_grid() + elif button == f0.INPUT_BUTTON_LEFT and type == f0.INPUT_TYPE_SHORT: + m_x = (m_x - 1) % 3 + elif button == f0.INPUT_BUTTON_RIGHT and type == f0.INPUT_TYPE_SHORT: + m_x = (m_x + 1) % 3 + elif button == f0.INPUT_BUTTON_UP and type == f0.INPUT_TYPE_SHORT: + m_y = (m_y - 1) % 3 + elif button == f0.INPUT_BUTTON_DOWN and type == f0.INPUT_TYPE_SHORT: + m_y = (m_y + 1) % 3 + elif button == f0.INPUT_BUTTON_OK and type == f0.INPUT_TYPE_SHORT: + m_grid[m_x][m_y] = 'X' if m_is_cross else 'O' + m_is_cross = not m_is_cross + +def draw_grid(): + global m_grid + global m_x + global m_y + + f0.canvas_clear() + f0.canvas_draw_frame(2, 2, 60, 60) + + f0.canvas_draw_line(22, 2, 22, 62) + f0.canvas_draw_line(42, 2, 42, 62) + + f0.canvas_draw_line(2, 22, 62, 22) + f0.canvas_draw_line(2, 42, 62, 42) + + px = m_x * 20 + 4 + py = m_y * 20 + 4 + + f0.canvas_draw_frame(px, py, 16, 16) + + f0.canvas_set_text_align(f0.ALIGN_CENTER, f0.ALIGN_CENTER) + + for x in range(0, 3): + for y in range(0, 3): + px = x * 20 + 10 + 2 + py = y * 20 + 10 + 2 + f0.canvas_set_text(px, py, m_grid[x][y]) + + f0.canvas_update() + +while not m_exit: + draw_grid() + time.sleep_ms(25) diff --git a/non_catalog_apps/mp_flipper/examples/try_except_test.py b/non_catalog_apps/mp_flipper/examples/try_except_test.py new file mode 100644 index 00000000..eeabefec --- /dev/null +++ b/non_catalog_apps/mp_flipper/examples/try_except_test.py @@ -0,0 +1,4 @@ +try: + raise Exception('something went wrong') +except Exception as e: + print(e) \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/flipperzero/README.md b/non_catalog_apps/mp_flipper/flipperzero/README.md new file mode 100644 index 00000000..d6360b4e --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/README.md @@ -0,0 +1,6 @@ +# MicroPython on Flipper Zero + +This package contains the type definitions for the `flipperzero` [MicroPython](https://micropython.org/) module. +Check out the [website](https://ofabel.github.io/mp-flipper/) for details. + +Get the interpreter application on [Flipper Lab](https://lab.flipper.net/apps/upython) to write and run your own Python files. diff --git a/non_catalog_apps/mp_flipper/flipperzero/_adc.py b/non_catalog_apps/mp_flipper/flipperzero/_adc.py new file mode 100644 index 00000000..2c1ff23b --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_adc.py @@ -0,0 +1,29 @@ +def adc_read_pin_value(pin: int) -> int: + ''' + Read the raw value from the ADC channel. + + :param pin: The pin to read (e.g. :const:`GPIO_PIN_PC1`). + :returns: The raw value between 0 and 4095. + + .. versionadded:: 1.3.0 + + .. hint:: + + Don't forget to initialize the pin first. + ''' + pass + +def adc_read_pin_voltage(pin: int) -> float: + ''' + Read the voltage from the ADC channel. + + :param pin: The pin to read (e.g. :const:`GPIO_PIN_PC1`). + :returns: The voltage between 0 - 2.048 V with a precision of ~0.1%. + + .. versionadded:: 1.3.0 + + .. hint:: + + Don't forget to initialize the pin first. + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_canvas.py b/non_catalog_apps/mp_flipper/flipperzero/_canvas.py new file mode 100644 index 00000000..e8f78685 --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_canvas.py @@ -0,0 +1,226 @@ +def canvas_update() -> None: + ''' + Updates the display buffer with your drawings from the canvas. + + .. versionadded:: 1.0.0 + + .. note:: + + Your drawings will only appear on the display after this function call. + ''' + pass + +def canvas_clear() -> None: + ''' + Clear the whole canvas. This does not affect the current display buffer. + You need to call :func:`canvas_update` to reveal your changes. + + .. versionadded:: 1.0.0 + ''' + pass + +def canvas_width() -> int: + ''' + Get the canvas width in pixels. + + .. versionadded:: 1.0.0 + + :returns: The canvas width. + ''' + pass + +def canvas_height() -> int: + ''' + Get the canvas height in pixels. + + .. versionadded:: 1.0.0 + + :returns: The canvas height. + ''' + pass + +COLOR_BLACK: int +''' +Constant value for the color `black`. + +.. versionadded:: 1.0.0 +''' + +COLOR_WHITE: int +''' +Constant value for the color `white`. + +.. versionadded:: 1.0.0 +''' + +def canvas_set_color(color: int) -> None: + ''' + Set the color to use when drawing or writing on the canvas. + + .. versionadded:: 1.0.0 + + :param color: The color to use. + ''' + pass + +ALIGN_BEGIN: int +''' +Align element at `begin` (horizontal or vertical, depends on the context). + +.. versionadded:: 1.0.0 +''' + +ALIGN_END: int +''' +Align element at `end` (horizontal or vertical, depends on the context). + +.. versionadded:: 1.0.0 +''' + +ALIGN_CENTER: int +''' +Align element at `center` (horizontal or vertical, depends on the context). + +.. versionadded:: 1.0.0 +''' + +def canvas_set_text_align(x: int, y: int) -> None: + ''' + Define how the text should be aligned in relation to the ``x`` and ``y`` coordinates + when writing on the canvas, using the :func:`canvas_set_text` function. + + :param x: The horizontal alignment. + :param y: The vertical alignment. + + .. versionadded:: 1.0.0 + ''' + pass + +FONT_PRIMARY: int +''' +Constant value for the primary font. + +.. versionadded:: 1.0.0 +''' + +FONT_SECONDARY: int +''' +Constant value for the secondary font. + +.. versionadded:: 1.0.0 +''' + +def canvas_set_font(font: int) -> None: + ''' + Change the font to use when writing on the canvas using the :func:`canvas_set_text` function. + + :param font: The font to use. + + .. versionadded:: 1.0.0 + ''' + pass + +def canvas_set_text(x: int, y: int, text: str) -> None: + ''' + Write text on the canvas at the position of ``x`` and ``y`` by using the currently active color, font and alignment settings. + + :param x: The horizontal position. + :param y: The vertical position. + :param text: The text to write. + + .. versionadded:: 1.0.0 + + .. code-block:: + + import flipperzero as f0 + + f0.canvas_set_color(f0.COLOR_BLACK) + f0.canvas_set_text_align(f0.ALIGN_CENTER, f0.ALIGN_BEGIN) + f0.canvas_set_text(64, 32, 'Hello World!') + f0.canvas_update() + + .. seealso:: + + * :func:`canvas_set_color` to change the canvas color. + * :func:`canvas_set_text_align` to change the alignment settings. + * :func:`canvas_set_font` to change the current font. + ''' + pass + +def canvas_draw_dot(x: int, y: int) -> None: + ''' + Draw a dot on the canvas by using the currently active color settings. + + :param x: The horizontal position. + :param y: The vertical position. + + .. versionadded:: 1.0.0 + ''' + pass + +def canvas_draw_box(x: int, y: int, w: int, h: int, r: int) -> None: + ''' + Draw a box on the canvas. The fill color is defined by the currently active color settings. + Set the corner radius to zero to draw a rectangle without rounded corners. + + :param x: The horizontal position. + :param y: The vertical position. + :param w: The width of the box. + :param h: The height of the box. + :param r: The corner radius to use. + + .. versionadded:: 1.0.0 + ''' + pass + +def canvas_draw_frame(x: int, y: int, w: int, h: int, r: int) -> None: + ''' + Draw a frame on the canvas. The border color is defined by the currently active color settings. + Set the corner radius to zero to draw a rectangle without rounded corners. + + :param x: The horizontal position. + :param y: The vertical position. + :param w: The width of the box. + :param h: The height of the box. + :param r: The corner radius to use. + + .. versionadded:: 1.0.0 + ''' + pass + +def canvas_draw_line(x0: int, y0: int, x1: int, y1: int) -> None: + ''' + Draw a line on the canvas. The color is defined by the currently active color settings. + + :param x0: The horizontal start position. + :param y0: The vertical start position. + :param x1: The horizontal end position. + :param y1: The vertical end sposition. + + .. versionadded:: 1.0.0 + ''' + pass + +def canvas_draw_circle(x: int, y: int, r: int) -> None: + ''' + Draw a circle on the canvas. The border color is defined by the currently active color settings. + + :param x: The horizontal position. + :param y: The vertical position. + :param r: The radius to use. + + .. versionadded:: 1.0.0 + ''' + pass + +def canvas_draw_disc(x: int, y: int, r: int) -> None: + ''' + Draw a disc on the canvas. The fill color is defined by the currently active color settings. + + :param x: The horizontal position. + :param y: The vertical position. + :param r: The radius to use. + + .. versionadded:: 1.0.0 + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_dialog.py b/non_catalog_apps/mp_flipper/flipperzero/_dialog.py new file mode 100644 index 00000000..301b9f2f --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_dialog.py @@ -0,0 +1,61 @@ +def dialog_message_set_header(text: str, x: int, y: int, h: int, v: int) -> None: + ''' + Set a header text on the dialog box. + + :param text: The text to set. + :param x: The x coordinates to use. + :param y: The y coordinates to use. + :param h: The horizontal alignment. + :param v: The vertical alignment. + + .. versionadded:: 1.0.0 + ''' + pass + +def dialog_message_set_text(text: str, x: int, y: int, h: int, v: int) -> None: + ''' + Set a text on the dialog box. + + :param text: The text to set. + :param x: The x coordinates to use. + :param y: The y coordinates to use. + :param h: The horizontal alignment. + :param v: The vertical alignment. + + .. versionadded:: 1.0.0 + ''' + pass + +def dialog_message_set_button(text: str, button: int) -> None: + ''' + Set the text of a dialog box button. + + :param text: The text to set. + :param button: The button to use (e.g. :const:`INPUT_BUTTON_UP`). + + .. versionadded:: 1.0.0 + ''' + pass + +def dialog_message_show() -> int: + ''' + Display the dialog box with the configured settings. + This function is blocking. + + :returns: The button code, used to close the dialog (e.g. :const:`INPUT_BUTTON_OK`) + + .. versionadded:: 1.0.0 + + .. code-block:: + + import flipperzero as f0 + + f0.dialog_message_set_header('Important',64, 12) + f0.dialog_message_set_text('It this awesome?', 64, 24) + f0.dialog_message_set_button('Yes', f0.INPUT_BUTTON_LEFT) + f0.dialog_message_set_button('No', f0.INPUT_BUTTON_RIGHT) + + while f0.dialog_message_show() is not f0.INPUT_BUTTON_LEFT: + pass + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_gpio.py b/non_catalog_apps/mp_flipper/flipperzero/_gpio.py new file mode 100644 index 00000000..9ef5289c --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_gpio.py @@ -0,0 +1,252 @@ +from typing import Callable + +GPIO_PIN_PC0: int +''' +Constant identifier for GPIO pin PC0. + +* This pin can be used as ADC input. + +.. versionadded:: 1.2.0 +''' + +GPIO_PIN_PC1: int +''' +Constant identifier for GPIO pin PC1. + +* This pin can be used as ADC input. + +.. versionadded:: 1.2.0 +''' + +GPIO_PIN_PC3: int +''' +Constant identifier for GPIO pin PC3. + +* This pin can be used as ADC input. + +.. versionadded:: 1.2.0 +''' + +GPIO_PIN_PB2: int +''' +Constant identifier for GPIO pin PB2. + +.. versionadded:: 1.2.0 +''' + +GPIO_PIN_PB3: int +''' +Constant identifier for GPIO pin PB3. + +.. versionadded:: 1.2.0 +''' + +GPIO_PIN_PA4: int +''' +Constant identifier for GPIO pin PA4. + +* This pin can be used as ADC input. +* This pin can be used as PWM output. + +.. versionadded:: 1.2.0 +''' + +GPIO_PIN_PA6: int +''' +Constant identifier for GPIO pin PA6. + +* This pin can be used as ADC input. + +.. versionadded:: 1.2.0 +''' + +GPIO_PIN_PA7: int +''' +Constant identifier for GPIO pin PA7. + +* This pin can be used as ADC input. +* This pin can be used as PWM output. +* This pin can be used to transmit an infrared signal with an IR LED. + + +.. versionadded:: 1.2.0 +''' + +GPIO_MODE_INPUT: int +''' +Constant configuration value for the GPIO input mode. + +.. versionadded:: 1.2.0 +''' + +GPIO_MODE_OUTPUT_PUSH_PULL: int +''' +Constant configuration value for the GPIO output as push-pull mode. + +.. versionadded:: 1.2.0 +''' + +GPIO_MODE_OUTPUT_OPEN_DRAIN: int +''' +Constant configuration value for the GPIO output as open-drain mode. + +.. versionadded:: 1.2.0 +''' + +GPIO_MODE_ANALOG: int +''' +Constant configuration value for the GPIO analog mode. + +.. versionadded:: 1.2.0 +''' + +GPIO_MODE_INTERRUPT_RISE: int +''' +Constant configuration value for the GPIO interrupt on rising edges mode. + +.. versionadded:: 1.2.0 +''' + +GPIO_MODE_INTERRUPT_FALL: int +''' +Constant configuration value for the GPIO interrupt on falling edges mode. + +.. versionadded:: 1.2.0 +''' + +GPIO_PULL_NO: int +''' +Constant configuration value for the GPIO internal pull resistor disabled. + +.. versionadded:: 1.2.0 +''' + +GPIO_PULL_UP: int +''' +Constant configuration value for the GPIO internal pull-up resistor enabled. + +.. versionadded:: 1.2.0 +''' + +GPIO_PULL_DOWN: int +''' +Constant configuration value for the GPIO internal pull-down resistor enabled. + +.. versionadded:: 1.2.0 +''' + +GPIO_SPEED_LOW: int +''' +Constant configuration value for the GPIO in low speed. + +.. versionadded:: 1.2.0 +''' + +GPIO_SPEED_MEDIUM: int +''' +Constant configuration value for the GPIO in medium speed. + +.. versionadded:: 1.2.0 +''' + +GPIO_SPEED_HIGH: int +''' +Constant configuration value for the GPIO in high speed. + +.. versionadded:: 1.2.0 +''' + +GPIO_SPEED_VERY_HIGH: int +''' +Constant configuration value for the GPIO in very high speed. + +.. versionadded:: 1.2.0 +''' + +def gpio_init_pin(pin: int, mode: int, pull: int = None, speed: int = None) -> bool: + ''' + Initialize a GPIO pin. + + :param pin: The pin to initialize (e.g. :const:`GPIO_PIN_PA4`). + :param mode: The mode to use (e.g. :const:`GPIO_MODE_INPUT`). + :param pull: The pull resistor to use. Default is :const:`GPIO_PULL_NO`. + :param speed: The speed to use. Default is :const:`GPIO_SPEED_LOW`. + :returns: :const:`True` on success, :const:`False` otherwise. + + .. versionadded:: 1.2.0 + .. versionchanged:: 1.3.0 + The return value changed from ``None`` to ``bool``. + + .. hint:: + + The interrupt modes :const:`GPIO_MODE_INTERRUPT_RISE` and :const:`GPIO_MODE_INTERRUPT_FALL` can be combined using bitwise OR. + This allows you to handle rising `and` falling edges. + ''' + pass + +def gpio_deinit_pin(pin: int) -> None: + ''' + Deinitialize a GPIO pin. + + :param pin: The pin to deinitialize (e.g. :const:`GPIO_PIN_PA4`). + + .. versionadded:: 1.3.0 + + .. note:: + + It's not strictly necessary to deinitialize your GPIO pins upon script termination, this is already covered by the interpreter. + ''' + pass + +def gpio_set_pin(pin: int, state: bool) -> None: + ''' + Set the state of an output pin. + + :param pin: The pin to set (e.g. :const:`GPIO_PIN_PA4`). + :param state: The state to set. + + .. versionadded:: 1.2.0 + + .. hint:: + + Don't forget to initialize the pin first. + ''' + pass + +def gpio_get_pin(pin: int) -> bool: + ''' + Read the state of an input pin. + + :param pin: The pin to read (e.g. :const:`GPIO_PIN_PA4`). + :returns: :const:`True` if the pin is high, :const:`False` on a low signal. + + .. versionadded:: 1.2.0 + + .. hint:: + + Don't forget to initialize the pin first. + ''' + pass + +def on_gpio() -> Callable[[int], None]: + ''' + Decorate a function to be used as GPIO interrupt handler. The decorated function will be invoked upon a GPIO interrupt. + + .. versionadded:: 1.0.0 + + .. code-block:: + + import flipperzero as f0 + + f0.gpio_init_pin(f0.GPIO_PIN_PC0, f0.GPIO_MODE_INTERRUPT_RISE, f0.GPIO_PULL_UP) + + @f0.on_gpio + def interrupt_handler(pin): + if pin == f0.GPIO_PIN_PC0: + ... + + .. warning:: + + You can only decorate one function per application. + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_infrared.py b/non_catalog_apps/mp_flipper/flipperzero/_infrared.py new file mode 100644 index 00000000..04468472 --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_infrared.py @@ -0,0 +1,43 @@ +from typing import List + +def infrared_receive(timeout: int = 1000000) -> List[int]: + ''' + Receive an infrared signal. This is a blocking method. + The method blocks until a timeout occurs or the internal + signal buffer (capacity is 1024 timings) is filled. + + :param timeout: The timeout to use in microseconds. + :returns: A list of timings in microseconds, starting with high. + + .. versionadded:: 1.3.0 + ''' + pass + +def infrared_transmit(signal: List[int], repeat: int = 1, use_external_pin: bool = False, frequency: int = 38000, duty: float = 0.33) -> bool: + ''' + Transmit an infrared signal. This is a blocking method. + The method blocks until the whole signal is sent. + The signal list has the same format as the return value + of :func:`infrared_receive`. Hence you can directly re-send + a received signal without any further processing. + + :param signal: The signal to use. + :param repeat: How many times the signal should be sent. + :param use_external_pin: :const:`True` to use an external IR LED on GPIO pin :const:`flipperzero.GPIO_PIN_PA7`. + :param frequency: The frequency to use for the PWM signal. + :param duty: The duty cycle to use for the PWM signal. + :returns: :const:`True` on success, :const:`False` otherwise. + + .. versionadded:: 1.3.0 + ''' + pass + +def infrared_is_busy() -> bool: + ''' + Check if the infrared subsystem is busy. + + :returns: :const:`True` if occupied, :const:`False` otherwise. + + .. versionadded:: 1.3.0 + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_input.py b/non_catalog_apps/mp_flipper/flipperzero/_input.py new file mode 100644 index 00000000..a3f6288f --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_input.py @@ -0,0 +1,100 @@ +from typing import Callable + +INPUT_BUTTON_UP: int +''' +Constant value for the `up` button. + +.. versionadded:: 1.0.0 +''' + +INPUT_BUTTON_DOWN: int +''' +Constant value for the `down` button. + +.. versionadded:: 1.0.0 +''' + +INPUT_BUTTON_RIGHT: int +''' +Constant value for the `right` button. + +.. versionadded:: 1.0.0 +''' + +INPUT_BUTTON_LEFT: int +''' +Constant value for the `left` button. + +.. versionadded:: 1.0.0 +''' + +INPUT_BUTTON_OK: int +''' +Constant value for the `ok` button. + +.. versionadded:: 1.0.0 +''' + +INPUT_BUTTON_BACK: int +''' +Constant value for the `back` button. + +.. versionadded:: 1.0.0 +''' + +INPUT_TYPE_PRESS: int +''' +Constant value for the `press` event of a button. + +.. versionadded:: 1.0.0 +''' + +INPUT_TYPE_RELEASE: int +''' +Constant value for the `release` event of a button. + +.. versionadded:: 1.0.0 +''' + +INPUT_TYPE_SHORT: int +''' +Constant value for the `short` press event of a button. + +.. versionadded:: 1.0.0 +''' + +INPUT_TYPE_LONG: int +''' +Constant value for the `long` press event of a button. + +.. versionadded:: 1.0.0 +''' + +INPUT_TYPE_REPEAT: int +''' +Constant value for the `repeat` press event of a button. + +.. versionadded:: 1.0.0 +''' + +def on_input() -> Callable[[int, int], None]: + ''' + Decorate a function to be used as input handler. The decorated function will be invoked upon interaction with one of the buttons on the Flipper. + + .. versionadded:: 1.0.0 + + .. code-block:: + + import flipperzero as f0 + + @f0.on_input + def input_handler(button, type): + if button == f0.INPUT_BUTTON_BACK: + if type == f0.INPUT_TYPE_LONG: + ... + + .. warning:: + + You can only decorate one function per application. + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_light.py b/non_catalog_apps/mp_flipper/flipperzero/_light.py new file mode 100644 index 00000000..3fc9bbe0 --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_light.py @@ -0,0 +1,91 @@ +from typing import Callable + +LIGHT_RED: int +''' +Constant value for the red LED light. + +.. versionadded:: 1.0.0 +''' + +LIGHT_GREEN: int +''' +Constant value for the green LED light. + +.. versionadded:: 1.0.0 +''' + +LIGHT_BLUE: int +''' +Constant value for the blue LED light. + +.. versionadded:: 1.0.0 +''' + +LIGHT_BACKLIGHT: int +''' +Constant value for the display backlight. + +.. versionadded:: 1.0.0 +''' + +def light_set(light: int, brightness: int) -> None: + ''' + Control the RGB LED on your Flipper. You can also set the brightness of multiple channels at once using bitwise operations. + The ``brightness`` parameter accepts values from 0 (light off) to 255 (very bright). + + :param light: The RGB channels to set. + :param brightness: The brightness to use. + + .. versionadded:: 1.0.0 + + .. code-block:: + + import flipperzero as f0 + + f0.light_set(f0.LIGHT_RED | f0.LIGHT_GREEN, 250) + + .. tip:: + + You can use up to seven colors using `additive mixing `_. + ''' + pass + +def light_blink_start(light: int, brightness: int, on_time: int, period: int) -> None: + ''' + Let the RGB LED blink. You can define the total duration of a blink period and the duration, the LED is active during a blink period. + Hence, ``on_time`` must be smaller than ``period``. This is a non-blocking operation. The LED will continue to blink until you call :func:`light_blink_stop`. + + :param light: The RGB channels to set. + :param brightness: The brightness to use. + :param on_time: The LED's active duration in milliseconds. + :param period: Total duration of a blink period in milliseconds. + + .. versionadded:: 1.0.0 + + .. code-block:: + + import flipperzero as f0 + + f0.light_blink_start(f0.LIGHT_RED, 150, 100, 200) + ''' + pass + +def light_blink_set_color(light: int) -> None: + ''' + Change the RGB LED's color while blinking. This is a non-blocking operation. + Be aware, that you must start the blinking procedure first by using the :func:`light_blink_start` function. + Call the :func:`light_blink_stop` function to stop the blinking LED. + + :param light: The RGB channels to set. + + .. versionadded:: 1.0.0 + ''' + pass + +def light_blink_stop() -> None: + ''' + Stop the blinking LED. + + .. versionadded:: 1.0.0 + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_pwm.py b/non_catalog_apps/mp_flipper/flipperzero/_pwm.py new file mode 100644 index 00000000..2eeb2ce4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_pwm.py @@ -0,0 +1,37 @@ +def pwm_start(pin: int, frequency: int, duty: int) -> bool: + ''' + Start or change the PWM signal on the corresponding GPIO pin. + + :param pin: The pin to read (e.g. :const:`GPIO_PIN_PA7`). + :param frequency: The frequency to set in Hz. + :param duty: The duty cycle per period in percent. + :returns: :const:`True` on success, :const:`False` otherwise. + + .. versionadded:: 1.3.0 + + .. warning:: + + You don't have to initialize the pin first. + ''' + pass + +def pwm_stop(pin: int) -> None: + ''' + Stop the PWM signal on the corresponding GPIO pin. + + :param pin: The pin to use (e.g. :const:`GPIO_PIN_PA7`). + + .. versionadded:: 1.3.0 + ''' + pass + +def pwm_is_running(pin: int) -> bool: + ''' + Check if the corresponding GPIO pin has a PWM signal output. + + :param pin: The pin to check (e.g. :const:`GPIO_PIN_PA7`). + :returns: :const:`True` on success, :const:`False` otherwise. + + .. versionadded:: 1.3.0 + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_speaker.py b/non_catalog_apps/mp_flipper/flipperzero/_speaker.py new file mode 100644 index 00000000..b9a4daa6 --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_speaker.py @@ -0,0 +1,851 @@ +''' +Python script for notes generation + +# coding: utf-8 +# Python script for notes generation + +from typing import List + +note_names: List = ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B'] + +for octave in range(9): + for name in note_names: + print("SPEAKER_NOTE_%s%s: float" % (name, octave)) + print('\'\'\'') + print('The musical note %s\\ :sub:`0` as frequency in `Hz`.\n' % (name if len(name) == 1 else (name[0]+'#'))) + print('.. versionadded:: 1.2.0') + print('\'\'\'\n') +''' + +SPEAKER_NOTE_C0: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS0: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D0: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS0: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E0: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F0: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS0: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G0: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS0: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A0: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS0: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B0: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C1: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS1: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D1: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS1: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E1: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F1: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS1: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G1: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS1: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A1: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS1: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B1: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C2: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS2: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D2: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS2: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E2: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F2: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS2: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G2: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS2: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A2: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS2: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B2: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C3: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS3: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D3: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS3: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E3: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F3: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS3: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G3: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS3: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A3: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS3: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B3: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C4: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS4: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D4: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS4: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E4: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F4: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS4: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G4: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS4: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A4: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS4: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B4: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C5: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS5: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D5: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS5: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E5: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F5: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS5: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G5: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS5: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A5: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS5: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B5: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C6: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS6: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D6: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS6: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E6: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F6: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS6: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G6: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS6: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A6: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS6: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B6: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C7: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS7: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D7: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS7: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E7: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F7: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS7: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G7: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS7: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A7: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS7: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B7: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_C8: float +''' +The musical note C\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_CS8: float +''' +The musical note C#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_D8: float +''' +The musical note D\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_DS8: float +''' +The musical note D#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_E8: float +''' +The musical note E\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_F8: float +''' +The musical note F\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_FS8: float +''' +The musical note F#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_G8: float +''' +The musical note G\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_GS8: float +''' +The musical note G#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_A8: float +''' +The musical note A\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_AS8: float +''' +The musical note A#\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_NOTE_B8: float +''' +The musical note B\ :sub:`0` as frequency in `Hz`. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_VOLUME_MIN: float +''' +The minimal volume value. + +.. versionadded:: 1.2.0 +''' + +SPEAKER_VOLUME_MAX: float +''' +The maximum volume value. + +.. versionadded:: 1.2.0 +''' + +def speaker_start(frequency: float, volume: float) -> bool: + ''' + Output a steady tone of a defined frequency and volume on the Flipper's speaker. + This is a non-blocking operation. The tone will continue until you call :func:`speaker_stop`. + The ``volume`` parameter accepts values from :py:const:`SPEAKER_VOLUME_MIN` (silent) up to :py:const:`SPEAKER_VOLUME_MAX` (very loud). + + :param frequency: The frequency to play in `Hz `_. + :param volume: The volume to use. + :returns: :const:`True` if the speaker was acquired. + + .. versionadded:: 1.0.0 + + .. code-block:: + + import flipperzero as f0 + + f0.speaker_start(50.0, 0.8) + ''' + pass + +def speaker_set_volume(volume: float) -> bool: + ''' + Set the speaker's volume while playing a tone. This is a non-blocking operation. + The tone will continue until you call :func:`speaker_stop`. + The ``volume`` parameter accepts values from 0.0 (silent) up to 1.0 (very loud). + + :param volume: The volume to use. + :returns: :const:`True` if the speaker was acquired. + + .. versionadded:: 1.0.0 + + This function can be used to play `nice` sounds: + + .. code-block:: + + import time + import flipperzero as f0 + + volume = 0.8 + + f0.speaker_start(100.0, volume) + + for _ in range(0, 150): + volume *= 0.9945679 + + f0.speaker_set_volume(volume) + + time.sleep_ms(1) + + f0.speaker_stop() + ''' + pass + +def speaker_stop() -> bool: + ''' + Stop the speaker output. + + :returns: :const:`True` if the speaker was successfully released. + + .. versionadded:: 1.0.0 + ''' + pass diff --git a/non_catalog_apps/mp_flipper/flipperzero/_vibro.py b/non_catalog_apps/mp_flipper/flipperzero/_vibro.py new file mode 100644 index 00000000..5a1ee356 --- /dev/null +++ b/non_catalog_apps/mp_flipper/flipperzero/_vibro.py @@ -0,0 +1,10 @@ +def vibro_set(state: bool) -> bool: + ''' + Turn vibration on or off. This is a non-blocking operation. The vibration motor will continue to run until you stop it. + + :param state: :const:`True` to turn on vibration. + :returns: :const:`True` if vibration is on. + + .. versionadded:: 1.0.0 + ''' + pass diff --git a/non_catalog_apps/mp_flipper/icon.png b/non_catalog_apps/mp_flipper/icon.png new file mode 100644 index 00000000..3ac49131 Binary files /dev/null and b/non_catalog_apps/mp_flipper/icon.png differ diff --git a/non_catalog_apps/mp_flipper/images/ButtonCenter_7x7.png b/non_catalog_apps/mp_flipper/images/ButtonCenter_7x7.png new file mode 100644 index 00000000..e1e015ab Binary files /dev/null and b/non_catalog_apps/mp_flipper/images/ButtonCenter_7x7.png differ diff --git a/non_catalog_apps/mp_flipper/images/Pin_back_arrow_10x8.png b/non_catalog_apps/mp_flipper/images/Pin_back_arrow_10x8.png new file mode 100644 index 00000000..64b25db5 Binary files /dev/null and b/non_catalog_apps/mp_flipper/images/Pin_back_arrow_10x8.png differ diff --git a/non_catalog_apps/mp_flipper/images/qrcode.png b/non_catalog_apps/mp_flipper/images/qrcode.png new file mode 100644 index 00000000..da510a9f Binary files /dev/null and b/non_catalog_apps/mp_flipper/images/qrcode.png differ diff --git a/non_catalog_apps/mp_flipper/images/splash.png b/non_catalog_apps/mp_flipper/images/splash.png new file mode 100644 index 00000000..8d97f7eb Binary files /dev/null and b/non_catalog_apps/mp_flipper/images/splash.png differ diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_context.h b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_context.h new file mode 100644 index 00000000..ec7fc717 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_context.h @@ -0,0 +1,47 @@ +#include + +#include +#include +#include + +#include + +#define MP_FLIPPER_GPIO_PIN_OFF (1 << 15) +#define MP_FLIPPER_GPIO_PIN_BLOCKED (1 << 7) +#define MP_FLIPPER_GPIO_PIN_PWM ((MP_FLIPPER_GPIO_PIN_BLOCKED) | (1 << 8)) + +typedef uint16_t mp_flipper_gpio_pin_t; + +#define MP_FLIPPER_INFRARED_RX_BUFFER_SIZE (1024) + +typedef struct { + uint16_t size; + uint32_t* buffer; + uint16_t pointer; + bool running; +} mp_flipper_infrared_rx_t; + +typedef struct { + size_t size; + void* signal; + size_t index; + uint32_t repeat; + bool level; + mp_flipper_infrared_signal_tx_provider provider; +} mp_flipper_infrared_tx_t; + +typedef struct { + Gui* gui; + ViewPort* view_port; + Canvas* canvas; + FuriPubSub* input_event_queue; + FuriPubSubSubscription* input_event; + DialogMessage* dialog_message; + const char* dialog_message_button_left; + const char* dialog_message_button_center; + const char* dialog_message_button_right; + FuriHalAdcHandle* adc_handle; + mp_flipper_gpio_pin_t* gpio_pins; + mp_flipper_infrared_rx_t* infrared_rx; + mp_flipper_infrared_tx_t* infrared_tx; +} mp_flipper_context_t; diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_helper.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_helper.c new file mode 100644 index 00000000..d4c51911 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_helper.c @@ -0,0 +1,56 @@ +#include +#include + +#include +#include + +mp_flipper_import_stat_t mp_flipper_try_resolve_filesystem_path(FuriString* path) { + const char* path_str = furi_string_get_cstr(path); + FuriString* _path = furi_string_alloc_printf("%s", path_str); + Storage* storage = furi_record_open(RECORD_STORAGE); + + mp_flipper_import_stat_t stat = MP_FLIPPER_IMPORT_STAT_FILE; + + FS_Error error; + FileInfo info; + + do { + // make path absolute + if(!furi_string_start_with_str(_path, "/")) { + furi_string_printf(_path, "%s/%s", mp_flipper_root_module_path, path_str); + } + + // check if file or folder exists + error = storage_common_stat(storage, furi_string_get_cstr(_path), &info); + if(error == FSE_OK) { + break; + } + + // check for existing python file + furi_string_cat_str(_path, ".py"); + + error = storage_common_stat(storage, furi_string_get_cstr(_path), &info); + if(error == FSE_OK) { + break; + } + } while(false); + + // file or folder missing + if(error == FSE_NOT_EXIST) { + stat = MP_FLIPPER_IMPORT_STAT_NO_EXIST; + } + // abort on error + else if(error != FSE_OK) { + mp_flipper_raise_os_error_with_filename(MP_ENOENT, furi_string_get_cstr(path)); + } + // path points to directory + else if((info.flags & FSF_DIRECTORY) == FSF_DIRECTORY) { + stat = MP_FLIPPER_IMPORT_STAT_DIR; + } + + furi_record_close(RECORD_STORAGE); + + furi_string_move(path, _path); + + return stat; +} \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_helper.h b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_helper.h new file mode 100644 index 00000000..8d991ebe --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_helper.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include + +mp_flipper_import_stat_t mp_flipper_try_resolve_filesystem_path(FuriString* path); \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_reader.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_reader.c new file mode 100644 index 00000000..db18657f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_file_reader.c @@ -0,0 +1,76 @@ +#include + +#include +#include + +#include +#include + +#include "mp_flipper_file_helper.h" + +typedef struct { + size_t pointer; + FuriString* content; + size_t size; +} FileReaderContext; + +inline void* mp_flipper_file_reader_context_alloc(const char* filename) { + FuriString* path = furi_string_alloc_printf("%s", filename); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + FileReaderContext* ctx = NULL; + + do { + if(mp_flipper_try_resolve_filesystem_path(path) == MP_FLIPPER_IMPORT_STAT_NO_EXIST) { + furi_string_free(path); + + mp_flipper_raise_os_error_with_filename(MP_ENOENT, filename); + } + + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + storage_file_free(file); + + mp_flipper_raise_os_error_with_filename(MP_ENOENT, filename); + + break; + } + + ctx = malloc(sizeof(FileReaderContext)); + + ctx->pointer = 0; + ctx->content = furi_string_alloc(); + ctx->size = storage_file_size(file); + + char character = '\0'; + + for(size_t i = 0; i < ctx->size; i++) { + storage_file_read(file, &character, 1); + + furi_string_push_back(ctx->content, character); + } + } while(false); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + furi_string_free(path); + + return ctx; +} + +inline uint32_t mp_flipper_file_reader_read(void* data) { + FileReaderContext* ctx = data; + + if(ctx->pointer >= ctx->size) { + return MP_FLIPPER_FILE_READER_EOF; + } + + return furi_string_get_char(ctx->content, ctx->pointer++); +} + +void mp_flipper_file_reader_close(void* data) { + FileReaderContext* ctx = data; + + furi_string_free(ctx->content); + + free(data); +} \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_halport.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_halport.c new file mode 100644 index 00000000..9ae96f8b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_halport.c @@ -0,0 +1,33 @@ +#include + +#include +#include + +#include +#include + +#include "mp_flipper_file_helper.h" + +inline void mp_flipper_stdout_tx_str(const char* str) { + printf("%s", str); +} + +inline void mp_flipper_stdout_tx_strn_cooked(const char* str, size_t len) { + printf("%.*s", len, str); +} + +inline mp_flipper_import_stat_t mp_flipper_import_stat(const char* path) { + FuriString* file_path = furi_string_alloc_printf("%s", path); + + mp_flipper_import_stat_t stat = mp_flipper_try_resolve_filesystem_path(file_path); + + stat = furi_string_end_with_str(file_path, path) ? stat : MP_FLIPPER_IMPORT_STAT_NO_EXIST; + + furi_string_free(file_path); + + return stat; +} + +inline size_t mp_flipper_gc_get_max_new_split(void) { + return memmgr_heap_get_max_free_block(); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_adc.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_adc.c new file mode 100644 index 00000000..d205a778 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_adc.c @@ -0,0 +1,47 @@ +#include + +#include +#include + +#include "mp_flipper_context.h" + +inline static FuriHalAdcChannel decode_pin_to_adc_channel(uint8_t pin) { + switch(pin) { + case MP_FLIPPER_GPIO_PIN_PC0: + return FuriHalAdcChannel1; + case MP_FLIPPER_GPIO_PIN_PC1: + return FuriHalAdcChannel2; + case MP_FLIPPER_GPIO_PIN_PC3: + return FuriHalAdcChannel4; + case MP_FLIPPER_GPIO_PIN_PA4: + return FuriHalAdcChannel9; + case MP_FLIPPER_GPIO_PIN_PA6: + return FuriHalAdcChannel11; + case MP_FLIPPER_GPIO_PIN_PA7: + return FuriHalAdcChannel12; + default: + return FuriHalAdcChannelNone; + } +} + +inline uint16_t mp_flipper_adc_read_pin(uint8_t raw_pin) { + mp_flipper_context_t* ctx = mp_flipper_context; + + FuriHalAdcChannel channel = decode_pin_to_adc_channel(raw_pin); + + if(channel == FuriHalAdcChannelNone) { + return 0; + } + + if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_MODE_ANALOG) { + return 0; + } + + return ctx->adc_handle ? furi_hal_adc_read(ctx->adc_handle, channel) : 0; +} + +inline float mp_flipper_adc_convert_to_voltage(uint16_t value) { + mp_flipper_context_t* ctx = mp_flipper_context; + + return ctx->adc_handle ? furi_hal_adc_convert_to_voltage(ctx->adc_handle, value) : 0.0f; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_canvas.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_canvas.c new file mode 100644 index 00000000..fe1fc825 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_canvas.c @@ -0,0 +1,108 @@ +#include +#include + +#include +#include + +#include "mp_flipper_context.h" + +static Align text_align_x = AlignLeft; +static Align text_align_y = AlignTop; + +inline uint8_t mp_flipper_canvas_width() { + mp_flipper_context_t* ctx = mp_flipper_context; + + return canvas_width(ctx->canvas); +} + +inline uint8_t mp_flipper_canvas_height() { + mp_flipper_context_t* ctx = mp_flipper_context; + + return canvas_height(ctx->canvas); +} + +inline uint8_t mp_flipper_canvas_text_width(const char* text) { + mp_flipper_context_t* ctx = mp_flipper_context; + + return canvas_string_width(ctx->canvas, text); +} + +inline uint8_t mp_flipper_canvas_text_height() { + mp_flipper_context_t* ctx = mp_flipper_context; + + return canvas_current_font_height(ctx->canvas); +} + +inline void mp_flipper_canvas_draw_dot(uint8_t x, uint8_t y) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_draw_dot(ctx->canvas, x, y); +} + +inline void mp_flipper_canvas_draw_box(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_draw_rbox(ctx->canvas, x, y, w, h, r); +} + +inline void mp_flipper_canvas_draw_frame(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_draw_rframe(ctx->canvas, x, y, w, h, r); +} + +inline void mp_flipper_canvas_draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_draw_line(ctx->canvas, x0, y0, x1, y1); +} + +inline void mp_flipper_canvas_draw_circle(uint8_t x, uint8_t y, uint8_t r) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_draw_circle(ctx->canvas, x, y, r); +} + +inline void mp_flipper_canvas_draw_disc(uint8_t x, uint8_t y, uint8_t r) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_draw_disc(ctx->canvas, x, y, r); +} + +inline void mp_flipper_canvas_set_font(uint8_t font) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_set_font(ctx->canvas, font == MP_FLIPPER_FONT_PRIMARY ? FontPrimary : FontSecondary); +} + +inline void mp_flipper_canvas_set_color(uint8_t color) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_set_color(ctx->canvas, color == MP_FLIPPER_COLOR_BLACK ? ColorBlack : ColorWhite); +} + +inline void mp_flipper_canvas_set_text(uint8_t x, uint8_t y, const char* text) { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_draw_str_aligned(ctx->canvas, x, y, text_align_x, text_align_y, text); +} + +inline void mp_flipper_canvas_set_text_align(uint8_t x, uint8_t y) { + Align align_x = x == MP_FLIPPER_ALIGN_BEGIN ? AlignLeft : AlignRight; + Align align_y = y == MP_FLIPPER_ALIGN_BEGIN ? AlignTop : AlignBottom; + + text_align_x = x == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_x; + text_align_y = y == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_y; +} + +inline void mp_flipper_canvas_update() { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_commit(ctx->canvas); +} + +inline void mp_flipper_canvas_clear() { + mp_flipper_context_t* ctx = mp_flipper_context; + + canvas_clear(ctx->canvas); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_dialog.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_dialog.c new file mode 100644 index 00000000..11b8433e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_dialog.c @@ -0,0 +1,101 @@ +#include + +#include +#include + +#include "mp_flipper_context.h" + +void mp_flipper_dialog_message_set_text( + const char* text, + uint8_t x, + uint8_t y, + uint8_t h, + uint8_t v) { + mp_flipper_context_t* ctx = mp_flipper_context; + + Align align_x = h == MP_FLIPPER_ALIGN_BEGIN ? AlignLeft : AlignRight; + Align align_y = v == MP_FLIPPER_ALIGN_BEGIN ? AlignTop : AlignBottom; + + align_x = h == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_x; + align_y = v == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_y; + + dialog_message_set_text(ctx->dialog_message, text, x, y, align_x, align_y); +} + +void mp_flipper_dialog_message_set_header( + const char* text, + uint8_t x, + uint8_t y, + uint8_t h, + uint8_t v) { + mp_flipper_context_t* ctx = mp_flipper_context; + + Align align_x = h == MP_FLIPPER_ALIGN_BEGIN ? AlignLeft : AlignRight; + Align align_y = v == MP_FLIPPER_ALIGN_BEGIN ? AlignTop : AlignBottom; + + align_x = h == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_x; + align_y = v == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_y; + + dialog_message_set_header(ctx->dialog_message, text, x, y, align_x, align_y); +} + +void mp_flipper_dialog_message_set_button(const char* text, uint8_t button) { + mp_flipper_context_t* ctx = mp_flipper_context; + + // left button + if(button == MP_FLIPPER_INPUT_BUTTON_LEFT) { + ctx->dialog_message_button_left = text; + } + // center button + else if(button == MP_FLIPPER_INPUT_BUTTON_OK) { + ctx->dialog_message_button_center = text; + } + // right button + else if(button == MP_FLIPPER_INPUT_BUTTON_RIGHT) { + ctx->dialog_message_button_right = text; + } + + dialog_message_set_buttons( + ctx->dialog_message, + ctx->dialog_message_button_left, + ctx->dialog_message_button_center, + ctx->dialog_message_button_right); +} + +uint8_t mp_flipper_dialog_message_show() { + mp_flipper_context_t* ctx = mp_flipper_context; + + gui_direct_draw_release(ctx->gui); + + DialogsApp* dialog = furi_record_open(RECORD_DIALOGS); + + uint8_t button = dialog_message_show(dialog, ctx->dialog_message); + + furi_record_close(RECORD_DIALOGS); + + ctx->canvas = gui_direct_draw_acquire(ctx->gui); + + switch(button) { + case DialogMessageButtonLeft: + return MP_FLIPPER_INPUT_BUTTON_LEFT; + case DialogMessageButtonCenter: + return MP_FLIPPER_INPUT_BUTTON_OK; + case DialogMessageButtonRight: + return MP_FLIPPER_INPUT_BUTTON_RIGHT; + case DialogMessageButtonBack: + default: + return MP_FLIPPER_INPUT_BUTTON_BACK; + } +} + +void mp_flipper_dialog_message_clear() { + mp_flipper_context_t* ctx = mp_flipper_context; + + dialog_message_free(ctx->dialog_message); + + ctx->dialog_message = dialog_message_alloc(); + + ctx->dialog_message_button_left = NULL; + ctx->dialog_message_button_center = NULL; + ctx->dialog_message_button_right = NULL; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_gpio.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_gpio.c new file mode 100644 index 00000000..41a1db49 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_gpio.c @@ -0,0 +1,175 @@ +#include + +#include +#include + +#include "mp_flipper_context.h" + +#define NO_VALUE (-1) + +static const GpioPin* decode_pin(uint8_t pin) { + switch(pin) { + case MP_FLIPPER_GPIO_PIN_PC0: + return &gpio_ext_pc0; + case MP_FLIPPER_GPIO_PIN_PC1: + return &gpio_ext_pc1; + case MP_FLIPPER_GPIO_PIN_PC3: + return &gpio_ext_pc3; + case MP_FLIPPER_GPIO_PIN_PB2: + return &gpio_ext_pb2; + case MP_FLIPPER_GPIO_PIN_PB3: + return &gpio_ext_pb3; + case MP_FLIPPER_GPIO_PIN_PA4: + return &gpio_ext_pa4; + case MP_FLIPPER_GPIO_PIN_PA6: + return &gpio_ext_pa6; + case MP_FLIPPER_GPIO_PIN_PA7: + return &gpio_ext_pa7; + default: + return NULL; + } +} + +static inline const GpioMode decode_mode(uint8_t mode) { + switch(mode) { + case MP_FLIPPER_GPIO_MODE_INPUT: + return GpioModeInput; + case MP_FLIPPER_GPIO_MODE_OUTPUT_PUSH_PULL: + return GpioModeOutputPushPull; + case MP_FLIPPER_GPIO_MODE_OUTPUT_OPEN_DRAIN: + return GpioModeOutputOpenDrain; + case MP_FLIPPER_GPIO_MODE_ANALOG: + return GpioModeAnalog; + case MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL: + return GpioModeInterruptFall; + case MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE: + return GpioModeInterruptRise; + } + + if((mode & MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL) && + (mode & MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE)) { + return GpioModeInterruptRiseFall; + } + + return NO_VALUE; +} + +static inline const GpioPull decode_pull(uint8_t pull) { + switch(pull) { + case MP_FLIPPER_GPIO_PULL_NO: + return GpioPullNo; + case MP_FLIPPER_GPIO_PULL_UP: + return GpioPullUp; + case MP_FLIPPER_GPIO_PULL_DOWN: + return GpioPullDown; + default: + return NO_VALUE; + } +} + +static inline const GpioSpeed decode_speed(uint8_t speed) { + switch(speed) { + case MP_FLIPPER_GPIO_SPEED_LOW: + return GpioSpeedLow; + case MP_FLIPPER_GPIO_SPEED_MEDIUM: + return GpioSpeedMedium; + case MP_FLIPPER_GPIO_SPEED_HIGH: + return GpioSpeedHigh; + case MP_FLIPPER_GPIO_SPEED_VERY_HIGH: + return GpioSpeedVeryHigh; + default: + return NO_VALUE; + } +} + +inline bool mp_flipper_gpio_init_pin( + uint8_t raw_pin, + uint8_t raw_mode, + uint8_t raw_pull, + uint8_t raw_speed) { + mp_flipper_context_t* ctx = mp_flipper_context; + + const GpioPin* pin = decode_pin(raw_pin); + const GpioMode mode = decode_mode(raw_mode); + const GpioPull pull = decode_pull(raw_pull); + const GpioSpeed speed = decode_speed(raw_speed); + + if(pin == NULL || mode == NO_VALUE || pull == NO_VALUE || speed == NO_VALUE) { + return false; + } + + if(ctx->gpio_pins[raw_pin] & MP_FLIPPER_GPIO_PIN_BLOCKED) { + return false; + } + + furi_hal_gpio_init(pin, mode, pull, speed); + + if(raw_mode & (MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL | MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE)) { + furi_hal_gpio_add_int_callback(pin, mp_flipper_on_gpio, (void*)raw_pin); + furi_hal_gpio_enable_int_callback(pin); + } else { + furi_hal_gpio_disable_int_callback(pin); + furi_hal_gpio_remove_int_callback(pin); + } + + if(raw_mode == MP_FLIPPER_GPIO_MODE_ANALOG) { + ctx->adc_handle = furi_hal_adc_acquire(); + + furi_hal_adc_configure(ctx->adc_handle); + } + + ctx->gpio_pins[raw_pin] = raw_mode; + + return true; +} + +inline void mp_flipper_gpio_deinit_pin(uint8_t raw_pin) { + const mp_flipper_context_t* ctx = mp_flipper_context; + + const GpioPin* pin = decode_pin(raw_pin); + + if(pin == NULL) { + return; + } + + if(ctx->gpio_pins[raw_pin] & (MP_FLIPPER_GPIO_PIN_BLOCKED | MP_FLIPPER_GPIO_PIN_OFF)) { + return; + } + + furi_hal_gpio_disable_int_callback(pin); + furi_hal_gpio_remove_int_callback(pin); + furi_hal_gpio_init_simple(pin, GpioModeAnalog); + + ctx->gpio_pins[raw_pin] = MP_FLIPPER_GPIO_PIN_OFF; +} + +inline void mp_flipper_gpio_set_pin(uint8_t raw_pin, bool state) { + const mp_flipper_context_t* ctx = mp_flipper_context; + + const GpioPin* pin = decode_pin(raw_pin); + + if(pin == NULL) { + return; + } + + if(ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_MODE_OUTPUT_PUSH_PULL || + ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_MODE_OUTPUT_OPEN_DRAIN) { + furi_hal_gpio_write(pin, state); + } +} + +inline bool mp_flipper_gpio_get_pin(uint8_t raw_pin) { + const mp_flipper_context_t* ctx = mp_flipper_context; + + const GpioPin* pin = decode_pin(raw_pin); + + if(pin == NULL) { + return false; + } + + if(ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_MODE_INPUT) { + return furi_hal_gpio_read(pin); + } else { + return false; + } +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_infrared.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_infrared.c new file mode 100644 index 00000000..631bd9b8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_infrared.c @@ -0,0 +1,119 @@ +#include +#include + +#include +#include + +#include "mp_flipper_context.h" + +inline static void on_rx(void* ctx, bool level, uint32_t duration) { + mp_flipper_infrared_rx_t* session = ctx; + + if(session->pointer == 0 && !level) { + return; + } + + if(session->pointer < session->size) { + session->buffer[session->pointer] = duration; + + session->pointer++; + } else { + session->running = false; + } +} + +inline static void on_rx_timeout(void* ctx) { + mp_flipper_infrared_rx_t* session = ctx; + + session->running = false; +} + +inline static FuriHalInfraredTxGetDataState on_tx(void* ctx, uint32_t* duration, bool* level) { + mp_flipper_infrared_tx_t* session = ctx; + + *duration = session->provider(session->signal, session->index); + *level = session->level; + + session->index++; + + if(session->index >= session->size) { + session->index = 0; + session->repeat--; + } + + session->level = !session->level; + + return session->repeat > 0 ? FuriHalInfraredTxGetDataStateOk : + FuriHalInfraredTxGetDataStateLastDone; +} + +inline uint32_t* mp_flipper_infrared_receive(uint32_t timeout, size_t* length) { + const mp_flipper_context_t* ctx = mp_flipper_context; + + mp_flipper_infrared_rx_t* session = ctx->infrared_rx; + + if(!furi_hal_infrared_is_busy()) { + session->pointer = 0; + session->running = true; + + furi_hal_infrared_async_rx_set_capture_isr_callback(on_rx, session); + furi_hal_infrared_async_rx_set_timeout_isr_callback(on_rx_timeout, session); + + furi_hal_infrared_async_rx_start(); + + furi_hal_infrared_async_rx_set_timeout(timeout); + + while(session->running) { + furi_delay_tick(10); + } + + furi_hal_infrared_async_rx_stop(); + + *length = session->pointer; + } else { + *length = 0; + } + + return session->buffer; +} + +inline bool mp_flipper_infrared_transmit( + void* signal, + size_t length, + mp_flipper_infrared_signal_tx_provider callback, + uint32_t repeat, + uint32_t frequency, + float duty, + bool use_external_pin) { + if(furi_hal_infrared_is_busy() || length == 0) { + return false; + } + + const mp_flipper_context_t* ctx = mp_flipper_context; + + mp_flipper_infrared_tx_t* session = ctx->infrared_tx; + + session->index = 0; + session->level = true; + session->provider = callback; + session->signal = signal; + session->repeat = repeat; + session->size = length; + + const FuriHalInfraredTxPin output = use_external_pin ? FuriHalInfraredTxPinExtPA7 : + FuriHalInfraredTxPinInternal; + + furi_hal_infrared_set_tx_output(output); + + furi_hal_infrared_async_tx_set_data_isr_callback(on_tx, session); + + furi_hal_infrared_async_tx_start(frequency, duty); + + furi_hal_infrared_async_tx_wait_termination(); + + return true; +} + +inline bool mp_flipper_infrared_is_busy() { + return furi_hal_infrared_is_busy(); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_light.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_light.c new file mode 100644 index 00000000..943a7905 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_light.c @@ -0,0 +1,40 @@ +#include + +#include + +static Light decode_light(uint8_t value) { + Light light = 0; + + light += value & MP_FLIPPER_LED_RED ? LightRed : 0; + light += value & MP_FLIPPER_LED_GREEN ? LightGreen : 0; + light += value & MP_FLIPPER_LED_BLUE ? LightBlue : 0; + light += value & MP_FLIPPER_LED_BACKLIGHT ? LightBacklight : 0; + + return light; +} + +inline void mp_flipper_light_set(uint8_t raw_light, uint8_t brightness) { + Light light = decode_light(raw_light); + + furi_hal_light_set(light, brightness); +} + +inline void mp_flipper_light_blink_start( + uint8_t raw_light, + uint8_t brightness, + uint16_t on_time, + uint16_t period) { + Light light = decode_light(raw_light); + + furi_hal_light_blink_start(light, brightness, on_time, period); +} + +inline void mp_flipper_light_blink_set_color(uint8_t raw_light) { + Light light = decode_light(raw_light); + + furi_hal_light_blink_set_color(light); +} + +inline void mp_flipper_light_blink_stop() { + furi_hal_light_blink_stop(); +} \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_pwm.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_pwm.c new file mode 100644 index 00000000..41b7b7e9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_pwm.c @@ -0,0 +1,79 @@ +#include +#include + +#include +#include + +#include "mp_flipper_context.h" + +#define NO_VALUE (-1) + +inline static const FuriHalPwmOutputId decode_pin_to_output_id(uint8_t pin) { + switch(pin) { + case MP_FLIPPER_GPIO_PIN_PA4: + return FuriHalPwmOutputIdLptim2PA4; + case MP_FLIPPER_GPIO_PIN_PA7: + return FuriHalPwmOutputIdTim1PA7; + default: + return NO_VALUE; + } +} + +inline bool mp_flipper_pwm_start(uint8_t raw_pin, uint32_t frequency, uint8_t duty) { + const mp_flipper_context_t* ctx = mp_flipper_context; + + FuriHalPwmOutputId channel = decode_pin_to_output_id(raw_pin); + + if(channel == NO_VALUE) { + return false; + } + + if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_OFF && + ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_PWM) { + return false; + } + + if(ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_PIN_OFF) { + furi_hal_pwm_start(channel, frequency, duty); + } else { + furi_hal_pwm_set_params(channel, frequency, duty); + } + + ctx->gpio_pins[raw_pin] = MP_FLIPPER_GPIO_PIN_PWM; + + return true; +} + +inline void mp_flipper_pwm_stop(uint8_t raw_pin) { + const mp_flipper_context_t* ctx = mp_flipper_context; + + FuriHalPwmOutputId channel = decode_pin_to_output_id(raw_pin); + + if(channel == NO_VALUE) { + return; + } + + if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_PWM) { + return; + } + + furi_hal_pwm_stop(channel); + + ctx->gpio_pins[raw_pin] = MP_FLIPPER_GPIO_PIN_OFF; +} + +inline bool mp_flipper_pwm_is_running(uint8_t raw_pin) { + const mp_flipper_context_t* ctx = mp_flipper_context; + + FuriHalPwmOutputId channel = decode_pin_to_output_id(raw_pin); + + if(channel == NO_VALUE) { + return false; + } + + if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_PWM) { + return false; + } + + return furi_hal_pwm_is_running(channel); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_speaker.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_speaker.c new file mode 100644 index 00000000..5a47779b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_speaker.c @@ -0,0 +1,35 @@ +#include + +#include + +inline bool mp_flipper_speaker_start(float frequency, float volume) { + if(furi_hal_speaker_acquire(100)) { + furi_hal_speaker_start(frequency, volume); + + return true; + } + + return false; +} + +inline bool mp_flipper_speaker_set_volume(float volume) { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_set_volume(volume); + + return true; + } + + return false; +} + +inline bool mp_flipper_speaker_stop() { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + + furi_hal_speaker_release(); + + return true; + } + + return false; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_vibro.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_vibro.c new file mode 100644 index 00000000..c851a2d3 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_vibro.c @@ -0,0 +1,7 @@ +#include + +#include + +inline void mp_flipper_vibro(bool state) { + furi_hal_vibro_on(state); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modrandom.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modrandom.c new file mode 100644 index 00000000..974fa146 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modrandom.c @@ -0,0 +1,9 @@ +#include + +#include + +inline uint32_t mp_flipper_seed_init() { + furi_hal_random_init(); + + return furi_hal_random_get(); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modtime.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modtime.c new file mode 100644 index 00000000..5f14d564 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_modtime.c @@ -0,0 +1,24 @@ +#include +#include + +#include + +inline uint32_t mp_flipper_get_timestamp() { + return furi_hal_rtc_get_timestamp(); +} + +inline uint32_t mp_flipper_get_tick_frequency() { + return furi_kernel_get_tick_frequency(); +} + +inline uint32_t mp_flipper_get_tick() { + return furi_get_tick(); +} + +inline void mp_flipper_delay_ms(uint32_t ms) { + furi_delay_ms(ms); +} + +inline void mp_flipper_delay_us(uint32_t us) { + furi_delay_us(us); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_polyfill.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_polyfill.c new file mode 100644 index 00000000..4bf5415c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_polyfill.c @@ -0,0 +1,398 @@ +#include +#include +#include +#include +#include + +#define POLYFILL_FUN_1(name, ret, arg1) ret name(arg1) +#define POLYFILL_FUN_2(name, ret, arg1, arg2) ret name(arg1, arg2) +#define POLYFILL_FUN_3(name, ret, arg1, arg2, arg3) ret name(arg1, arg2, arg3) +#define POLYFILL_FUN_4(name, ret, arg1, arg2, arg3, arg4) ret name(arg1, arg2, arg3, arg4) + +#ifndef __aeabi_dcmple +POLYFILL_FUN_2(__aeabi_dcmple, double, double, double) { +} +#endif + +#ifndef __aeabi_dcmplt +POLYFILL_FUN_2(__aeabi_dcmplt, double, double, double) { +} +#endif + +#ifndef __aeabi_dcmpun +POLYFILL_FUN_2(__aeabi_dcmpun, double, double, double) { +} +#endif + +#ifndef __aeabi_dcmpeq +POLYFILL_FUN_2(__aeabi_dcmpeq, double, double, double) { +} +#endif + +#ifndef __aeabi_dmul +double __aeabi_dmul(double x, double y) { + return x * y; +} +#endif + +#ifndef __aeabi_dadd +POLYFILL_FUN_2(__aeabi_dadd, double, double x, double y) { + return x + y; +} +#endif + +#ifndef __aeabi_ddiv +POLYFILL_FUN_2(__aeabi_ddiv, double, double x, double y) { + return x / y; +} +#endif + +#ifndef __aeabi_l2d +POLYFILL_FUN_1(__aeabi_l2d, double, long x) { + return x; +} +#endif + +#ifndef __aeabi_f2d +POLYFILL_FUN_1(__aeabi_f2d, double, float x) { + return x; +} +#endif + +#ifndef __aeabi_dsub +POLYFILL_FUN_2(__aeabi_dsub, double, double x, double y) { + return x - y; +} +#endif + +#ifndef __aeabi_dcmpge +POLYFILL_FUN_2(__aeabi_dcmpge, double, double, double) { +} +#endif + +#ifndef __aeabi_i2d +POLYFILL_FUN_1(__aeabi_i2d, double, int x) { + return x; +} +#endif + +#ifndef __aeabi_dcmpgt +POLYFILL_FUN_1(__aeabi_dcmpgt, double, double) { +} +#endif + +#ifndef __aeabi_d2iz +POLYFILL_FUN_1(__aeabi_d2iz, long int, double x) { + return x; +} +#endif + +#ifndef __aeabi_d2lz +POLYFILL_FUN_1(__aeabi_d2lz, long, double x) { + return x; +} +#endif + +#ifndef __aeabi_d2uiz +POLYFILL_FUN_1(__aeabi_d2uiz, unsigned long int, double x) { + return x; +} +#endif + +#ifndef __aeabi_d2f +POLYFILL_FUN_1(__aeabi_d2f, float, double x) { + return x; +} +#endif + +#ifndef __aeabi_ldivmod +POLYFILL_FUN_2(__aeabi_ldivmod, long, long, long) { +} +#endif + +#ifndef strtox +POLYFILL_FUN_4(strtox, long long unsigned int, const char*, char**, int, long long unsigned int) { +} +#endif + +#if FLT_EVAL_METHOD == 0 || FLT_EVAL_METHOD == 1 +#define EPS DBL_EPSILON +#elif FLT_EVAL_METHOD == 2 +#define EPS LDBL_EPSILON +#endif +static const double_t toint = 1 / EPS; + +#ifndef FORCE_EVAL +#define FORCE_EVAL(x) \ + do { \ + if(sizeof(x) == sizeof(float)) { \ + volatile float __x; \ + __x = (x); \ + (void)__x; \ + } else if(sizeof(x) == sizeof(double)) { \ + volatile double __x; \ + __x = (x); \ + (void)__x; \ + } else { \ + volatile long double __x; \ + __x = (x); \ + (void)__x; \ + } \ + } while(0) +#endif + +#ifndef floorf +float floorf(float x) { + union { + float f; + uint32_t i; + } u = {x}; + int e = (int)(u.i >> 23 & 0xff) - 0x7f; + uint32_t m; + + if(e >= 23) return x; + if(e >= 0) { + m = 0x007fffff >> e; + if((u.i & m) == 0) return x; + FORCE_EVAL(x + 0x1p120f); + if(u.i >> 31) u.i += m; + u.i &= ~m; + } else { + FORCE_EVAL(x + 0x1p120f); + if(u.i >> 31 == 0) + u.i = 0; + else if(u.i << 1) + u.f = -1.0; + } + return u.f; +} +#endif + +#ifndef floor +double floor(double x) { + union { + double f; + uint64_t i; + } u = {x}; + int e = u.i >> 52 & 0x7ff; + double_t y; + + if(e >= 0x3ff + 52 || x == 0) return x; + /* y = int(x) - x, where int(x) is an integer neighbor of x */ + if(u.i >> 63) + y = x - toint + toint - x; + else + y = x + toint - toint - x; + /* special case because of non-nearest rounding modes */ + if(e <= 0x3ff - 1) { + FORCE_EVAL(y); + return u.i >> 63 ? -1 : 0; + } + if(y > 0) return x + y - 1; + return x + y; +} +#endif + +#ifndef fmodf +float fmodf(float x, float y) { + union { + float f; + uint32_t i; + } ux = {x}, uy = {y}; + int ex = ux.i >> 23 & 0xff; + int ey = uy.i >> 23 & 0xff; + uint32_t sx = ux.i & 0x80000000; + uint32_t i; + uint32_t uxi = ux.i; + + if(uy.i << 1 == 0 || isnan(y) || ex == 0xff) return (x * y) / (x * y); + if(uxi << 1 <= uy.i << 1) { + if(uxi << 1 == uy.i << 1) return 0 * x; + return x; + } + + /* normalize x and y */ + if(!ex) { + for(i = uxi << 9; i >> 31 == 0; ex--, i <<= 1) + ; + uxi <<= -ex + 1; + } else { + uxi &= -1U >> 9; + uxi |= 1U << 23; + } + if(!ey) { + for(i = uy.i << 9; i >> 31 == 0; ey--, i <<= 1) + ; + uy.i <<= -ey + 1; + } else { + uy.i &= -1U >> 9; + uy.i |= 1U << 23; + } + + /* x mod y */ + for(; ex > ey; ex--) { + i = uxi - uy.i; + if(i >> 31 == 0) { + if(i == 0) return 0 * x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if(i >> 31 == 0) { + if(i == 0) return 0 * x; + uxi = i; + } + for(; uxi >> 23 == 0; uxi <<= 1, ex--) + ; + + /* scale result up */ + if(ex > 0) { + uxi -= 1U << 23; + uxi |= (uint32_t)ex << 23; + } else { + uxi >>= -ex + 1; + } + uxi |= sx; + ux.i = uxi; + return ux.f; +} +#endif + +#ifndef fmod +double fmod(double x, double y) { + union { + double f; + uint64_t i; + } ux = {x}, uy = {y}; + int ex = ux.i >> 52 & 0x7ff; + int ey = uy.i >> 52 & 0x7ff; + int sx = ux.i >> 63; + uint64_t i; + + /* in the followings uxi should be ux.i, but then gcc wrongly adds */ + /* float load/store to inner loops ruining performance and code size */ + uint64_t uxi = ux.i; + + if(uy.i << 1 == 0 || isnan(y) || ex == 0x7ff) return (x * y) / (x * y); + if(uxi << 1 <= uy.i << 1) { + if(uxi << 1 == uy.i << 1) return 0 * x; + return x; + } + + /* normalize x and y */ + if(!ex) { + for(i = uxi << 12; i >> 63 == 0; ex--, i <<= 1) + ; + uxi <<= -ex + 1; + } else { + uxi &= -1ULL >> 12; + uxi |= 1ULL << 52; + } + if(!ey) { + for(i = uy.i << 12; i >> 63 == 0; ey--, i <<= 1) + ; + uy.i <<= -ey + 1; + } else { + uy.i &= -1ULL >> 12; + uy.i |= 1ULL << 52; + } + + /* x mod y */ + for(; ex > ey; ex--) { + i = uxi - uy.i; + if(i >> 63 == 0) { + if(i == 0) return 0 * x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if(i >> 63 == 0) { + if(i == 0) return 0 * x; + uxi = i; + } + for(; uxi >> 52 == 0; uxi <<= 1, ex--) + ; + + /* scale result */ + if(ex > 0) { + uxi -= 1ULL << 52; + uxi |= (uint64_t)ex << 52; + } else { + uxi >>= -ex + 1; + } + uxi |= (uint64_t)sx << 63; + ux.i = uxi; + return ux.f; +} +#endif + +#ifndef nearbyintf +float nearbyintf(float x) { + union { + float f; + uint32_t i; + } u = {x}; + int e = u.i >> 23 & 0xff; + int s = u.i >> 31; + float_t y; + + if(e >= 0x7f + 23) return x; + if(s) + y = x - 0x1p23f + 0x1p23f; + else + y = x + 0x1p23f - 0x1p23f; + if(y == 0) return s ? -0.0f : 0.0f; + return y; +} +#endif + +#ifndef stroll +long long strtoll(const char* restrict s, char** restrict p, int base) { + return strtox(s, p, base, LLONG_MIN); +} +#endif + +#ifndef pow +double pow(double x, double y) { + return powf(x, y); +} +#endif + +#ifndef rint + +double rint(double x) { + union { + double f; + uint64_t i; + } u = {x}; + int e = u.i >> 52 & 0x7ff; + int s = u.i >> 63; + double_t y; + + if(e >= 0x3ff + 52) return x; + if(s) + y = x - toint + toint; + else + y = x + toint - toint; + if(y == 0) return s ? -0.0 : 0; + return y; +} +#endif + +#ifndef nearbyint +double nearbyint(double x) { +#ifdef FE_INEXACT +#pragma STDC FENV_ACCESS ON + int e; + + e = fetestexcept(FE_INEXACT); +#endif + x = rint(x); +#ifdef FE_INEXACT + if(!e) feclearexcept(FE_INEXACT); +#endif + return x; +} +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_runtime.c b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_runtime.c new file mode 100644 index 00000000..4e3923f8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython-port/mp_flipper_runtime.c @@ -0,0 +1,154 @@ +#include +#include + +#include +#include + +#include "mp_flipper_context.h" + +static void on_input_callback(const InputEvent* event, void* ctx) { + uint16_t button = 1 << event->key; + uint16_t type = 1 << (InputKeyMAX + event->type); + + mp_flipper_on_input(button, type); +} + +void mp_flipper_save_file(const char* file_path, const char* data, size_t size) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + do { + if(!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_free(file); + + mp_flipper_raise_os_error_with_filename(MP_ENOENT, file_path); + + break; + } + + storage_file_write(file, data, size); + } while(false); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +inline void mp_flipper_nlr_jump_fail(void* val) { + furi_crash(); +} + +inline void mp_flipper_assert(const char* file, int line, const char* func, const char* expr) { +} + +inline void mp_flipper_fatal_error(const char* msg) { + furi_crash(msg); +} + +const char* mp_flipper_print_get_data(void* data) { + return furi_string_get_cstr(data); +} + +size_t mp_flipper_print_get_data_length(void* data) { + return furi_string_size(data); +} + +void* mp_flipper_print_data_alloc() { + return furi_string_alloc(); +} + +void mp_flipper_print_strn(void* data, const char* str, size_t length) { + for(size_t i = 0; i < length; i++) { + furi_string_push_back(data, str[i]); + } +} + +void mp_flipper_print_data_free(void* data) { + furi_string_free(data); +} + +void* mp_flipper_context_alloc() { + mp_flipper_context_t* ctx = malloc(sizeof(mp_flipper_context_t)); + + ctx->gui = furi_record_open(RECORD_GUI); + ctx->view_port = view_port_alloc(); + + ctx->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS); + ctx->input_event = furi_pubsub_subscribe(ctx->input_event_queue, on_input_callback, NULL); + + gui_add_view_port(ctx->gui, ctx->view_port, GuiLayerFullscreen); + + ctx->canvas = gui_direct_draw_acquire(ctx->gui); + + ctx->dialog_message = dialog_message_alloc(); + ctx->dialog_message_button_left = NULL; + ctx->dialog_message_button_center = NULL; + ctx->dialog_message_button_right = NULL; + + ctx->adc_handle = NULL; + + // GPIO + ctx->gpio_pins = malloc(MP_FLIPPER_GPIO_PINS * sizeof(mp_flipper_gpio_pin_t)); + + for(uint8_t pin = 0; pin < MP_FLIPPER_GPIO_PINS; pin++) { + ctx->gpio_pins[pin] = MP_FLIPPER_GPIO_PIN_OFF; + } + + // infrared rx + ctx->infrared_rx = malloc(sizeof(mp_flipper_infrared_rx_t)); + + ctx->infrared_rx->size = MP_FLIPPER_INFRARED_RX_BUFFER_SIZE; + ctx->infrared_rx->buffer = calloc(ctx->infrared_rx->size, sizeof(uint16_t)); + ctx->infrared_rx->pointer = 0; + ctx->infrared_rx->running = true; + + // infrared tx + ctx->infrared_tx = malloc(sizeof(mp_flipper_infrared_tx_t)); + ctx->infrared_tx->index = 0; + ctx->infrared_tx->provider = NULL; + ctx->infrared_tx->repeat = 0; + ctx->infrared_tx->signal = NULL; + ctx->infrared_tx->size = 0; + ctx->infrared_tx->level = false; + + return ctx; +} + +void mp_flipper_context_free(void* context) { + mp_flipper_context_t* ctx = context; + + gui_direct_draw_release(ctx->gui); + + furi_pubsub_unsubscribe(ctx->input_event_queue, ctx->input_event); + + gui_remove_view_port(ctx->gui, ctx->view_port); + + view_port_free(ctx->view_port); + + dialog_message_free(ctx->dialog_message); + + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_INPUT_EVENTS); + + // disable ADC handle + if(ctx->adc_handle) { + furi_hal_adc_release(ctx->adc_handle); + } + + // de-initialize all GPIO pins + for(uint8_t pin = 0; pin < MP_FLIPPER_GPIO_PINS; pin++) { + mp_flipper_gpio_deinit_pin(pin); + } + + // stop running PWM output + mp_flipper_pwm_stop(MP_FLIPPER_GPIO_PIN_PA4); + mp_flipper_pwm_stop(MP_FLIPPER_GPIO_PIN_PA7); + + free(ctx->gpio_pins); + + // stop infrared + free(ctx->infrared_rx->buffer); + free(ctx->infrared_rx); + free(ctx->infrared_tx); + + free(ctx); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/extmod/modjson.c b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modjson.c new file mode 100644 index 00000000..e655a02b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modjson.c @@ -0,0 +1,386 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2019 Damien P. George + * + * 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 "py/objlist.h" +#include "py/objstringio.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "py/stream.h" + +#if MICROPY_PY_JSON + +#if MICROPY_PY_JSON_SEPARATORS + +enum { + DUMP_MODE_TO_STRING = 1, + DUMP_MODE_TO_STREAM = 2, +}; + +static mp_obj_t mod_json_dump_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, unsigned int mode) { + enum { ARG_separators }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_separators, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - mode, pos_args + mode, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_print_ext_t print_ext; + + if (args[ARG_separators].u_obj == mp_const_none) { + print_ext.item_separator = ", "; + print_ext.key_separator = ": "; + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[ARG_separators].u_obj, 2, &items); + print_ext.item_separator = mp_obj_str_get_str(items[0]); + print_ext.key_separator = mp_obj_str_get_str(items[1]); + } + + if (mode == DUMP_MODE_TO_STRING) { + // dumps(obj) + vstr_t vstr; + vstr_init_print(&vstr, 8, &print_ext.base); + mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON); + return mp_obj_new_str_from_utf8_vstr(&vstr); + } else { + // dump(obj, stream) + print_ext.base.data = MP_OBJ_TO_PTR(pos_args[1]); + print_ext.base.print_strn = mp_stream_write_adaptor; + mp_get_stream_raise(pos_args[1], MP_STREAM_OP_WRITE); + mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON); + return mp_const_none; + } +} + +static mp_obj_t mod_json_dump(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return mod_json_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STREAM); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(mod_json_dump_obj, 2, mod_json_dump); + +static mp_obj_t mod_json_dumps(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return mod_json_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STRING); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(mod_json_dumps_obj, 1, mod_json_dumps); + +#else + +static mp_obj_t mod_json_dump(mp_obj_t obj, mp_obj_t stream) { + mp_get_stream_raise(stream, MP_STREAM_OP_WRITE); + mp_print_t print = {MP_OBJ_TO_PTR(stream), mp_stream_write_adaptor}; + mp_obj_print_helper(&print, obj, PRINT_JSON); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(mod_json_dump_obj, mod_json_dump); + +static mp_obj_t mod_json_dumps(mp_obj_t obj) { + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 8, &print); + mp_obj_print_helper(&print, obj, PRINT_JSON); + return mp_obj_new_str_from_utf8_vstr(&vstr); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mod_json_dumps_obj, mod_json_dumps); + +#endif + +// The function below implements a simple non-recursive JSON parser. +// +// The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt +// The parser here will parse any valid JSON and return the correct +// corresponding Python object. It allows through a superset of JSON, since +// it treats commas and colons as "whitespace", and doesn't care if +// brackets/braces are correctly paired. It will raise a ValueError if the +// input is outside it's specs. +// +// Most of the work is parsing the primitives (null, false, true, numbers, +// strings). It does 1 pass over the input stream. It tries to be fast and +// small in code size, while not using more RAM than necessary. + +typedef struct _json_stream_t { + mp_obj_t stream_obj; + mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode); + int errcode; + byte cur; +} json_stream_t; + +#define S_EOF (0) // null is not allowed in json stream so is ok as EOF marker +#define S_END(s) ((s).cur == S_EOF) +#define S_CUR(s) ((s).cur) +#define S_NEXT(s) (json_stream_next(&(s))) + +static byte json_stream_next(json_stream_t *s) { + mp_uint_t ret = s->read(s->stream_obj, &s->cur, 1, &s->errcode); + if (s->errcode != 0) { + mp_raise_OSError(s->errcode); + } + if (ret == 0) { + s->cur = S_EOF; + } + return s->cur; +} + +static mp_obj_t mod_json_load(mp_obj_t stream_obj) { + const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ); + json_stream_t s = {stream_obj, stream_p->read, 0, 0}; + vstr_t vstr; + vstr_init(&vstr, 8); + mp_obj_list_t stack; // we use a list as a simple stack for nested JSON + stack.len = 0; + stack.items = NULL; + mp_obj_t stack_top = MP_OBJ_NULL; + const mp_obj_type_t *stack_top_type = NULL; + mp_obj_t stack_key = MP_OBJ_NULL; + S_NEXT(s); + for (;;) { + cont: + if (S_END(s)) { + break; + } + mp_obj_t next = MP_OBJ_NULL; + bool enter = false; + byte cur = S_CUR(s); + S_NEXT(s); + switch (cur) { + case ',': + case ':': + case ' ': + case '\t': + case '\n': + case '\r': + goto cont; + case 'n': + if (S_CUR(s) == 'u' && S_NEXT(s) == 'l' && S_NEXT(s) == 'l') { + S_NEXT(s); + next = mp_const_none; + } else { + goto fail; + } + break; + case 'f': + if (S_CUR(s) == 'a' && S_NEXT(s) == 'l' && S_NEXT(s) == 's' && S_NEXT(s) == 'e') { + S_NEXT(s); + next = mp_const_false; + } else { + goto fail; + } + break; + case 't': + if (S_CUR(s) == 'r' && S_NEXT(s) == 'u' && S_NEXT(s) == 'e') { + S_NEXT(s); + next = mp_const_true; + } else { + goto fail; + } + break; + case '"': + vstr_reset(&vstr); + for (; !S_END(s) && S_CUR(s) != '"';) { + byte c = S_CUR(s); + if (c == '\\') { + c = S_NEXT(s); + switch (c) { + case 'b': + c = 0x08; + break; + case 'f': + c = 0x0c; + break; + case 'n': + c = 0x0a; + break; + case 'r': + c = 0x0d; + break; + case 't': + c = 0x09; + break; + case 'u': { + mp_uint_t num = 0; + for (int i = 0; i < 4; i++) { + c = (S_NEXT(s) | 0x20) - '0'; + if (c > 9) { + c -= ('a' - ('9' + 1)); + } + num = (num << 4) | c; + } + vstr_add_char(&vstr, num); + goto str_cont; + } + } + } + vstr_add_byte(&vstr, c); + str_cont: + S_NEXT(s); + } + if (S_END(s)) { + goto fail; + } + S_NEXT(s); + next = mp_obj_new_str(vstr.buf, vstr.len); + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + bool flt = false; + vstr_reset(&vstr); + for (;;) { + vstr_add_byte(&vstr, cur); + cur = S_CUR(s); + if (cur == '.' || cur == 'E' || cur == 'e') { + flt = true; + } else if (cur == '+' || cur == '-' || unichar_isdigit(cur)) { + // pass + } else { + break; + } + S_NEXT(s); + } + if (flt) { + next = mp_parse_num_float(vstr.buf, vstr.len, false, NULL); + } else { + next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); + } + break; + } + case '[': + next = mp_obj_new_list(0, NULL); + enter = true; + break; + case '{': + next = mp_obj_new_dict(0); + enter = true; + break; + case '}': + case ']': { + if (stack_top == MP_OBJ_NULL) { + // no object at all + goto fail; + } + if (stack.len == 0) { + // finished; compound object + goto success; + } + stack.len -= 1; + stack_top = stack.items[stack.len]; + stack_top_type = mp_obj_get_type(stack_top); + goto cont; + } + default: + goto fail; + } + if (stack_top == MP_OBJ_NULL) { + stack_top = next; + stack_top_type = mp_obj_get_type(stack_top); + if (!enter) { + // finished; single primitive only + goto success; + } + } else { + // append to list or dict + if (stack_top_type == &mp_type_list) { + mp_obj_list_append(stack_top, next); + } else { + if (stack_key == MP_OBJ_NULL) { + stack_key = next; + if (enter) { + goto fail; + } + } else { + mp_obj_dict_store(stack_top, stack_key, next); + stack_key = MP_OBJ_NULL; + } + } + if (enter) { + if (stack.items == NULL) { + mp_obj_list_init(&stack, 1); + stack.items[0] = stack_top; + } else { + mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top); + } + stack_top = next; + stack_top_type = mp_obj_get_type(stack_top); + } + } + } +success: + // eat trailing whitespace + while (unichar_isspace(S_CUR(s))) { + S_NEXT(s); + } + if (!S_END(s)) { + // unexpected chars + goto fail; + } + if (stack_top == MP_OBJ_NULL || stack.len != 0) { + // not exactly 1 object + goto fail; + } + vstr_clear(&vstr); + return stack_top; + +fail: + mp_raise_ValueError(MP_ERROR_TEXT("syntax error in JSON")); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mod_json_load_obj, mod_json_load); + +static mp_obj_t mod_json_loads(mp_obj_t obj) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(obj, &bufinfo, MP_BUFFER_READ); + vstr_t vstr = {bufinfo.len, bufinfo.len, (char *)bufinfo.buf, true}; + mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0, MP_OBJ_NULL}; + return mod_json_load(MP_OBJ_FROM_PTR(&sio)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mod_json_loads_obj, mod_json_loads); + +static const mp_rom_map_elem_t mp_module_json_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_json) }, + { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_json_dump_obj) }, + { MP_ROM_QSTR(MP_QSTR_dumps), MP_ROM_PTR(&mod_json_dumps_obj) }, + { MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&mod_json_load_obj) }, + { MP_ROM_QSTR(MP_QSTR_loads), MP_ROM_PTR(&mod_json_loads_obj) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_json_globals, mp_module_json_globals_table); + +const mp_obj_module_t mp_module_json = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_json_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_json, mp_module_json); + +#endif // MICROPY_PY_JSON diff --git a/non_catalog_apps/mp_flipper/lib/micropython/extmod/modplatform.h b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modplatform.h new file mode 100644 index 00000000..56a50e53 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modplatform.h @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * + * 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. + */ +#ifndef MICROPY_INCLUDED_MODPLATFORM_H +#define MICROPY_INCLUDED_MODPLATFORM_H + +#include "py/misc.h" // For MP_STRINGIFY. +#include "py/mpconfig.h" + +// Preprocessor directives identifying the platform. +// The platform module itself is guarded by MICROPY_PY_PLATFORM, see the +// .c file, but these are made available because they're generally usable. +// TODO: Add more architectures, compilers and libraries. +// See: https://sourceforge.net/p/predef/wiki/Home/ + +#if defined(__ARM_ARCH) +#define MICROPY_PLATFORM_ARCH "arm" +#elif defined(__x86_64__) || defined(_M_X64) +#define MICROPY_PLATFORM_ARCH "x86_64" +#elif defined(__i386__) || defined(_M_IX86) +#define MICROPY_PLATFORM_ARCH "x86" +#elif defined(__xtensa__) +#define MICROPY_PLATFORM_ARCH "xtensa" +#elif defined(__riscv) +#define MICROPY_PLATFORM_ARCH "riscv" +#else +#define MICROPY_PLATFORM_ARCH "" +#endif + +#if defined(__GNUC__) +#define MICROPY_PLATFORM_COMPILER \ + "GCC " \ + MP_STRINGIFY(__GNUC__) "." \ + MP_STRINGIFY(__GNUC_MINOR__) "." \ + MP_STRINGIFY(__GNUC_PATCHLEVEL__) +#elif defined(__ARMCC_VERSION) +#define MICROPY_PLATFORM_COMPILER \ + "ARMCC " \ + MP_STRINGIFY((__ARMCC_VERSION / 1000000)) "." \ + MP_STRINGIFY((__ARMCC_VERSION / 10000 % 100)) "." \ + MP_STRINGIFY((__ARMCC_VERSION % 10000)) +#elif defined(_MSC_VER) +#if defined(_WIN64) +#define MICROPY_PLATFORM_COMPILER_BITS "64 bit" +#elif defined(_M_IX86) +#define MICROPY_PLATFORM_COMPILER_BITS "32 bit" +#else +#define MICROPY_PLATFORM_COMPILER_BITS "" +#endif +#define MICROPY_PLATFORM_COMPILER \ + "MSC v." MP_STRINGIFY(_MSC_VER) " " MICROPY_PLATFORM_COMPILER_BITS +#else +#define MICROPY_PLATFORM_COMPILER "" +#endif + +#if defined(__GLIBC__) +#define MICROPY_PLATFORM_LIBC_LIB "glibc" +#define MICROPY_PLATFORM_LIBC_VER \ + MP_STRINGIFY(__GLIBC__) "." \ + MP_STRINGIFY(__GLIBC_MINOR__) +#elif defined(__NEWLIB__) +#define MICROPY_PLATFORM_LIBC_LIB "newlib" +#define MICROPY_PLATFORM_LIBC_VER _NEWLIB_VERSION +#else +#define MICROPY_PLATFORM_LIBC_LIB "" +#define MICROPY_PLATFORM_LIBC_VER "" +#endif + +#if defined(__linux) +#define MICROPY_PLATFORM_SYSTEM "Linux" +#elif defined(__unix__) +#define MICROPY_PLATFORM_SYSTEM "Unix" +#elif defined(__CYGWIN__) +#define MICROPY_PLATFORM_SYSTEM "Cygwin" +#elif defined(_WIN32) +#define MICROPY_PLATFORM_SYSTEM "Windows" +#else +#define MICROPY_PLATFORM_SYSTEM "MicroPython" +#endif + +#ifndef MICROPY_PLATFORM_VERSION +#define MICROPY_PLATFORM_VERSION "" +#endif + +#endif // MICROPY_INCLUDED_MODPLATFORM_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/extmod/modrandom.c b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modrandom.c new file mode 100644 index 00000000..79a1b18b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modrandom.c @@ -0,0 +1,261 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * + * 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 "py/runtime.h" + +#if MICROPY_PY_RANDOM + +// Work out if the seed will be set on import or not. +#if MICROPY_MODULE_BUILTIN_INIT && defined(MICROPY_PY_RANDOM_SEED_INIT_FUNC) +#define SEED_ON_IMPORT (1) +#else +#define SEED_ON_IMPORT (0) +#endif + +// Yasmarang random number generator +// by Ilya Levin +// http://www.literatecode.com/yasmarang +// Public Domain + +#if !MICROPY_ENABLE_DYNRUNTIME +#if SEED_ON_IMPORT +// If the state is seeded on import then keep these variables in the BSS. +static uint32_t yasmarang_pad, yasmarang_n, yasmarang_d; +static uint8_t yasmarang_dat; +#else +// Without seed-on-import these variables must be initialised via the data section. +static uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233; +static uint8_t yasmarang_dat = 0; +#endif +#endif + +static uint32_t yasmarang(void) { + yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n; + yasmarang_pad = (yasmarang_pad << 3) + (yasmarang_pad >> 29); + yasmarang_n = yasmarang_pad | 2; + yasmarang_d ^= (yasmarang_pad << 31) + (yasmarang_pad >> 1); + yasmarang_dat ^= (char)yasmarang_pad ^ (yasmarang_d >> 8) ^ 1; + + return yasmarang_pad ^ (yasmarang_d << 5) ^ (yasmarang_pad >> 18) ^ (yasmarang_dat << 1); +} /* yasmarang */ + +// End of Yasmarang + +#if MICROPY_PY_RANDOM_EXTRA_FUNCS + +// returns an unsigned integer below the given argument +// n must not be zero +static uint32_t yasmarang_randbelow(uint32_t n) { + uint32_t mask = 1; + while ((n & mask) < n) { + mask = (mask << 1) | 1; + } + uint32_t r; + do { + r = yasmarang() & mask; + } while (r >= n); + return r; +} + +#endif + +static mp_obj_t mod_random_getrandbits(mp_obj_t num_in) { + mp_int_t n = mp_obj_get_int(num_in); + if (n > 32 || n < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("bits must be 32 or less")); + } + if (n == 0) { + return MP_OBJ_NEW_SMALL_INT(0); + } + uint32_t mask = ~0; + // Beware of C undefined behavior when shifting by >= than bit size + mask >>= (32 - n); + return mp_obj_new_int_from_uint(yasmarang() & mask); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mod_random_getrandbits_obj, mod_random_getrandbits); + +static mp_obj_t mod_random_seed(size_t n_args, const mp_obj_t *args) { + mp_uint_t seed; + if (n_args == 0 || args[0] == mp_const_none) { + #ifdef MICROPY_PY_RANDOM_SEED_INIT_FUNC + seed = MICROPY_PY_RANDOM_SEED_INIT_FUNC; + #else + mp_raise_ValueError(MP_ERROR_TEXT("no default seed")); + #endif + } else { + seed = mp_obj_get_int_truncated(args[0]); + } + yasmarang_pad = (uint32_t)seed; + yasmarang_n = 69; + yasmarang_d = 233; + yasmarang_dat = 0; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_random_seed_obj, 0, 1, mod_random_seed); + +#if MICROPY_PY_RANDOM_EXTRA_FUNCS + +static mp_obj_t mod_random_randrange(size_t n_args, const mp_obj_t *args) { + mp_int_t start = mp_obj_get_int(args[0]); + if (n_args == 1) { + // range(stop) + if (start > 0) { + return mp_obj_new_int(yasmarang_randbelow((uint32_t)start)); + } else { + goto error; + } + } else { + mp_int_t stop = mp_obj_get_int(args[1]); + if (n_args == 2) { + // range(start, stop) + if (start < stop) { + return mp_obj_new_int(start + yasmarang_randbelow((uint32_t)(stop - start))); + } else { + goto error; + } + } else { + // range(start, stop, step) + mp_int_t step = mp_obj_get_int(args[2]); + mp_int_t n; + if (step > 0) { + n = (stop - start + step - 1) / step; + } else if (step < 0) { + n = (stop - start + step + 1) / step; + } else { + goto error; + } + if (n > 0) { + return mp_obj_new_int(start + step * yasmarang_randbelow((uint32_t)n)); + } else { + goto error; + } + } + } + +error: + mp_raise_ValueError(NULL); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_random_randrange_obj, 1, 3, mod_random_randrange); + +static mp_obj_t mod_random_randint(mp_obj_t a_in, mp_obj_t b_in) { + mp_int_t a = mp_obj_get_int(a_in); + mp_int_t b = mp_obj_get_int(b_in); + if (a <= b) { + return mp_obj_new_int(a + yasmarang_randbelow((uint32_t)(b - a + 1))); + } else { + mp_raise_ValueError(NULL); + } +} +static MP_DEFINE_CONST_FUN_OBJ_2(mod_random_randint_obj, mod_random_randint); + +static mp_obj_t mod_random_choice(mp_obj_t seq) { + mp_int_t len = mp_obj_get_int(mp_obj_len(seq)); + if (len > 0) { + return mp_obj_subscr(seq, mp_obj_new_int(yasmarang_randbelow((uint32_t)len)), MP_OBJ_SENTINEL); + } else { + mp_raise_type(&mp_type_IndexError); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(mod_random_choice_obj, mod_random_choice); + +#if MICROPY_PY_BUILTINS_FLOAT + +// returns a number in the range [0..1) using Yasmarang to fill in the fraction bits +static mp_float_t yasmarang_float(void) { + mp_float_union_t u; + u.p.sgn = 0; + u.p.exp = (1 << (MP_FLOAT_EXP_BITS - 1)) - 1; + if (MP_FLOAT_FRAC_BITS <= 32) { + u.p.frc = yasmarang(); + } else { + u.p.frc = ((uint64_t)yasmarang() << 32) | (uint64_t)yasmarang(); + } + return u.f - 1; +} + +static mp_obj_t mod_random_random(void) { + return mp_obj_new_float(yasmarang_float()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mod_random_random_obj, mod_random_random); + +static mp_obj_t mod_random_uniform(mp_obj_t a_in, mp_obj_t b_in) { + mp_float_t a = mp_obj_get_float(a_in); + mp_float_t b = mp_obj_get_float(b_in); + return mp_obj_new_float(a + (b - a) * yasmarang_float()); +} +static MP_DEFINE_CONST_FUN_OBJ_2(mod_random_uniform_obj, mod_random_uniform); + +#endif + +#endif // MICROPY_PY_RANDOM_EXTRA_FUNCS + +#if SEED_ON_IMPORT +static mp_obj_t mod_random___init__(void) { + // This module may be imported by more than one name so need to ensure + // that it's only ever seeded once. + static bool seeded = false; + if (!seeded) { + seeded = true; + mod_random_seed(0, NULL); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(mod_random___init___obj, mod_random___init__); +#endif + +#if !MICROPY_ENABLE_DYNRUNTIME +static const mp_rom_map_elem_t mp_module_random_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) }, + #if SEED_ON_IMPORT + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&mod_random___init___obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&mod_random_getrandbits_obj) }, + { MP_ROM_QSTR(MP_QSTR_seed), MP_ROM_PTR(&mod_random_seed_obj) }, + #if MICROPY_PY_RANDOM_EXTRA_FUNCS + { MP_ROM_QSTR(MP_QSTR_randrange), MP_ROM_PTR(&mod_random_randrange_obj) }, + { MP_ROM_QSTR(MP_QSTR_randint), MP_ROM_PTR(&mod_random_randint_obj) }, + { MP_ROM_QSTR(MP_QSTR_choice), MP_ROM_PTR(&mod_random_choice_obj) }, + #if MICROPY_PY_BUILTINS_FLOAT + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mod_random_random_obj) }, + { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&mod_random_uniform_obj) }, + #endif + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_random_globals, mp_module_random_globals_table); + +const mp_obj_module_t mp_module_random = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_random_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_random, mp_module_random); +#endif + +#endif // MICROPY_PY_RANDOM diff --git a/non_catalog_apps/mp_flipper/lib/micropython/extmod/modtime.c b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modtime.c new file mode 100644 index 00000000..deb4bb4c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modtime.c @@ -0,0 +1,236 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2023 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * 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 "py/mphal.h" +#include "py/runtime.h" +#include "py/smallint.h" +#include "extmod/modtime.h" + +#if MICROPY_PY_TIME + +#ifdef MICROPY_PY_TIME_INCLUDEFILE +#include MICROPY_PY_TIME_INCLUDEFILE +#endif + +#if MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME + +#include "shared/timeutils/timeutils.h" + +// localtime([secs]) +// Convert a time expressed in seconds since the Epoch into an 8-tuple which +// contains: (year, month, mday, hour, minute, second, weekday, yearday) +// If secs is not provided or None, then the current time is used. +// - year is the full year, eg 2000 +// - month is 1-12 +// - mday is 1-31 +// - hour is 0-23 +// - minute is 0-59 +// - second is 0-59 +// - weekday is 0-6 for Mon-Sun +// - yearday is 1-366 +static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + if (n_args == 0 || args[0] == mp_const_none) { + // Get current date and time. + return mp_time_localtime_get(); + } else { + // Convert given seconds to tuple. + mp_int_t seconds = mp_obj_get_int(args[0]); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_time_localtime_obj, 0, 1, time_localtime); + +// mktime() +// This is the inverse function of localtime. Its argument is a full 8-tuple +// which expresses a time as per localtime. It returns an integer which is +// the number of seconds since the Epoch (eg 1st Jan 1970, or 1st Jan 2000). +static mp_obj_t time_mktime(mp_obj_t tuple) { + size_t len; + mp_obj_t *elem; + mp_obj_get_array(tuple, &len, &elem); + + // localtime generates a tuple of len 8. CPython uses 9, so we accept both. + if (len < 8 || len > 9) { + mp_raise_TypeError(MP_ERROR_TEXT("mktime needs a tuple of length 8 or 9")); + } + + return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]), + mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_obj_get_int(elem[3]), + mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5]))); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_time_mktime_obj, time_mktime); + +#endif // MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME + +#if MICROPY_PY_TIME_TIME_TIME_NS + +// time() +// Return the number of seconds since the Epoch. +static mp_obj_t time_time(void) { + return mp_time_time_get(); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_time_time_obj, time_time); + +// time_ns() +// Returns the number of nanoseconds since the Epoch, as an integer. +static mp_obj_t time_time_ns(void) { + return mp_obj_new_int_from_ull(mp_hal_time_ns()); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_time_time_ns_obj, time_time_ns); + +#endif // MICROPY_PY_TIME_TIME_TIME_NS + +static mp_obj_t time_sleep(mp_obj_t seconds_o) { + #ifdef MICROPY_PY_TIME_CUSTOM_SLEEP + mp_time_sleep(seconds_o); + #else + #if MICROPY_PY_BUILTINS_FLOAT + mp_hal_delay_ms((mp_uint_t)(1000 * mp_obj_get_float(seconds_o))); + #else + mp_hal_delay_ms(1000 * mp_obj_get_int(seconds_o)); + #endif + #endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_time_sleep_obj, time_sleep); + +static mp_obj_t time_sleep_ms(mp_obj_t arg) { + mp_int_t ms = mp_obj_get_int(arg); + if (ms >= 0) { + mp_hal_delay_ms(ms); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_time_sleep_ms_obj, time_sleep_ms); + +static mp_obj_t time_sleep_us(mp_obj_t arg) { + mp_int_t us = mp_obj_get_int(arg); + if (us > 0) { + mp_hal_delay_us(us); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_time_sleep_us_obj, time_sleep_us); + +static mp_obj_t time_ticks_ms(void) { + return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_time_ticks_ms_obj, time_ticks_ms); + +static mp_obj_t time_ticks_us(void) { + return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_us() & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_time_ticks_us_obj, time_ticks_us); + +static mp_obj_t time_ticks_cpu(void) { + return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_cpu() & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_time_ticks_cpu_obj, time_ticks_cpu); + +static mp_obj_t time_ticks_diff(mp_obj_t end_in, mp_obj_t start_in) { + // we assume that the arguments come from ticks_xx so are small ints + mp_uint_t start = MP_OBJ_SMALL_INT_VALUE(start_in); + mp_uint_t end = MP_OBJ_SMALL_INT_VALUE(end_in); + // Optimized formula avoiding if conditions. We adjust difference "forward", + // wrap it around and adjust back. + mp_int_t diff = ((end - start + MICROPY_PY_TIME_TICKS_PERIOD / 2) & (MICROPY_PY_TIME_TICKS_PERIOD - 1)) + - MICROPY_PY_TIME_TICKS_PERIOD / 2; + return MP_OBJ_NEW_SMALL_INT(diff); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_time_ticks_diff_obj, time_ticks_diff); + +static mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) { + // we assume that first argument come from ticks_xx so is small int + mp_uint_t ticks = MP_OBJ_SMALL_INT_VALUE(ticks_in); + mp_uint_t delta = mp_obj_get_int(delta_in); + + // Check that delta does not overflow the range that ticks_diff can handle. + // This ensures the following: + // - ticks_diff(ticks_add(T, delta), T) == delta + // - ticks_diff(T, ticks_add(T, delta)) == -delta + // The latter requires excluding delta=-TICKS_PERIOD/2. + // + // This unsigned comparison is equivalent to a signed comparison of: + // delta <= -TICKS_PERIOD/2 || delta >= TICKS_PERIOD/2 + if (delta + MICROPY_PY_TIME_TICKS_PERIOD / 2 - 1 >= MICROPY_PY_TIME_TICKS_PERIOD - 1) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ticks interval overflow")); + } + + return MP_OBJ_NEW_SMALL_INT((ticks + delta) & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_time_ticks_add_obj, time_ticks_add); + +static const mp_rom_map_elem_t mp_module_time_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_time) }, + + #if MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME + { MP_ROM_QSTR(MP_QSTR_gmtime), MP_ROM_PTR(&mp_time_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&mp_time_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&mp_time_mktime_obj) }, + #endif + + #if MICROPY_PY_TIME_TIME_TIME_NS + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_time_time_obj) }, + { MP_ROM_QSTR(MP_QSTR_time_ns), MP_ROM_PTR(&mp_time_time_ns_obj) }, + #endif + + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_time_sleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_time_sleep_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_time_sleep_us_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_time_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_time_ticks_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_time_ticks_cpu_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_time_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_time_ticks_diff_obj) }, + + #ifdef MICROPY_PY_TIME_EXTRA_GLOBALS + MICROPY_PY_TIME_EXTRA_GLOBALS + #endif +}; +static MP_DEFINE_CONST_DICT(mp_module_time_globals, mp_module_time_globals_table); + +const mp_obj_module_t mp_module_time = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_time_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_time, mp_module_time); + +#endif // MICROPY_PY_TIME diff --git a/non_catalog_apps/mp_flipper/lib/micropython/extmod/modtime.h b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modtime.h new file mode 100644 index 00000000..f5ea6b55 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/extmod/modtime.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * 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. + */ +#ifndef MICROPY_INCLUDED_EXTMOD_MODUTIME_H +#define MICROPY_INCLUDED_EXTMOD_MODUTIME_H + +#include "py/obj.h" + +MP_DECLARE_CONST_FUN_OBJ_1(mp_time_mktime_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_time_sleep_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_time_sleep_ms_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_time_sleep_us_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_time_ticks_ms_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_time_ticks_us_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_time_ticks_cpu_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_time_ticks_diff_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_time_ticks_add_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_time_time_ns_obj); + +#endif // MICROPY_INCLUDED_EXTMOD_MODUTIME_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/genhdr/moduledefs.h b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/moduledefs.h new file mode 100644 index 00000000..13b37384 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/moduledefs.h @@ -0,0 +1,33 @@ +// Automatically generated by makemoduledefs.py. + +extern const struct _mp_obj_module_t mp_module_random; +#undef MODULE_DEF_RANDOM +#define MODULE_DEF_RANDOM { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mp_module_random) }, + +extern const struct _mp_obj_module_t mp_module_time; +#undef MODULE_DEF_TIME +#define MODULE_DEF_TIME { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_time) }, + +extern const struct _mp_obj_module_t mp_module___main__; +#undef MODULE_DEF___MAIN__ +#define MODULE_DEF___MAIN__ { MP_ROM_QSTR(MP_QSTR___main__), MP_ROM_PTR(&mp_module___main__) }, + +extern const struct _mp_obj_module_t mp_module_builtins; +#undef MODULE_DEF_BUILTINS +#define MODULE_DEF_BUILTINS { MP_ROM_QSTR(MP_QSTR_builtins), MP_ROM_PTR(&mp_module_builtins) }, + +extern const struct _mp_obj_module_t flipperzero_module; +#undef MODULE_DEF_FLIPPERZERO +#define MODULE_DEF_FLIPPERZERO { MP_ROM_QSTR(MP_QSTR_flipperzero), MP_ROM_PTR(&flipperzero_module) }, + + +#define MICROPY_REGISTERED_MODULES \ + MODULE_DEF_BUILTINS \ + MODULE_DEF_FLIPPERZERO \ + MODULE_DEF___MAIN__ \ +// MICROPY_REGISTERED_MODULES + +#define MICROPY_REGISTERED_EXTENSIBLE_MODULES \ + MODULE_DEF_RANDOM \ + MODULE_DEF_TIME \ +// MICROPY_REGISTERED_EXTENSIBLE_MODULES diff --git a/non_catalog_apps/mp_flipper/lib/micropython/genhdr/mpversion.h b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/mpversion.h new file mode 100644 index 00000000..3e137d77 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/mpversion.h @@ -0,0 +1,4 @@ +// This file was generated by py/makeversionhdr.py +#define MICROPY_GIT_TAG "v1.23.0-preview.322.g5114f2c1e" +#define MICROPY_GIT_HASH "5114f2c1e" +#define MICROPY_BUILD_DATE "2024-09-08" diff --git a/non_catalog_apps/mp_flipper/lib/micropython/genhdr/qstrdefs.generated.h b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/qstrdefs.generated.h new file mode 100644 index 00000000..6fce4058 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/qstrdefs.generated.h @@ -0,0 +1,447 @@ +// This file was automatically generated by makeqstrdata.py + +QDEF0(MP_QSTRnull, 0, 0, "") +QDEF0(MP_QSTR_, 5, 0, "") +QDEF0(MP_QSTR___dir__, 122, 7, "__dir__") +QDEF0(MP_QSTR__0x0a_, 175, 1, "\x0a") +QDEF0(MP_QSTR__space_, 133, 1, " ") +QDEF0(MP_QSTR__star_, 143, 1, "*") +QDEF0(MP_QSTR__slash_, 138, 1, "/") +QDEF0(MP_QSTR__lt_module_gt_, 189, 8, "") +QDEF0(MP_QSTR__, 250, 1, "_") +QDEF0(MP_QSTR___call__, 167, 8, "__call__") +QDEF0(MP_QSTR___class__, 43, 9, "__class__") +QDEF0(MP_QSTR___delitem__, 253, 11, "__delitem__") +QDEF0(MP_QSTR___enter__, 109, 9, "__enter__") +QDEF0(MP_QSTR___exit__, 69, 8, "__exit__") +QDEF0(MP_QSTR___getattr__, 64, 11, "__getattr__") +QDEF0(MP_QSTR___getitem__, 38, 11, "__getitem__") +QDEF0(MP_QSTR___hash__, 247, 8, "__hash__") +QDEF0(MP_QSTR___init__, 95, 8, "__init__") +QDEF0(MP_QSTR___int__, 22, 7, "__int__") +QDEF0(MP_QSTR___iter__, 207, 8, "__iter__") +QDEF0(MP_QSTR___len__, 226, 7, "__len__") +QDEF0(MP_QSTR___main__, 142, 8, "__main__") +QDEF0(MP_QSTR___module__, 255, 10, "__module__") +QDEF0(MP_QSTR___name__, 226, 8, "__name__") +QDEF0(MP_QSTR___new__, 121, 7, "__new__") +QDEF0(MP_QSTR___next__, 2, 8, "__next__") +QDEF0(MP_QSTR___qualname__, 107, 12, "__qualname__") +QDEF0(MP_QSTR___repr__, 16, 8, "__repr__") +QDEF0(MP_QSTR___setitem__, 50, 11, "__setitem__") +QDEF0(MP_QSTR___str__, 208, 7, "__str__") +QDEF0(MP_QSTR_ArithmeticError, 45, 15, "ArithmeticError") +QDEF0(MP_QSTR_AssertionError, 151, 14, "AssertionError") +QDEF0(MP_QSTR_AttributeError, 33, 14, "AttributeError") +QDEF0(MP_QSTR_BaseException, 7, 13, "BaseException") +QDEF0(MP_QSTR_EOFError, 145, 8, "EOFError") +QDEF0(MP_QSTR_Ellipsis, 240, 8, "Ellipsis") +QDEF0(MP_QSTR_Exception, 242, 9, "Exception") +QDEF0(MP_QSTR_GeneratorExit, 22, 13, "GeneratorExit") +QDEF0(MP_QSTR_ImportError, 32, 11, "ImportError") +QDEF0(MP_QSTR_IndentationError, 92, 16, "IndentationError") +QDEF0(MP_QSTR_IndexError, 131, 10, "IndexError") +QDEF0(MP_QSTR_KeyError, 234, 8, "KeyError") +QDEF0(MP_QSTR_KeyboardInterrupt, 175, 17, "KeyboardInterrupt") +QDEF0(MP_QSTR_LookupError, 255, 11, "LookupError") +QDEF0(MP_QSTR_MemoryError, 220, 11, "MemoryError") +QDEF0(MP_QSTR_NameError, 186, 9, "NameError") +QDEF0(MP_QSTR_NoneType, 23, 8, "NoneType") +QDEF0(MP_QSTR_NotImplementedError, 198, 19, "NotImplementedError") +QDEF0(MP_QSTR_OSError, 161, 7, "OSError") +QDEF0(MP_QSTR_OverflowError, 129, 13, "OverflowError") +QDEF0(MP_QSTR_RuntimeError, 97, 12, "RuntimeError") +QDEF0(MP_QSTR_StopIteration, 234, 13, "StopIteration") +QDEF0(MP_QSTR_SyntaxError, 148, 11, "SyntaxError") +QDEF0(MP_QSTR_SystemExit, 32, 10, "SystemExit") +QDEF0(MP_QSTR_TypeError, 37, 9, "TypeError") +QDEF0(MP_QSTR_ValueError, 150, 10, "ValueError") +QDEF0(MP_QSTR_ZeroDivisionError, 182, 17, "ZeroDivisionError") +QDEF0(MP_QSTR_abs, 149, 3, "abs") +QDEF0(MP_QSTR_all, 68, 3, "all") +QDEF0(MP_QSTR_any, 19, 3, "any") +QDEF0(MP_QSTR_append, 107, 6, "append") +QDEF0(MP_QSTR_args, 194, 4, "args") +QDEF0(MP_QSTR_bool, 235, 4, "bool") +QDEF0(MP_QSTR_builtins, 247, 8, "builtins") +QDEF0(MP_QSTR_bytearray, 118, 9, "bytearray") +QDEF0(MP_QSTR_bytecode, 34, 8, "bytecode") +QDEF0(MP_QSTR_bytes, 92, 5, "bytes") +QDEF0(MP_QSTR_callable, 13, 8, "callable") +QDEF0(MP_QSTR_chr, 220, 3, "chr") +QDEF0(MP_QSTR_classmethod, 180, 11, "classmethod") +QDEF0(MP_QSTR_clear, 124, 5, "clear") +QDEF0(MP_QSTR_close, 51, 5, "close") +QDEF0(MP_QSTR_const, 192, 5, "const") +QDEF0(MP_QSTR_copy, 224, 4, "copy") +QDEF0(MP_QSTR_count, 166, 5, "count") +QDEF0(MP_QSTR_dict, 63, 4, "dict") +QDEF0(MP_QSTR_dir, 250, 3, "dir") +QDEF0(MP_QSTR_divmod, 184, 6, "divmod") +QDEF0(MP_QSTR_end, 10, 3, "end") +QDEF0(MP_QSTR_endswith, 27, 8, "endswith") +QDEF0(MP_QSTR_eval, 155, 4, "eval") +QDEF0(MP_QSTR_exec, 30, 4, "exec") +QDEF0(MP_QSTR_extend, 99, 6, "extend") +QDEF0(MP_QSTR_find, 1, 4, "find") +QDEF0(MP_QSTR_format, 38, 6, "format") +QDEF0(MP_QSTR_from_bytes, 53, 10, "from_bytes") +QDEF0(MP_QSTR_get, 51, 3, "get") +QDEF0(MP_QSTR_getattr, 192, 7, "getattr") +QDEF0(MP_QSTR_globals, 157, 7, "globals") +QDEF0(MP_QSTR_hasattr, 140, 7, "hasattr") +QDEF0(MP_QSTR_hash, 183, 4, "hash") +QDEF0(MP_QSTR_id, 40, 2, "id") +QDEF0(MP_QSTR_index, 123, 5, "index") +QDEF0(MP_QSTR_insert, 18, 6, "insert") +QDEF0(MP_QSTR_int, 22, 3, "int") +QDEF0(MP_QSTR_isalpha, 235, 7, "isalpha") +QDEF0(MP_QSTR_isdigit, 168, 7, "isdigit") +QDEF0(MP_QSTR_isinstance, 182, 10, "isinstance") +QDEF0(MP_QSTR_islower, 252, 7, "islower") +QDEF0(MP_QSTR_isspace, 91, 7, "isspace") +QDEF0(MP_QSTR_issubclass, 181, 10, "issubclass") +QDEF0(MP_QSTR_isupper, 221, 7, "isupper") +QDEF0(MP_QSTR_items, 227, 5, "items") +QDEF0(MP_QSTR_iter, 143, 4, "iter") +QDEF0(MP_QSTR_join, 167, 4, "join") +QDEF0(MP_QSTR_key, 50, 3, "key") +QDEF0(MP_QSTR_keys, 1, 4, "keys") +QDEF0(MP_QSTR_len, 98, 3, "len") +QDEF0(MP_QSTR_list, 39, 4, "list") +QDEF0(MP_QSTR_little, 137, 6, "little") +QDEF0(MP_QSTR_locals, 59, 6, "locals") +QDEF0(MP_QSTR_lower, 198, 5, "lower") +QDEF0(MP_QSTR_lstrip, 229, 6, "lstrip") +QDEF0(MP_QSTR_main, 206, 4, "main") +QDEF0(MP_QSTR_map, 185, 3, "map") +QDEF0(MP_QSTR_micropython, 11, 11, "micropython") +QDEF0(MP_QSTR_next, 66, 4, "next") +QDEF0(MP_QSTR_object, 144, 6, "object") +QDEF0(MP_QSTR_open, 209, 4, "open") +QDEF0(MP_QSTR_ord, 28, 3, "ord") +QDEF0(MP_QSTR_pop, 42, 3, "pop") +QDEF0(MP_QSTR_popitem, 191, 7, "popitem") +QDEF0(MP_QSTR_pow, 45, 3, "pow") +QDEF0(MP_QSTR_print, 84, 5, "print") +QDEF0(MP_QSTR_range, 26, 5, "range") +QDEF0(MP_QSTR_read, 183, 4, "read") +QDEF0(MP_QSTR_readinto, 75, 8, "readinto") +QDEF0(MP_QSTR_readline, 249, 8, "readline") +QDEF0(MP_QSTR_remove, 99, 6, "remove") +QDEF0(MP_QSTR_replace, 73, 7, "replace") +QDEF0(MP_QSTR_repr, 208, 4, "repr") +QDEF0(MP_QSTR_reverse, 37, 7, "reverse") +QDEF0(MP_QSTR_rfind, 210, 5, "rfind") +QDEF0(MP_QSTR_rindex, 233, 6, "rindex") +QDEF0(MP_QSTR_round, 231, 5, "round") +QDEF0(MP_QSTR_rsplit, 165, 6, "rsplit") +QDEF0(MP_QSTR_rstrip, 59, 6, "rstrip") +QDEF0(MP_QSTR_self, 121, 4, "self") +QDEF0(MP_QSTR_send, 185, 4, "send") +QDEF0(MP_QSTR_sep, 35, 3, "sep") +QDEF0(MP_QSTR_set, 39, 3, "set") +QDEF0(MP_QSTR_setattr, 212, 7, "setattr") +QDEF0(MP_QSTR_setdefault, 108, 10, "setdefault") +QDEF0(MP_QSTR_sort, 191, 4, "sort") +QDEF0(MP_QSTR_sorted, 94, 6, "sorted") +QDEF0(MP_QSTR_split, 183, 5, "split") +QDEF0(MP_QSTR_start, 133, 5, "start") +QDEF0(MP_QSTR_startswith, 116, 10, "startswith") +QDEF0(MP_QSTR_staticmethod, 98, 12, "staticmethod") +QDEF0(MP_QSTR_step, 87, 4, "step") +QDEF0(MP_QSTR_stop, 157, 4, "stop") +QDEF0(MP_QSTR_str, 80, 3, "str") +QDEF0(MP_QSTR_strip, 41, 5, "strip") +QDEF0(MP_QSTR_sum, 46, 3, "sum") +QDEF0(MP_QSTR_super, 196, 5, "super") +QDEF0(MP_QSTR_throw, 179, 5, "throw") +QDEF0(MP_QSTR_to_bytes, 216, 8, "to_bytes") +QDEF0(MP_QSTR_tuple, 253, 5, "tuple") +QDEF0(MP_QSTR_type, 157, 4, "type") +QDEF0(MP_QSTR_update, 180, 6, "update") +QDEF0(MP_QSTR_upper, 39, 5, "upper") +QDEF0(MP_QSTR_utf_hyphen_8, 183, 5, "utf-8") +QDEF0(MP_QSTR_value, 78, 5, "value") +QDEF0(MP_QSTR_values, 125, 6, "values") +QDEF0(MP_QSTR_write, 152, 5, "write") +QDEF0(MP_QSTR_zip, 230, 3, "zip") +QDEF0(MP_QSTR__lt_dictcomp_gt_, 204, 10, "") +QDEF0(MP_QSTR__lt_genexpr_gt_, 52, 9, "") +QDEF0(MP_QSTR__lt_lambda_gt_, 128, 8, "") +QDEF0(MP_QSTR__lt_listcomp_gt_, 212, 10, "") +QDEF0(MP_QSTR__lt_setcomp_gt_, 84, 9, "") +QDEF1(MP_QSTR__lt_stdin_gt_, 227, 7, "") +QDEF1(MP_QSTR__lt_string_gt_, 82, 8, "") +QDEF1(MP_QSTR_ALIGN_BEGIN, 240, 11, "ALIGN_BEGIN") +QDEF1(MP_QSTR_ALIGN_CENTER, 28, 12, "ALIGN_CENTER") +QDEF1(MP_QSTR_ALIGN_END, 248, 9, "ALIGN_END") +QDEF1(MP_QSTR_CANVAS_BLACK, 213, 12, "CANVAS_BLACK") +QDEF1(MP_QSTR_CANVAS_WHITE, 53, 12, "CANVAS_WHITE") +QDEF1(MP_QSTR_FONT_PRIMARY, 133, 12, "FONT_PRIMARY") +QDEF1(MP_QSTR_FONT_SECONDARY, 51, 14, "FONT_SECONDARY") +QDEF1(MP_QSTR_GPIO_MODE_ANALOG, 29, 16, "GPIO_MODE_ANALOG") +QDEF1(MP_QSTR_GPIO_MODE_INPUT, 97, 15, "GPIO_MODE_INPUT") +QDEF1(MP_QSTR_GPIO_MODE_INTERRUPT_FALL, 40, 24, "GPIO_MODE_INTERRUPT_FALL") +QDEF1(MP_QSTR_GPIO_MODE_INTERRUPT_RISE, 130, 24, "GPIO_MODE_INTERRUPT_RISE") +QDEF1(MP_QSTR_GPIO_MODE_OUTPUT_OPEN_DRAIN, 172, 27, "GPIO_MODE_OUTPUT_OPEN_DRAIN") +QDEF1(MP_QSTR_GPIO_MODE_OUTPUT_PUSH_PULL, 179, 26, "GPIO_MODE_OUTPUT_PUSH_PULL") +QDEF1(MP_QSTR_GPIO_PIN_PA4, 102, 12, "GPIO_PIN_PA4") +QDEF1(MP_QSTR_GPIO_PIN_PA6, 100, 12, "GPIO_PIN_PA6") +QDEF1(MP_QSTR_GPIO_PIN_PA7, 101, 12, "GPIO_PIN_PA7") +QDEF1(MP_QSTR_GPIO_PIN_PB2, 3, 12, "GPIO_PIN_PB2") +QDEF1(MP_QSTR_GPIO_PIN_PB3, 2, 12, "GPIO_PIN_PB3") +QDEF1(MP_QSTR_GPIO_PIN_PC0, 32, 12, "GPIO_PIN_PC0") +QDEF1(MP_QSTR_GPIO_PIN_PC1, 33, 12, "GPIO_PIN_PC1") +QDEF1(MP_QSTR_GPIO_PIN_PC3, 35, 12, "GPIO_PIN_PC3") +QDEF1(MP_QSTR_GPIO_PULL_DOWN, 163, 14, "GPIO_PULL_DOWN") +QDEF1(MP_QSTR_GPIO_PULL_NO, 144, 12, "GPIO_PULL_NO") +QDEF1(MP_QSTR_GPIO_PULL_UP, 52, 12, "GPIO_PULL_UP") +QDEF1(MP_QSTR_GPIO_SPEED_HIGH, 125, 15, "GPIO_SPEED_HIGH") +QDEF1(MP_QSTR_GPIO_SPEED_LOW, 71, 14, "GPIO_SPEED_LOW") +QDEF1(MP_QSTR_GPIO_SPEED_MEDIUM, 78, 17, "GPIO_SPEED_MEDIUM") +QDEF1(MP_QSTR_GPIO_SPEED_VERY_HIGH, 154, 20, "GPIO_SPEED_VERY_HIGH") +QDEF1(MP_QSTR_INPUT_BUTTON_BACK, 174, 17, "INPUT_BUTTON_BACK") +QDEF1(MP_QSTR_INPUT_BUTTON_DOWN, 247, 17, "INPUT_BUTTON_DOWN") +QDEF1(MP_QSTR_INPUT_BUTTON_LEFT, 94, 17, "INPUT_BUTTON_LEFT") +QDEF1(MP_QSTR_INPUT_BUTTON_OK, 33, 15, "INPUT_BUTTON_OK") +QDEF1(MP_QSTR_INPUT_BUTTON_RIGHT, 5, 18, "INPUT_BUTTON_RIGHT") +QDEF1(MP_QSTR_INPUT_BUTTON_UP, 96, 15, "INPUT_BUTTON_UP") +QDEF1(MP_QSTR_INPUT_TYPE_LONG, 97, 15, "INPUT_TYPE_LONG") +QDEF1(MP_QSTR_INPUT_TYPE_PRESS, 108, 16, "INPUT_TYPE_PRESS") +QDEF1(MP_QSTR_INPUT_TYPE_RELEASE, 34, 18, "INPUT_TYPE_RELEASE") +QDEF1(MP_QSTR_INPUT_TYPE_REPEAT, 28, 17, "INPUT_TYPE_REPEAT") +QDEF1(MP_QSTR_INPUT_TYPE_SHORT, 153, 16, "INPUT_TYPE_SHORT") +QDEF1(MP_QSTR_LIGHT_BACKLIGHT, 17, 15, "LIGHT_BACKLIGHT") +QDEF1(MP_QSTR_LIGHT_BLUE, 90, 10, "LIGHT_BLUE") +QDEF1(MP_QSTR_LIGHT_GREEN, 95, 11, "LIGHT_GREEN") +QDEF1(MP_QSTR_LIGHT_RED, 215, 9, "LIGHT_RED") +QDEF1(MP_QSTR_SPEAKER_NOTE_A0, 95, 15, "SPEAKER_NOTE_A0") +QDEF1(MP_QSTR_SPEAKER_NOTE_A1, 94, 15, "SPEAKER_NOTE_A1") +QDEF1(MP_QSTR_SPEAKER_NOTE_A2, 93, 15, "SPEAKER_NOTE_A2") +QDEF1(MP_QSTR_SPEAKER_NOTE_A3, 92, 15, "SPEAKER_NOTE_A3") +QDEF1(MP_QSTR_SPEAKER_NOTE_A4, 91, 15, "SPEAKER_NOTE_A4") +QDEF1(MP_QSTR_SPEAKER_NOTE_A5, 90, 15, "SPEAKER_NOTE_A5") +QDEF1(MP_QSTR_SPEAKER_NOTE_A6, 89, 15, "SPEAKER_NOTE_A6") +QDEF1(MP_QSTR_SPEAKER_NOTE_A7, 88, 15, "SPEAKER_NOTE_A7") +QDEF1(MP_QSTR_SPEAKER_NOTE_A8, 87, 15, "SPEAKER_NOTE_A8") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS0, 140, 16, "SPEAKER_NOTE_AS0") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS1, 141, 16, "SPEAKER_NOTE_AS1") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS2, 142, 16, "SPEAKER_NOTE_AS2") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS3, 143, 16, "SPEAKER_NOTE_AS3") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS4, 136, 16, "SPEAKER_NOTE_AS4") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS5, 137, 16, "SPEAKER_NOTE_AS5") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS6, 138, 16, "SPEAKER_NOTE_AS6") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS7, 139, 16, "SPEAKER_NOTE_AS7") +QDEF1(MP_QSTR_SPEAKER_NOTE_AS8, 132, 16, "SPEAKER_NOTE_AS8") +QDEF1(MP_QSTR_SPEAKER_NOTE_B0, 60, 15, "SPEAKER_NOTE_B0") +QDEF1(MP_QSTR_SPEAKER_NOTE_B1, 61, 15, "SPEAKER_NOTE_B1") +QDEF1(MP_QSTR_SPEAKER_NOTE_B2, 62, 15, "SPEAKER_NOTE_B2") +QDEF1(MP_QSTR_SPEAKER_NOTE_B3, 63, 15, "SPEAKER_NOTE_B3") +QDEF1(MP_QSTR_SPEAKER_NOTE_B4, 56, 15, "SPEAKER_NOTE_B4") +QDEF1(MP_QSTR_SPEAKER_NOTE_B5, 57, 15, "SPEAKER_NOTE_B5") +QDEF1(MP_QSTR_SPEAKER_NOTE_B6, 58, 15, "SPEAKER_NOTE_B6") +QDEF1(MP_QSTR_SPEAKER_NOTE_B7, 59, 15, "SPEAKER_NOTE_B7") +QDEF1(MP_QSTR_SPEAKER_NOTE_B8, 52, 15, "SPEAKER_NOTE_B8") +QDEF1(MP_QSTR_SPEAKER_NOTE_C0, 29, 15, "SPEAKER_NOTE_C0") +QDEF1(MP_QSTR_SPEAKER_NOTE_C1, 28, 15, "SPEAKER_NOTE_C1") +QDEF1(MP_QSTR_SPEAKER_NOTE_C2, 31, 15, "SPEAKER_NOTE_C2") +QDEF1(MP_QSTR_SPEAKER_NOTE_C3, 30, 15, "SPEAKER_NOTE_C3") +QDEF1(MP_QSTR_SPEAKER_NOTE_C4, 25, 15, "SPEAKER_NOTE_C4") +QDEF1(MP_QSTR_SPEAKER_NOTE_C5, 24, 15, "SPEAKER_NOTE_C5") +QDEF1(MP_QSTR_SPEAKER_NOTE_C6, 27, 15, "SPEAKER_NOTE_C6") +QDEF1(MP_QSTR_SPEAKER_NOTE_C7, 26, 15, "SPEAKER_NOTE_C7") +QDEF1(MP_QSTR_SPEAKER_NOTE_C8, 21, 15, "SPEAKER_NOTE_C8") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS0, 14, 16, "SPEAKER_NOTE_CS0") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS1, 15, 16, "SPEAKER_NOTE_CS1") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS2, 12, 16, "SPEAKER_NOTE_CS2") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS3, 13, 16, "SPEAKER_NOTE_CS3") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS4, 10, 16, "SPEAKER_NOTE_CS4") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS5, 11, 16, "SPEAKER_NOTE_CS5") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS6, 8, 16, "SPEAKER_NOTE_CS6") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS7, 9, 16, "SPEAKER_NOTE_CS7") +QDEF1(MP_QSTR_SPEAKER_NOTE_CS8, 6, 16, "SPEAKER_NOTE_CS8") +QDEF1(MP_QSTR_SPEAKER_NOTE_D0, 250, 15, "SPEAKER_NOTE_D0") +QDEF1(MP_QSTR_SPEAKER_NOTE_D1, 251, 15, "SPEAKER_NOTE_D1") +QDEF1(MP_QSTR_SPEAKER_NOTE_D2, 248, 15, "SPEAKER_NOTE_D2") +QDEF1(MP_QSTR_SPEAKER_NOTE_D3, 249, 15, "SPEAKER_NOTE_D3") +QDEF1(MP_QSTR_SPEAKER_NOTE_D4, 254, 15, "SPEAKER_NOTE_D4") +QDEF1(MP_QSTR_SPEAKER_NOTE_D5, 255, 15, "SPEAKER_NOTE_D5") +QDEF1(MP_QSTR_SPEAKER_NOTE_D6, 252, 15, "SPEAKER_NOTE_D6") +QDEF1(MP_QSTR_SPEAKER_NOTE_D7, 253, 15, "SPEAKER_NOTE_D7") +QDEF1(MP_QSTR_SPEAKER_NOTE_D8, 242, 15, "SPEAKER_NOTE_D8") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS0, 137, 16, "SPEAKER_NOTE_DS0") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS1, 136, 16, "SPEAKER_NOTE_DS1") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS2, 139, 16, "SPEAKER_NOTE_DS2") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS3, 138, 16, "SPEAKER_NOTE_DS3") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS4, 141, 16, "SPEAKER_NOTE_DS4") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS5, 140, 16, "SPEAKER_NOTE_DS5") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS6, 143, 16, "SPEAKER_NOTE_DS6") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS7, 142, 16, "SPEAKER_NOTE_DS7") +QDEF1(MP_QSTR_SPEAKER_NOTE_DS8, 129, 16, "SPEAKER_NOTE_DS8") +QDEF1(MP_QSTR_SPEAKER_NOTE_E0, 219, 15, "SPEAKER_NOTE_E0") +QDEF1(MP_QSTR_SPEAKER_NOTE_E1, 218, 15, "SPEAKER_NOTE_E1") +QDEF1(MP_QSTR_SPEAKER_NOTE_E2, 217, 15, "SPEAKER_NOTE_E2") +QDEF1(MP_QSTR_SPEAKER_NOTE_E3, 216, 15, "SPEAKER_NOTE_E3") +QDEF1(MP_QSTR_SPEAKER_NOTE_E4, 223, 15, "SPEAKER_NOTE_E4") +QDEF1(MP_QSTR_SPEAKER_NOTE_E5, 222, 15, "SPEAKER_NOTE_E5") +QDEF1(MP_QSTR_SPEAKER_NOTE_E6, 221, 15, "SPEAKER_NOTE_E6") +QDEF1(MP_QSTR_SPEAKER_NOTE_E7, 220, 15, "SPEAKER_NOTE_E7") +QDEF1(MP_QSTR_SPEAKER_NOTE_E8, 211, 15, "SPEAKER_NOTE_E8") +QDEF1(MP_QSTR_SPEAKER_NOTE_F0, 184, 15, "SPEAKER_NOTE_F0") +QDEF1(MP_QSTR_SPEAKER_NOTE_F1, 185, 15, "SPEAKER_NOTE_F1") +QDEF1(MP_QSTR_SPEAKER_NOTE_F2, 186, 15, "SPEAKER_NOTE_F2") +QDEF1(MP_QSTR_SPEAKER_NOTE_F3, 187, 15, "SPEAKER_NOTE_F3") +QDEF1(MP_QSTR_SPEAKER_NOTE_F4, 188, 15, "SPEAKER_NOTE_F4") +QDEF1(MP_QSTR_SPEAKER_NOTE_F5, 189, 15, "SPEAKER_NOTE_F5") +QDEF1(MP_QSTR_SPEAKER_NOTE_F6, 190, 15, "SPEAKER_NOTE_F6") +QDEF1(MP_QSTR_SPEAKER_NOTE_F7, 191, 15, "SPEAKER_NOTE_F7") +QDEF1(MP_QSTR_SPEAKER_NOTE_F8, 176, 15, "SPEAKER_NOTE_F8") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS0, 11, 16, "SPEAKER_NOTE_FS0") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS1, 10, 16, "SPEAKER_NOTE_FS1") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS2, 9, 16, "SPEAKER_NOTE_FS2") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS3, 8, 16, "SPEAKER_NOTE_FS3") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS4, 15, 16, "SPEAKER_NOTE_FS4") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS5, 14, 16, "SPEAKER_NOTE_FS5") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS6, 13, 16, "SPEAKER_NOTE_FS6") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS7, 12, 16, "SPEAKER_NOTE_FS7") +QDEF1(MP_QSTR_SPEAKER_NOTE_FS8, 3, 16, "SPEAKER_NOTE_FS8") +QDEF1(MP_QSTR_SPEAKER_NOTE_G0, 153, 15, "SPEAKER_NOTE_G0") +QDEF1(MP_QSTR_SPEAKER_NOTE_G1, 152, 15, "SPEAKER_NOTE_G1") +QDEF1(MP_QSTR_SPEAKER_NOTE_G2, 155, 15, "SPEAKER_NOTE_G2") +QDEF1(MP_QSTR_SPEAKER_NOTE_G3, 154, 15, "SPEAKER_NOTE_G3") +QDEF1(MP_QSTR_SPEAKER_NOTE_G4, 157, 15, "SPEAKER_NOTE_G4") +QDEF1(MP_QSTR_SPEAKER_NOTE_G5, 156, 15, "SPEAKER_NOTE_G5") +QDEF1(MP_QSTR_SPEAKER_NOTE_G6, 159, 15, "SPEAKER_NOTE_G6") +QDEF1(MP_QSTR_SPEAKER_NOTE_G7, 158, 15, "SPEAKER_NOTE_G7") +QDEF1(MP_QSTR_SPEAKER_NOTE_G8, 145, 15, "SPEAKER_NOTE_G8") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS0, 10, 16, "SPEAKER_NOTE_GS0") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS1, 11, 16, "SPEAKER_NOTE_GS1") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS2, 8, 16, "SPEAKER_NOTE_GS2") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS3, 9, 16, "SPEAKER_NOTE_GS3") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS4, 14, 16, "SPEAKER_NOTE_GS4") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS5, 15, 16, "SPEAKER_NOTE_GS5") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS6, 12, 16, "SPEAKER_NOTE_GS6") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS7, 13, 16, "SPEAKER_NOTE_GS7") +QDEF1(MP_QSTR_SPEAKER_NOTE_GS8, 2, 16, "SPEAKER_NOTE_GS8") +QDEF1(MP_QSTR_SPEAKER_VOLUME_MAX, 66, 18, "SPEAKER_VOLUME_MAX") +QDEF1(MP_QSTR_SPEAKER_VOLUME_MIN, 92, 18, "SPEAKER_VOLUME_MIN") +QDEF0(MP_QSTR___add__, 196, 7, "__add__") +QDEF1(MP_QSTR___bases__, 3, 9, "__bases__") +QDEF0(MP_QSTR___bool__, 43, 8, "__bool__") +QDEF1(MP_QSTR___build_class__, 66, 15, "__build_class__") +QDEF0(MP_QSTR___contains__, 198, 12, "__contains__") +QDEF1(MP_QSTR___dict__, 127, 8, "__dict__") +QDEF0(MP_QSTR___eq__, 113, 6, "__eq__") +QDEF1(MP_QSTR___file__, 3, 8, "__file__") +QDEF0(MP_QSTR___float__, 53, 9, "__float__") +QDEF0(MP_QSTR___ge__, 167, 6, "__ge__") +QDEF1(MP_QSTR___globals__, 157, 11, "__globals__") +QDEF0(MP_QSTR___gt__, 182, 6, "__gt__") +QDEF0(MP_QSTR___iadd__, 109, 8, "__iadd__") +QDEF1(MP_QSTR___import__, 56, 10, "__import__") +QDEF0(MP_QSTR___isub__, 8, 8, "__isub__") +QDEF0(MP_QSTR___le__, 204, 6, "__le__") +QDEF0(MP_QSTR___lt__, 93, 6, "__lt__") +QDEF0(MP_QSTR___ne__, 14, 6, "__ne__") +QDEF1(MP_QSTR___path__, 200, 8, "__path__") +QDEF1(MP_QSTR___repl_print__, 1, 14, "__repl_print__") +QDEF1(MP_QSTR___reversed__, 97, 12, "__reversed__") +QDEF0(MP_QSTR___sub__, 33, 7, "__sub__") +QDEF1(MP_QSTR___traceback__, 79, 13, "__traceback__") +QDEF1(MP_QSTR__gpio_trigger_handler, 235, 21, "_gpio_trigger_handler") +QDEF1(MP_QSTR__input_trigger_handler, 108, 22, "_input_trigger_handler") +QDEF1(MP_QSTR_adc_read_pin_value, 178, 18, "adc_read_pin_value") +QDEF1(MP_QSTR_adc_read_pin_voltage, 251, 20, "adc_read_pin_voltage") +QDEF1(MP_QSTR_add, 68, 3, "add") +QDEF1(MP_QSTR_bin, 224, 3, "bin") +QDEF1(MP_QSTR_bound_method, 151, 12, "bound_method") +QDEF1(MP_QSTR_canvas_clear, 107, 12, "canvas_clear") +QDEF1(MP_QSTR_canvas_draw_box, 56, 15, "canvas_draw_box") +QDEF1(MP_QSTR_canvas_draw_circle, 63, 18, "canvas_draw_circle") +QDEF1(MP_QSTR_canvas_draw_disc, 176, 16, "canvas_draw_disc") +QDEF1(MP_QSTR_canvas_draw_dot, 178, 15, "canvas_draw_dot") +QDEF1(MP_QSTR_canvas_draw_frame, 240, 17, "canvas_draw_frame") +QDEF1(MP_QSTR_canvas_draw_line, 67, 16, "canvas_draw_line") +QDEF1(MP_QSTR_canvas_height, 77, 13, "canvas_height") +QDEF1(MP_QSTR_canvas_set_color, 178, 16, "canvas_set_color") +QDEF1(MP_QSTR_canvas_set_font, 124, 15, "canvas_set_font") +QDEF1(MP_QSTR_canvas_set_text, 114, 15, "canvas_set_text") +QDEF1(MP_QSTR_canvas_set_text_align, 192, 21, "canvas_set_text_align") +QDEF1(MP_QSTR_canvas_text_height, 239, 18, "canvas_text_height") +QDEF1(MP_QSTR_canvas_text_width, 86, 17, "canvas_text_width") +QDEF1(MP_QSTR_canvas_update, 131, 13, "canvas_update") +QDEF1(MP_QSTR_canvas_width, 180, 12, "canvas_width") +QDEF1(MP_QSTR_closure, 116, 7, "closure") +QDEF1(MP_QSTR_decode, 169, 6, "decode") +QDEF1(MP_QSTR_default, 206, 7, "default") +QDEF1(MP_QSTR_delattr, 219, 7, "delattr") +QDEF1(MP_QSTR_dialog_message_clear, 95, 20, "dialog_message_clear") +QDEF1(MP_QSTR_dialog_message_set_button, 45, 25, "dialog_message_set_button") +QDEF1(MP_QSTR_dialog_message_set_header, 196, 25, "dialog_message_set_header") +QDEF1(MP_QSTR_dialog_message_set_text, 198, 23, "dialog_message_set_text") +QDEF1(MP_QSTR_dialog_message_show, 69, 19, "dialog_message_show") +QDEF1(MP_QSTR_dict_view, 45, 9, "dict_view") +QDEF1(MP_QSTR_difference, 114, 10, "difference") +QDEF1(MP_QSTR_difference_update, 156, 17, "difference_update") +QDEF1(MP_QSTR_discard, 15, 7, "discard") +QDEF1(MP_QSTR_encode, 67, 6, "encode") +QDEF1(MP_QSTR_errno, 193, 5, "errno") +QDEF1(MP_QSTR_filter, 37, 6, "filter") +QDEF1(MP_QSTR_flipperzero, 179, 11, "flipperzero") +QDEF1(MP_QSTR_float, 53, 5, "float") +QDEF1(MP_QSTR_function, 39, 8, "function") +QDEF1(MP_QSTR_generator, 150, 9, "generator") +QDEF1(MP_QSTR_getrandbits, 102, 11, "getrandbits") +QDEF1(MP_QSTR_gpio_deinit_pin, 120, 15, "gpio_deinit_pin") +QDEF1(MP_QSTR_gpio_get_pin, 85, 12, "gpio_get_pin") +QDEF1(MP_QSTR_gpio_init_pin, 185, 13, "gpio_init_pin") +QDEF1(MP_QSTR_gpio_set_pin, 65, 12, "gpio_set_pin") +QDEF1(MP_QSTR_hex, 112, 3, "hex") +QDEF1(MP_QSTR_infrared_is_busy, 195, 16, "infrared_is_busy") +QDEF1(MP_QSTR_infrared_receive, 112, 16, "infrared_receive") +QDEF1(MP_QSTR_infrared_transmit, 81, 17, "infrared_transmit") +QDEF1(MP_QSTR_intersection, 40, 12, "intersection") +QDEF1(MP_QSTR_intersection_update, 6, 19, "intersection_update") +QDEF1(MP_QSTR_isdisjoint, 247, 10, "isdisjoint") +QDEF1(MP_QSTR_issubset, 185, 8, "issubset") +QDEF1(MP_QSTR_issuperset, 252, 10, "issuperset") +QDEF1(MP_QSTR_iterator, 71, 8, "iterator") +QDEF1(MP_QSTR_light_blink_set_color, 217, 21, "light_blink_set_color") +QDEF1(MP_QSTR_light_blink_start, 121, 17, "light_blink_start") +QDEF1(MP_QSTR_light_blink_stop, 33, 16, "light_blink_stop") +QDEF1(MP_QSTR_light_set, 134, 9, "light_set") +QDEF1(MP_QSTR_max, 177, 3, "max") +QDEF1(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded, 115, 32, "maximum recursion depth exceeded") +QDEF1(MP_QSTR_min, 175, 3, "min") +QDEF1(MP_QSTR_module, 191, 6, "module") +QDEF1(MP_QSTR_oct, 253, 3, "oct") +QDEF1(MP_QSTR_on_gpio, 106, 7, "on_gpio") +QDEF1(MP_QSTR_on_input, 141, 8, "on_input") +QDEF1(MP_QSTR_pwm_is_running, 82, 14, "pwm_is_running") +QDEF1(MP_QSTR_pwm_start, 240, 9, "pwm_start") +QDEF1(MP_QSTR_pwm_stop, 200, 8, "pwm_stop") +QDEF1(MP_QSTR_random, 190, 6, "random") +QDEF1(MP_QSTR_rb, 213, 2, "rb") +QDEF1(MP_QSTR_reversed, 161, 8, "reversed") +QDEF1(MP_QSTR_seed, 146, 4, "seed") +QDEF1(MP_QSTR_sleep, 234, 5, "sleep") +QDEF1(MP_QSTR_sleep_ms, 11, 8, "sleep_ms") +QDEF1(MP_QSTR_sleep_us, 19, 8, "sleep_us") +QDEF1(MP_QSTR_speaker_set_volume, 116, 18, "speaker_set_volume") +QDEF1(MP_QSTR_speaker_start, 1, 13, "speaker_start") +QDEF1(MP_QSTR_speaker_stop, 153, 12, "speaker_stop") +QDEF1(MP_QSTR_symmetric_difference, 206, 20, "symmetric_difference") +QDEF1(MP_QSTR_symmetric_difference_update, 96, 27, "symmetric_difference_update") +QDEF1(MP_QSTR_ticks_add, 157, 9, "ticks_add") +QDEF1(MP_QSTR_ticks_cpu, 26, 9, "ticks_cpu") +QDEF1(MP_QSTR_ticks_diff, 177, 10, "ticks_diff") +QDEF1(MP_QSTR_ticks_ms, 66, 8, "ticks_ms") +QDEF1(MP_QSTR_ticks_us, 90, 8, "ticks_us") +QDEF1(MP_QSTR_time, 240, 4, "time") +QDEF1(MP_QSTR_time_ns, 114, 7, "time_ns") +QDEF1(MP_QSTR_union, 246, 5, "union") +QDEF1(MP_QSTR_vibro_set, 216, 9, "vibro_set") +QDEF1(MP_QSTR__brace_open__colon__hash_b_brace_close_, 88, 5, "{:#b}") +QDEF1(MP_QSTR__brace_open__colon__hash_o_brace_close_, 245, 5, "{:#o}") +QDEF1(MP_QSTR__brace_open__colon__hash_x_brace_close_, 2, 5, "{:#x}") diff --git a/non_catalog_apps/mp_flipper/lib/micropython/genhdr/root_pointers.h b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/root_pointers.h new file mode 100644 index 00000000..256690f5 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/genhdr/root_pointers.h @@ -0,0 +1,3 @@ +// Automatically generated by make_root_pointers.py. + +mp_sched_item_t sched_queue[(4)]; diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_compiler.c b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_compiler.c new file mode 100644 index 00000000..2318d774 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_compiler.c @@ -0,0 +1,98 @@ +#include + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/persistentcode.h" +#include "py/gc.h" +#include "py/stackctrl.h" +#include "shared/runtime/gchelper.h" + +#include "mp_flipper_runtime.h" +#include "mp_flipper_compiler.h" +#include "mp_flipper_halport.h" + +void mp_flipper_exec_str(const char* code) { +#if MP_FLIPPER_IS_COMPILER + nlr_buf_t nlr; + + if(nlr_push(&nlr) == 0) { + // Compile, parse and execute the given string + mp_lexer_t* lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, code, strlen(code), 0); + mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(lex->source_name)); + mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); + mp_obj_t module_fun = mp_compile(&parse_tree, lex->source_name, true); + mp_call_function_0(module_fun); + nlr_pop(); + } else { + // Uncaught exception: print it out. + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +#endif +} + +void mp_flipper_exec_py_file(const char* file_path) { +#if MP_FLIPPER_IS_COMPILER + nlr_buf_t nlr; + + if(nlr_push(&nlr) == 0) { + do { + // check if file exists + if(mp_flipper_import_stat(file_path) == MP_FLIPPER_IMPORT_STAT_NO_EXIST) { + mp_raise_OSError_with_filename(MP_ENOENT, file_path); + + break; + } + + // Compile, parse and execute the given file + mp_lexer_t* lex = mp_lexer_new_from_file(qstr_from_str(file_path)); + mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(lex->source_name)); + mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); + mp_obj_t module_fun = mp_compile(&parse_tree, lex->source_name, false); + mp_call_function_0(module_fun); + } while(false); + + nlr_pop(); + } else { + // Uncaught exception: print it out. + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +#endif +} + +void mp_flipper_compile_and_save_file(const char* py_file_path, const char* mpy_file_path) { +#if MP_FLIPPER_IS_COMPILER + nlr_buf_t nlr; + + if(nlr_push(&nlr) == 0) { + mp_lexer_t* lex = mp_lexer_new_from_file(qstr_from_str(py_file_path)); + + mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(lex->source_name)); + + mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); + mp_compiled_module_t cm; + cm.context = m_new_obj(mp_module_context_t); + mp_compile_to_raw_code(&parse_tree, lex->source_name, false, &cm); + + mp_print_t* print = malloc(sizeof(mp_print_t)); + + print->data = mp_flipper_print_data_alloc(); + print->print_strn = mp_flipper_print_strn; + + mp_raw_code_save(&cm, print); + + const char* data = mp_flipper_print_get_data(print->data); + size_t size = mp_flipper_print_get_data_length(print->data); + + mp_flipper_save_file(mpy_file_path, data, size); + + mp_flipper_print_data_free(print->data); + + free(print); + + nlr_pop(); + } else { + // Uncaught exception: print it out. + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +#endif +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_compiler.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_compiler.h new file mode 100644 index 00000000..b145f506 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_compiler.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include "mp_flipper_runtime.h" + +void mp_flipper_exec_str(const char* str); +void mp_flipper_exec_py_file(const char* file_path); +void mp_flipper_compile_and_save_file(const char* py_file_path, const char* mpy_file_path); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config.h new file mode 100644 index 00000000..399de446 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config.h @@ -0,0 +1,211 @@ +// Need to provide a declaration/definition of alloca() +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include +#else +#include +#endif + +#include + +#ifndef MP_FLIPPER_IS_COMPILER +#ifndef MP_FLIPPER_COMPILER +#define MP_FLIPPER_IS_COMPILER (0) +#else +#define MP_FLIPPER_IS_COMPILER (1) +#endif +#endif + +#ifndef MP_FLIPPER_IS_RUNTIME +#ifndef MP_FLIPPER_RUNTIME +#define MP_FLIPPER_IS_RUNTIME (0) +#else +#define MP_FLIPPER_IS_RUNTIME (1) +#endif +#endif + +// Type definitions for the specific machine +typedef int32_t mp_int_t; // must be pointer size +typedef uint32_t mp_uint_t; // must be pointer size +typedef long mp_off_t; + +#ifdef MP_FLIPPER_SPLIT_HEAP +#define MICROPY_GC_SPLIT_HEAP (1) +#define MICROPY_GC_SPLIT_HEAP_AUTO (1) +#endif + +#define MICROPY_MPHALPORT_H "mp_flipper_halport.h" + +#define MICROPY_MIN_USE_CORTEX_CPU (1) +#define MICROPY_MIN_USE_STM32_MCU (1) + +#define MICROPY_HW_BOARD_NAME "Flipper Zero" +#define MICROPY_HW_MCU_NAME "STM32WB55RG" + +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES) + +#ifdef MP_FLIPPER_MPY_SUPPORT +#define MICROPY_PERSISTENT_CODE_LOAD (MP_FLIPPER_IS_RUNTIME) +#define MICROPY_PERSISTENT_CODE_SAVE (MP_FLIPPER_IS_COMPILER) +#else +#define MICROPY_PERSISTENT_CODE_LOAD (0) +#define MICROPY_PERSISTENT_CODE_SAVE (0) +#endif +#define MICROPY_PERSISTENT_CODE_SAVE_FILE (0) + +#define MICROPY_ENABLE_COMPILER (MP_FLIPPER_IS_COMPILER) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_PY_GC_COLLECT_RETVAL (1) +#define MICROPY_ENABLE_PYSTACK (0) +#define MICROPY_STACK_CHECK (1) +#define MICROPY_ALLOC_PATH_MAX (256) + +#define MICROPY_ENABLE_FINALISER (0) + +#ifdef MP_FLIPPER_FIRMWARE +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) +#else +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NONE) +#endif + +#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t + +#define MICROPY_PY___FILE__ (1) +#define MICROPY_ENABLE_EXTERNAL_IMPORT (1) +#define MICROPY_READER_VFS (1) + +#define MICROPY_ENABLE_VM_ABORT (0) + +#define MICROPY_PY_ERRNO (0) +#define MICROPY_USE_INTERNAL_ERRNO (0) +#define MICROPY_PY_ERRNO_ERRORCODE (0) + +#define MICROPY_PY_TIME (1) +#define MICROPY_PY_TIME_TIME_TIME_NS (1) + +#define MICROPY_PY_RANDOM (1) +#define MICROPY_PY_RANDOM_EXTRA_FUNCS (0) + +#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (mp_flipper_seed_init()) + +#ifdef MP_FLIPPER_FIRMWARE +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) +#else +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE) +#endif + +#ifdef MP_FLIPPER_FIRMWARE +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) +#else +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#endif + +#ifdef MP_FLIPPER_FIRMWARE +#define MICROPY_HELPER_REPL (MP_FLIPPER_IS_COMPILER) +#else +#define MICROPY_HELPER_REPL (0) +#endif + +#define MICROPY_ENABLE_SOURCE_LINE (0) +#define MICROPY_ENABLE_DOC_STRING (0) + +#define MICROPY_REPL_INFO (0) +#define MICROPY_REPL_EMACS_KEYS (0) +#define MICROPY_REPL_EMACS_WORDS_MOVE (0) +#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (0) +#define MICROPY_REPL_AUTO_INDENT (0) +#define MICROPY_REPL_EVENT_DRIVEN (0) +#define MICROPY_READLINE_HISTORY_SIZE (0) + +#define MICROPY_CPYTHON_COMPAT (1) +#define MICROPY_FULL_CHECKS (0) +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) + +#define MICROPY_MODULE_FROZEN_MPY (0) + +#define MICROPY_PY_CMATH (0) +#define MICROPY_PY_BUILTINS_COMPLEX (0) +#define MICROPY_MULTIPLE_INHERITANCE (0) +#define MICROPY_MODULE_GETATTR (0) +#define MICROPY_PY_FUNCTION_ATTRS (1) +#define MICROPY_PY_DESCRIPTORS (0) +#define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_ASSIGN_EXPR (0) +#define MICROPY_PY_GENERATOR_PEND_THROW (0) +#define MICROPY_PY_BUILTINS_BYTES_HEX (0) +#define MICROPY_PY_BUILTINS_STR_UNICODE (0) +#define MICROPY_PY_BUILTINS_STR_CENTER (0) +#define MICROPY_PY_BUILTINS_STR_COUNT (0) +#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0) +#define MICROPY_PY_BUILTINS_STR_PARTITION (0) +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (0) +#define MICROPY_PY_BUILTINS_BYTEARRAY (0) +#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (0) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (0) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (0) +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (0) +#define MICROPY_PY_BUILTINS_SLICE_INDICES (0) +#define MICROPY_PY_BUILTINS_FROZENSET (0) +#define MICROPY_PY_BUILTINS_PROPERTY (0) +#define MICROPY_PY_BUILTINS_RANGE_ATTRS (0) +#define MICROPY_PY_BUILTINS_RANGE_BINOP (0) +#define MICROPY_PY_BUILTINS_NEXT2 (0) +#define MICROPY_PY_BUILTINS_ROUND_INT (0) +#define MICROPY_PY_ALL_SPECIAL_METHODS (0) +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (0) +#define MICROPY_PY_BUILTINS_ENUMERATE (0) +#define MICROPY_PY_BUILTINS_COMPILE (0) +#define MICROPY_PY_BUILTINS_EVAL_EXEC (0) +#define MICROPY_PY_BUILTINS_EXECFILE (0) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (0) +#define MICROPY_PY_BUILTINS_INPUT (0) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY_BUILTINS_POW3 (0) +#define MICROPY_PY_BUILTINS_HELP (0) +#define MICROPY_PY_MICROPYTHON (0) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (0) +#define MICROPY_PY_MICROPYTHON_STACK_USE (0) +#define MICROPY_PY_MICROPYTHON_HEAP_LOCKED (0) +#define MICROPY_PY_ARRAY (0) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (0) +#define MICROPY_PY_ATTRTUPLE (0) +#define MICROPY_PY_COLLECTIONS (0) +#define MICROPY_PY_STRUCT (0) +#define MICROPY_PY_GC (0) +#define MICROPY_PY_SYS (0) +#define MICROPY_PY_SYS_MODULES (0) +#define MICROPY_PY_SELECT_SELECT (0) +#define MICROPY_PY_SYS_EXIT (0) +#define MICROPY_PY_RE (0) +#define MICROPY_PY_CRYPTOLIB (0) +#define MICROPY_PY_VFS (0) +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_MODULE_BUILTIN_INIT (1) + +#ifdef MP_FLIPPER_MATH +#define MICROPY_PY_MATH (1) +#else +#define MICROPY_PY_MATH (0) +#endif + +#ifdef MP_FLIPPER_IO +#define MICROPY_PY_IO (1) +#else +#define MICROPY_PY_IO (0) +#endif + +#ifdef MP_FLIPPER_JSON +#define MICROPY_PY_JSON (1) +#define MICROPY_PY_JSON_SEPARATORS (1) +#else +#define MICROPY_PY_JSON (0) +#define MICROPY_PY_JSON_SEPARATORS (0) +#endif + +#define MICROPY_COMP_CONST_FOLDING (0) +#define MICROPY_COMP_CONST_TUPLE (0) +#define MICROPY_COMP_CONST_LITERAL (0) +#define MICROPY_COMP_CONST (0) +#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0) \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config_fap.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config_fap.h new file mode 100644 index 00000000..1f87805b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config_fap.h @@ -0,0 +1,7 @@ +#pragma once + +#define MP_FLIPPER_COMPILER +#define MP_FLIPPER_RUNTIME +#define MP_FLIPPER_SPLIT_HEAP + +#include "mp_flipper_config.h" diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config_firmware.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config_firmware.h new file mode 100644 index 00000000..483b6222 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_config_firmware.h @@ -0,0 +1,8 @@ +#pragma once + +#define MP_FLIPPER_COMPILER +#define MP_FLIPPER_RUNTIME +#define MP_FLIPPER_SPLIT_HEAP +#define MP_FLIPPER_IO + +#include "mp_flipper_config.h" diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_file_reader.c b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_file_reader.c new file mode 100644 index 00000000..e50a8160 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_file_reader.c @@ -0,0 +1,15 @@ +#include "py/reader.h" + +#include "mp_flipper_file_reader.h" + +static mp_uint_t mp_flipper_file_reader_read_internal(void* data) { + uint32_t character = mp_flipper_file_reader_read(data); + + return character == MP_FLIPPER_FILE_READER_EOF ? MP_READER_EOF : character; +} + +void mp_reader_new_file(mp_reader_t* reader, qstr filename) { + reader->data = mp_flipper_file_reader_context_alloc(qstr_str(filename)); + reader->readbyte = mp_flipper_file_reader_read_internal; + reader->close = mp_flipper_file_reader_close; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_file_reader.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_file_reader.h new file mode 100644 index 00000000..178292e3 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_file_reader.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#define MP_FLIPPER_FILE_READER_EOF ((uint32_t)(-1)) + +void* mp_flipper_file_reader_context_alloc(const char* filename); + +uint32_t mp_flipper_file_reader_read(void* data); + +void mp_flipper_file_reader_close(void* data); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_halport.c b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_halport.c new file mode 100644 index 00000000..b23da7fc --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_halport.c @@ -0,0 +1,40 @@ +#include + +#include "py/mphal.h" +#include "py/builtin.h" + +#include "mp_flipper_halport.h" + +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t* args, mp_map_t* kwargs) { + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); + +void mp_hal_stdout_tx_str(const char* str) { + mp_flipper_stdout_tx_str(str); +} + +void mp_hal_stdout_tx_strn_cooked(const char* str, size_t len) { + mp_flipper_stdout_tx_strn_cooked(str, len); +} + +mp_import_stat_t mp_import_stat(const char* path) { + mp_flipper_import_stat_t stat = mp_flipper_import_stat(path); + + if(stat == MP_FLIPPER_IMPORT_STAT_FILE) { + return MP_IMPORT_STAT_FILE; + } + + if(stat == MP_FLIPPER_IMPORT_STAT_DIR) { + return MP_IMPORT_STAT_DIR; + } + + return MP_IMPORT_STAT_NO_EXIST; +} + +#ifdef MP_FLIPPER_SPLIT_HEAP +size_t gc_get_max_new_split(void) { + return mp_flipper_gc_get_max_new_split(); +} +#endif \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_halport.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_halport.h new file mode 100644 index 00000000..31bd3e20 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_halport.h @@ -0,0 +1,21 @@ +#pragma once + +#include "mpconfigport.h" + +// Define so there's no dependency on extmod/virtpin.h +#define mp_hal_pin_obj_t + +typedef enum { + MP_FLIPPER_IMPORT_STAT_NO_EXIST, + MP_FLIPPER_IMPORT_STAT_FILE, + MP_FLIPPER_IMPORT_STAT_DIR, +} mp_flipper_import_stat_t; + +void mp_flipper_stdout_tx_str(const char* str); +void mp_flipper_stdout_tx_strn_cooked(const char* str, size_t len); + +mp_flipper_import_stat_t mp_flipper_import_stat(const char* path); + +#ifdef MP_FLIPPER_SPLIT_HEAP +size_t mp_flipper_gc_get_max_new_split(); +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modflipperzero.c b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modflipperzero.c new file mode 100644 index 00000000..3c88f23e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modflipperzero.c @@ -0,0 +1,874 @@ +#include +#include + +#include "py/objint.h" +#include "py/objfun.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "mp_flipper_modflipperzero.h" + +static mp_obj_t flipperzero_light_set(mp_obj_t light_obj, mp_obj_t brightness_obj) { + mp_int_t light = mp_obj_get_int(light_obj); + mp_int_t brightness = mp_obj_get_int(brightness_obj); + + mp_flipper_light_set(light, brightness); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_light_set_obj, flipperzero_light_set); + +static mp_obj_t flipperzero_light_blink_start(size_t n_args, const mp_obj_t* args) { + if(n_args != 4) { + return mp_const_none; + } + + mp_int_t light = mp_obj_get_int(args[0]); + mp_int_t brightness = mp_obj_get_int(args[1]); + mp_int_t on_time = mp_obj_get_int(args[2]); + mp_int_t period = mp_obj_get_int(args[3]); + + mp_flipper_light_blink_start(light, brightness, on_time, period); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_light_blink_start_obj, 4, 4, flipperzero_light_blink_start); + +static mp_obj_t flipperzero_light_blink_stop() { + mp_flipper_light_blink_stop(); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_light_blink_stop_obj, flipperzero_light_blink_stop); + +static mp_obj_t flipperzero_light_blink_set_color(mp_obj_t light_obj) { + mp_int_t light = mp_obj_get_int(light_obj); + + mp_flipper_light_blink_set_color(light); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_light_blink_set_color_obj, flipperzero_light_blink_set_color); + +static mp_obj_t flipperzero_vibro_set(mp_obj_t state) { + bool state_bool = mp_obj_is_true(state); + + mp_flipper_vibro(state_bool); + + return state_bool ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_vibro_set_obj, flipperzero_vibro_set); + +typedef struct _mp_obj_float_t { + mp_obj_base_t base; + mp_float_t value; +} mp_obj_float_t; + +/* +Python script for notes generation + +# coding: utf-8 +# Python script for notes generation + +from typing import List + +note_names: List = ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B'] + +for octave in range(9): + for name in note_names: + print("static const struct _mp_obj_float_t flipperzero_speaker_note_%s%s_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_%s%s};" % (name.lower(),octave,name,octave)) +*/ + +static const struct _mp_obj_float_t flipperzero_speaker_note_c0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b0_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B0}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b1_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B1}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b2_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B2}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b3_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B3}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b4_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B4}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b5_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B5}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b6_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B6}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b7_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B7}; +static const struct _mp_obj_float_t flipperzero_speaker_note_c8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_C8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_cs8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_CS8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_d8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_D8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_ds8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_DS8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_e8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_E8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_f8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_F8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_fs8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_FS8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_g8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_G8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_gs8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_GS8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_a8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_A8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_as8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_AS8}; +static const struct _mp_obj_float_t flipperzero_speaker_note_b8_obj = {{&mp_type_float}, (mp_float_t)MP_FLIPPER_SPEAKER_NOTE_B8}; + +static const struct _mp_obj_float_t flipperzero_speaker_volume_min_obj = { + {&mp_type_float}, + (mp_float_t)MP_FLIPPER_SPEAKER_VOLUME_MIN}; +static const struct _mp_obj_float_t flipperzero_speaker_volume_max_obj = { + {&mp_type_float}, + (mp_float_t)MP_FLIPPER_SPEAKER_VOLUME_MAX}; + +static mp_obj_t flipperzero_speaker_start(mp_obj_t frequency_obj, mp_obj_t volume_obj) { + mp_float_t frequency = mp_obj_get_float(frequency_obj); + mp_float_t volume = mp_obj_get_float(volume_obj); + + return mp_flipper_speaker_start(frequency, volume) ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_speaker_start_obj, flipperzero_speaker_start); + +static mp_obj_t flipperzero_speaker_set_volume(mp_obj_t volume_obj) { + mp_float_t volume = mp_obj_get_float(volume_obj); + + return mp_flipper_speaker_set_volume(volume) ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_speaker_set_volume_obj, flipperzero_speaker_set_volume); + +static mp_obj_t flipperzero_speaker_stop() { + mp_flipper_speaker_stop(); + + return mp_flipper_speaker_stop() ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_speaker_stop_obj, flipperzero_speaker_stop); + +static mp_obj_t flipperzero_canvas_width() { + uint8_t width = mp_flipper_canvas_width(); + + return mp_obj_new_int(width); +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_canvas_width_obj, flipperzero_canvas_width); + +static mp_obj_t flipperzero_canvas_height() { + uint8_t height = mp_flipper_canvas_height(); + + return mp_obj_new_int(height); +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_canvas_height_obj, flipperzero_canvas_height); + +static mp_obj_t flipperzero_canvas_text_width(mp_obj_t text_obj) { + const char* text = mp_obj_str_get_str(text_obj); + + uint8_t width = mp_flipper_canvas_text_width(text); + + return mp_obj_new_int(width); +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_canvas_text_width_obj, flipperzero_canvas_text_width); + +static mp_obj_t flipperzero_canvas_text_height() { + uint8_t height = mp_flipper_canvas_text_height(); + + return mp_obj_new_int(height); +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_canvas_text_height_obj, flipperzero_canvas_text_height); + +static mp_obj_t flipperzero_canvas_draw_dot(mp_obj_t x_obj, mp_obj_t y_obj) { + mp_int_t x = mp_obj_get_int(x_obj); + mp_int_t y = mp_obj_get_int(y_obj); + + mp_flipper_canvas_draw_dot(x, y); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_canvas_draw_dot_obj, flipperzero_canvas_draw_dot); + +static mp_obj_t flipperzero_canvas_draw_box(size_t n_args, const mp_obj_t* args) { + if(n_args < 4) { + return mp_const_none; + } + + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t width = mp_obj_get_int(args[2]); + mp_int_t height = mp_obj_get_int(args[3]); + mp_int_t radius = n_args == 5 ? mp_obj_get_int(args[4]) : 0; + + mp_flipper_canvas_draw_box(x, y, width, height, radius); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_canvas_draw_box_obj, 4, 5, flipperzero_canvas_draw_box); + +static mp_obj_t flipperzero_canvas_draw_frame(size_t n_args, const mp_obj_t* args) { + if(n_args < 4) { + return mp_const_none; + } + + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t width = mp_obj_get_int(args[2]); + mp_int_t height = mp_obj_get_int(args[3]); + mp_int_t radius = n_args == 5 ? mp_obj_get_int(args[4]) : 0; + + mp_flipper_canvas_draw_frame(x, y, width, height, radius); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_canvas_draw_frame_obj, 4, 5, flipperzero_canvas_draw_frame); + +static mp_obj_t flipperzero_canvas_draw_line(size_t n_args, const mp_obj_t* args) { + if(n_args != 4) { + return mp_const_none; + } + + mp_int_t x0 = mp_obj_get_int(args[0]); + mp_int_t y0 = mp_obj_get_int(args[1]); + mp_int_t x1 = mp_obj_get_int(args[2]); + mp_int_t y1 = mp_obj_get_int(args[3]); + + mp_flipper_canvas_draw_line(x0, y0, x1, y1); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_canvas_draw_line_obj, 4, 4, flipperzero_canvas_draw_line); + +static mp_obj_t flipperzero_canvas_draw_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj) { + mp_int_t x = mp_obj_get_int(x_obj); + mp_int_t y = mp_obj_get_int(y_obj); + mp_int_t r = mp_obj_get_int(r_obj); + + mp_flipper_canvas_draw_circle(x, y, r); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(flipperzero_canvas_draw_circle_obj, flipperzero_canvas_draw_circle); + +static mp_obj_t flipperzero_canvas_draw_disc(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj) { + mp_int_t x = mp_obj_get_int(x_obj); + mp_int_t y = mp_obj_get_int(y_obj); + mp_int_t r = mp_obj_get_int(r_obj); + + mp_flipper_canvas_draw_disc(x, y, r); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(flipperzero_canvas_draw_disc_obj, flipperzero_canvas_draw_disc); + +static mp_obj_t flipperzero_canvas_set_font(mp_obj_t font_obj) { + mp_int_t font = mp_obj_get_int(font_obj); + + mp_flipper_canvas_set_font(font); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_canvas_set_font_obj, flipperzero_canvas_set_font); + +static mp_obj_t flipperzero_canvas_set_color(mp_obj_t color_obj) { + mp_int_t color = mp_obj_get_int(color_obj); + + mp_flipper_canvas_set_color(color); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_canvas_set_color_obj, flipperzero_canvas_set_color); + +static mp_obj_t flipperzero_canvas_set_text(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t str_obj) { + mp_int_t x = mp_obj_get_int(x_obj); + mp_int_t y = mp_obj_get_int(y_obj); + + const char* str = mp_obj_str_get_str(str_obj); + + mp_flipper_canvas_set_text(x, y, str); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(flipperzero_canvas_set_text_obj, flipperzero_canvas_set_text); + +static mp_obj_t flipperzero_canvas_set_text_align(mp_obj_t x_obj, mp_obj_t y_obj) { + mp_int_t x = mp_obj_get_int(x_obj); + mp_int_t y = mp_obj_get_int(y_obj); + + mp_flipper_canvas_set_text_align(x, y); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_canvas_set_text_align_obj, flipperzero_canvas_set_text_align); + +static mp_obj_t flipperzero_canvas_update() { + mp_flipper_canvas_update(); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_canvas_update_obj, flipperzero_canvas_update); + +static mp_obj_t flipperzero_canvas_clear() { + mp_flipper_canvas_clear(); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_canvas_clear_obj, flipperzero_canvas_clear); + +static void* mp_flipper_on_input_callback = NULL; + +static mp_obj_t flipperzero_on_input(mp_obj_t callback_obj) { + mp_flipper_on_input_callback = callback_obj; + + return callback_obj; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_on_input_obj, flipperzero_on_input); + +static mp_obj_t flipperzero_input_trigger_handler(mp_obj_t flags_obj) { + if(mp_flipper_on_input_callback != NULL) { + mp_int_t flags = mp_obj_get_int(flags_obj); + + mp_obj_t button_obj = mp_obj_new_int(flags & MP_FLIPPER_INPUT_BUTTON); + mp_obj_t type_obj = mp_obj_new_int(flags & MP_FLIPPER_INPUT_TYPE); + + mp_call_function_2_protected(mp_flipper_on_input_callback, button_obj, type_obj); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_input_trigger_handler_obj, flipperzero_input_trigger_handler); + +static mp_obj_t flipperzero_dialog_message_set_text(size_t n_args, const mp_obj_t* args) { + if(n_args < 3) { + return mp_const_none; + } + + const char* text = mp_obj_str_get_str(args[0]); + + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + mp_int_t h = n_args > 3 ? mp_obj_get_int(args[3]) : MP_FLIPPER_ALIGN_BEGIN; + mp_int_t v = n_args > 4 ? mp_obj_get_int(args[4]) : MP_FLIPPER_ALIGN_BEGIN; + + mp_flipper_dialog_message_set_text(text, x, y, h, v); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_dialog_message_set_text_obj, 3, 5, flipperzero_dialog_message_set_text); + +static mp_obj_t flipperzero_dialog_message_set_header(size_t n_args, const mp_obj_t* args) { + if(n_args < 3) { + return mp_const_none; + } + + const char* text = mp_obj_str_get_str(args[0]); + + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + mp_int_t h = n_args > 3 ? mp_obj_get_int(args[3]) : MP_FLIPPER_ALIGN_BEGIN; + mp_int_t v = n_args > 4 ? mp_obj_get_int(args[4]) : MP_FLIPPER_ALIGN_BEGIN; + + mp_flipper_dialog_message_set_header(text, x, y, h, v); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_dialog_message_set_header_obj, 3, 5, flipperzero_dialog_message_set_header); + +static mp_obj_t flipperzero_dialog_message_set_button(mp_obj_t text_obj, mp_obj_t button_obj) { + const char* text = mp_obj_str_get_str(text_obj); + + mp_int_t button = mp_obj_get_int(button_obj); + + mp_flipper_dialog_message_set_button(text, button); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_dialog_message_set_button_obj, flipperzero_dialog_message_set_button); + +static mp_obj_t flipperzero_dialog_message_show() { + mp_int_t button = mp_flipper_dialog_message_show(); + + return mp_obj_new_int(button); +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_dialog_message_show_obj, flipperzero_dialog_message_show); + +static mp_obj_t flipperzero_dialog_message_clear() { + mp_flipper_dialog_message_clear(); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_dialog_message_clear_obj, flipperzero_dialog_message_clear); + +static void* mp_flipper_on_gpio_callback = NULL; + +static mp_obj_t flipperzero_gpio_init_pin(size_t n_args, const mp_obj_t* args) { + if(n_args < 2) { + return mp_const_false; + } + + mp_int_t pin = mp_obj_get_int(args[0]); + mp_int_t mode = mp_obj_get_int(args[1]); + mp_int_t pull = n_args > 2 ? mp_obj_get_int(args[2]) : MP_FLIPPER_GPIO_PULL_NO; + mp_int_t speed = n_args > 3 ? mp_obj_get_int(args[3]) : MP_FLIPPER_GPIO_SPEED_LOW; + + bool success = mp_flipper_gpio_init_pin(pin, mode, pull, speed); + + return success ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_gpio_init_pin_obj, 2, 4, flipperzero_gpio_init_pin); + +static mp_obj_t flipperzero_gpio_deinit_pin(mp_obj_t pin_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + + mp_flipper_gpio_deinit_pin(pin); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_gpio_deinit_pin_obj, flipperzero_gpio_deinit_pin); + +static mp_obj_t flipperzero_gpio_set_pin(mp_obj_t pin_obj, mp_obj_t state_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + bool state = mp_obj_is_true(state_obj); + + mp_flipper_gpio_set_pin(pin, state); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_gpio_set_pin_obj, flipperzero_gpio_set_pin); + +static mp_obj_t flipperzero_gpio_get_pin(mp_obj_t pin_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + + bool state = mp_flipper_gpio_get_pin(pin); + + return state ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_gpio_get_pin_obj, flipperzero_gpio_get_pin); + +static mp_obj_t flipperzero_on_gpio(mp_obj_t callback_obj) { + mp_flipper_on_gpio_callback = callback_obj; + + return callback_obj; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_on_gpio_obj, flipperzero_on_gpio); + +static mp_obj_t flipperzero_gpio_trigger_handler(mp_obj_t pin_obj) { + if(mp_flipper_on_gpio_callback != NULL) { + mp_call_function_1_protected(mp_flipper_on_gpio_callback, pin_obj); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_gpio_trigger_handler_obj, flipperzero_gpio_trigger_handler); + +static mp_obj_t flipperzero_adc_read_pin_value(mp_obj_t pin_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + + mp_int_t value = mp_flipper_adc_read_pin(pin); + + return mp_obj_new_int(value); +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_adc_read_pin_value_obj, flipperzero_adc_read_pin_value); + +static mp_obj_t flipperzero_adc_read_pin_voltage(mp_obj_t pin_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + + uint16_t value = mp_flipper_adc_read_pin(pin); + float voltage = mp_flipper_adc_convert_to_voltage(value); + + return mp_obj_new_float(voltage); +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_adc_read_pin_voltage_obj, flipperzero_adc_read_pin_voltage); + +static mp_obj_t flipperzero_pwm_start(mp_obj_t pin_obj, mp_obj_t frequency_obj, mp_obj_t duty_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + mp_int_t frequency = mp_obj_get_int(frequency_obj); + mp_int_t duty = mp_obj_get_int(duty_obj); + + bool success = mp_flipper_pwm_start(pin, frequency, duty); + + return success ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_3(flipperzero_pwm_start_obj, flipperzero_pwm_start); + +static mp_obj_t flipperzero_pwm_stop(mp_obj_t pin_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + + mp_flipper_pwm_stop(pin); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_pwm_stop_obj, flipperzero_pwm_stop); + +static mp_obj_t flipperzero_pwm_is_running(mp_obj_t pin_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + + return mp_flipper_pwm_is_running(pin) ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_pwm_is_running_obj, flipperzero_pwm_is_running); + +static mp_obj_t flipperzero_infrared_receive(size_t n_args, const mp_obj_t* args) { + mp_int_t timeout = n_args > 0 ? mp_obj_get_int(args[0]) : MP_FLIPPER_INFRARED_RX_DEFAULT_TIMEOUT; + + size_t length = 0; + uint32_t* buffer = mp_flipper_infrared_receive(timeout, &length); + mp_obj_t* signal = length > 0 ? malloc(length * sizeof(mp_obj_t)) : NULL; + + for(uint16_t i = 0; i < length; i++) { + signal[i] = mp_obj_new_int(buffer[i]); + } + + return mp_obj_new_list(length, signal); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_infrared_receive_obj, 0, 1, flipperzero_infrared_receive); + +inline static uint32_t flipperzero_infrared_tx_signal_provider(mp_obj_t* signal, const size_t index) { + return mp_obj_get_int(signal[index]); +} + +static mp_obj_t flipperzero_infrared_transmit(size_t n_args, const mp_obj_t* args) { + size_t length = 0; + mp_obj_t* signal; + + mp_obj_get_array(args[0], &length, &signal); + + mp_int_t repeat = n_args > 1 ? mp_obj_get_int(args[1]) : 1; + bool use_external_pin = n_args > 2 ? mp_obj_is_true(args[2]) : false; + mp_int_t frequency = n_args > 3 ? mp_obj_get_int(args[3]) : MP_FLIPPER_INFRARED_TX_DEFAULT_FREQUENCY; + mp_float_t duty_cycle = n_args > 4 ? mp_obj_get_float(args[4]) : MP_FLIPPER_INFRARED_TX_DEFAULT_DUTY_CYCLE; + + return mp_flipper_infrared_transmit( + signal, length, flipperzero_infrared_tx_signal_provider, repeat, frequency, duty_cycle, use_external_pin) ? + mp_const_true : + mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_infrared_transmit_obj, 1, 5, flipperzero_infrared_transmit); + +static mp_obj_t flipperzero_infrared_is_busy() { + return mp_flipper_infrared_is_busy() ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_infrared_is_busy_obj, flipperzero_infrared_is_busy); + +static const mp_rom_map_elem_t flipperzero_module_globals_table[] = { + // light + {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_flipperzero)}, + {MP_ROM_QSTR(MP_QSTR_LIGHT_RED), MP_ROM_INT(MP_FLIPPER_LED_RED)}, + {MP_ROM_QSTR(MP_QSTR_LIGHT_GREEN), MP_ROM_INT(MP_FLIPPER_LED_GREEN)}, + {MP_ROM_QSTR(MP_QSTR_LIGHT_BLUE), MP_ROM_INT(MP_FLIPPER_LED_BLUE)}, + {MP_ROM_QSTR(MP_QSTR_LIGHT_BACKLIGHT), MP_ROM_INT(MP_FLIPPER_LED_BACKLIGHT)}, + {MP_ROM_QSTR(MP_QSTR_light_set), MP_ROM_PTR(&flipperzero_light_set_obj)}, + {MP_ROM_QSTR(MP_QSTR_light_blink_start), MP_ROM_PTR(&flipperzero_light_blink_start_obj)}, + {MP_ROM_QSTR(MP_QSTR_light_blink_set_color), MP_ROM_PTR(&flipperzero_light_blink_set_color_obj)}, + {MP_ROM_QSTR(MP_QSTR_light_blink_stop), MP_ROM_PTR(&flipperzero_light_blink_stop_obj)}, + // vibro + {MP_ROM_QSTR(MP_QSTR_vibro_set), MP_ROM_PTR(&flipperzero_vibro_set_obj)}, + /* +Python script for notes generation + +# coding: utf-8 +# Python script for notes generation + +from typing import List + +note_names: List = ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B'] + +for octave in range(9): + for name in note_names: + print("{MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_%s%s), MP_ROM_PTR(&flipperzero_speaker_note_%s%s_obj)}," % (name,octave,name.lower(),octave)) +*/ + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C0), MP_ROM_PTR(&flipperzero_speaker_note_c0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS0), MP_ROM_PTR(&flipperzero_speaker_note_cs0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D0), MP_ROM_PTR(&flipperzero_speaker_note_d0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS0), MP_ROM_PTR(&flipperzero_speaker_note_ds0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E0), MP_ROM_PTR(&flipperzero_speaker_note_e0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F0), MP_ROM_PTR(&flipperzero_speaker_note_f0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS0), MP_ROM_PTR(&flipperzero_speaker_note_fs0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G0), MP_ROM_PTR(&flipperzero_speaker_note_g0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS0), MP_ROM_PTR(&flipperzero_speaker_note_gs0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A0), MP_ROM_PTR(&flipperzero_speaker_note_a0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS0), MP_ROM_PTR(&flipperzero_speaker_note_as0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B0), MP_ROM_PTR(&flipperzero_speaker_note_b0_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C1), MP_ROM_PTR(&flipperzero_speaker_note_c1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS1), MP_ROM_PTR(&flipperzero_speaker_note_cs1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D1), MP_ROM_PTR(&flipperzero_speaker_note_d1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS1), MP_ROM_PTR(&flipperzero_speaker_note_ds1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E1), MP_ROM_PTR(&flipperzero_speaker_note_e1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F1), MP_ROM_PTR(&flipperzero_speaker_note_f1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS1), MP_ROM_PTR(&flipperzero_speaker_note_fs1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G1), MP_ROM_PTR(&flipperzero_speaker_note_g1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS1), MP_ROM_PTR(&flipperzero_speaker_note_gs1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A1), MP_ROM_PTR(&flipperzero_speaker_note_a1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS1), MP_ROM_PTR(&flipperzero_speaker_note_as1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B1), MP_ROM_PTR(&flipperzero_speaker_note_b1_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C2), MP_ROM_PTR(&flipperzero_speaker_note_c2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS2), MP_ROM_PTR(&flipperzero_speaker_note_cs2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D2), MP_ROM_PTR(&flipperzero_speaker_note_d2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS2), MP_ROM_PTR(&flipperzero_speaker_note_ds2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E2), MP_ROM_PTR(&flipperzero_speaker_note_e2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F2), MP_ROM_PTR(&flipperzero_speaker_note_f2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS2), MP_ROM_PTR(&flipperzero_speaker_note_fs2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G2), MP_ROM_PTR(&flipperzero_speaker_note_g2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS2), MP_ROM_PTR(&flipperzero_speaker_note_gs2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A2), MP_ROM_PTR(&flipperzero_speaker_note_a2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS2), MP_ROM_PTR(&flipperzero_speaker_note_as2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B2), MP_ROM_PTR(&flipperzero_speaker_note_b2_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C3), MP_ROM_PTR(&flipperzero_speaker_note_c3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS3), MP_ROM_PTR(&flipperzero_speaker_note_cs3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D3), MP_ROM_PTR(&flipperzero_speaker_note_d3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS3), MP_ROM_PTR(&flipperzero_speaker_note_ds3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E3), MP_ROM_PTR(&flipperzero_speaker_note_e3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F3), MP_ROM_PTR(&flipperzero_speaker_note_f3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS3), MP_ROM_PTR(&flipperzero_speaker_note_fs3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G3), MP_ROM_PTR(&flipperzero_speaker_note_g3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS3), MP_ROM_PTR(&flipperzero_speaker_note_gs3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A3), MP_ROM_PTR(&flipperzero_speaker_note_a3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS3), MP_ROM_PTR(&flipperzero_speaker_note_as3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B3), MP_ROM_PTR(&flipperzero_speaker_note_b3_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C4), MP_ROM_PTR(&flipperzero_speaker_note_c4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS4), MP_ROM_PTR(&flipperzero_speaker_note_cs4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D4), MP_ROM_PTR(&flipperzero_speaker_note_d4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS4), MP_ROM_PTR(&flipperzero_speaker_note_ds4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E4), MP_ROM_PTR(&flipperzero_speaker_note_e4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F4), MP_ROM_PTR(&flipperzero_speaker_note_f4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS4), MP_ROM_PTR(&flipperzero_speaker_note_fs4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G4), MP_ROM_PTR(&flipperzero_speaker_note_g4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS4), MP_ROM_PTR(&flipperzero_speaker_note_gs4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A4), MP_ROM_PTR(&flipperzero_speaker_note_a4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS4), MP_ROM_PTR(&flipperzero_speaker_note_as4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B4), MP_ROM_PTR(&flipperzero_speaker_note_b4_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C5), MP_ROM_PTR(&flipperzero_speaker_note_c5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS5), MP_ROM_PTR(&flipperzero_speaker_note_cs5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D5), MP_ROM_PTR(&flipperzero_speaker_note_d5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS5), MP_ROM_PTR(&flipperzero_speaker_note_ds5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E5), MP_ROM_PTR(&flipperzero_speaker_note_e5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F5), MP_ROM_PTR(&flipperzero_speaker_note_f5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS5), MP_ROM_PTR(&flipperzero_speaker_note_fs5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G5), MP_ROM_PTR(&flipperzero_speaker_note_g5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS5), MP_ROM_PTR(&flipperzero_speaker_note_gs5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A5), MP_ROM_PTR(&flipperzero_speaker_note_a5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS5), MP_ROM_PTR(&flipperzero_speaker_note_as5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B5), MP_ROM_PTR(&flipperzero_speaker_note_b5_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C6), MP_ROM_PTR(&flipperzero_speaker_note_c6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS6), MP_ROM_PTR(&flipperzero_speaker_note_cs6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D6), MP_ROM_PTR(&flipperzero_speaker_note_d6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS6), MP_ROM_PTR(&flipperzero_speaker_note_ds6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E6), MP_ROM_PTR(&flipperzero_speaker_note_e6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F6), MP_ROM_PTR(&flipperzero_speaker_note_f6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS6), MP_ROM_PTR(&flipperzero_speaker_note_fs6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G6), MP_ROM_PTR(&flipperzero_speaker_note_g6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS6), MP_ROM_PTR(&flipperzero_speaker_note_gs6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A6), MP_ROM_PTR(&flipperzero_speaker_note_a6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS6), MP_ROM_PTR(&flipperzero_speaker_note_as6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B6), MP_ROM_PTR(&flipperzero_speaker_note_b6_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C7), MP_ROM_PTR(&flipperzero_speaker_note_c7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS7), MP_ROM_PTR(&flipperzero_speaker_note_cs7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D7), MP_ROM_PTR(&flipperzero_speaker_note_d7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS7), MP_ROM_PTR(&flipperzero_speaker_note_ds7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E7), MP_ROM_PTR(&flipperzero_speaker_note_e7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F7), MP_ROM_PTR(&flipperzero_speaker_note_f7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS7), MP_ROM_PTR(&flipperzero_speaker_note_fs7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G7), MP_ROM_PTR(&flipperzero_speaker_note_g7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS7), MP_ROM_PTR(&flipperzero_speaker_note_gs7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A7), MP_ROM_PTR(&flipperzero_speaker_note_a7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS7), MP_ROM_PTR(&flipperzero_speaker_note_as7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B7), MP_ROM_PTR(&flipperzero_speaker_note_b7_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_C8), MP_ROM_PTR(&flipperzero_speaker_note_c8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_CS8), MP_ROM_PTR(&flipperzero_speaker_note_cs8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_D8), MP_ROM_PTR(&flipperzero_speaker_note_d8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_DS8), MP_ROM_PTR(&flipperzero_speaker_note_ds8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_E8), MP_ROM_PTR(&flipperzero_speaker_note_e8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_F8), MP_ROM_PTR(&flipperzero_speaker_note_f8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_FS8), MP_ROM_PTR(&flipperzero_speaker_note_fs8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_G8), MP_ROM_PTR(&flipperzero_speaker_note_g8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_GS8), MP_ROM_PTR(&flipperzero_speaker_note_gs8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_A8), MP_ROM_PTR(&flipperzero_speaker_note_a8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_AS8), MP_ROM_PTR(&flipperzero_speaker_note_as8_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_NOTE_B8), MP_ROM_PTR(&flipperzero_speaker_note_b8_obj)}, + + {MP_ROM_QSTR(MP_QSTR_SPEAKER_VOLUME_MIN), MP_ROM_PTR(&flipperzero_speaker_volume_min_obj)}, + {MP_ROM_QSTR(MP_QSTR_SPEAKER_VOLUME_MAX), MP_ROM_PTR(&flipperzero_speaker_volume_max_obj)}, + + {MP_ROM_QSTR(MP_QSTR_speaker_start), MP_ROM_PTR(&flipperzero_speaker_start_obj)}, + {MP_ROM_QSTR(MP_QSTR_speaker_set_volume), MP_ROM_PTR(&flipperzero_speaker_set_volume_obj)}, + {MP_ROM_QSTR(MP_QSTR_speaker_stop), MP_ROM_PTR(&flipperzero_speaker_stop_obj)}, + // canvas + {MP_ROM_QSTR(MP_QSTR_CANVAS_BLACK), MP_ROM_INT(MP_FLIPPER_COLOR_BLACK)}, + {MP_ROM_QSTR(MP_QSTR_CANVAS_WHITE), MP_ROM_INT(MP_FLIPPER_COLOR_WHITE)}, + {MP_ROM_QSTR(MP_QSTR_canvas_width), MP_ROM_PTR(&flipperzero_canvas_width_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_height), MP_ROM_PTR(&flipperzero_canvas_height_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_text_width), MP_ROM_PTR(&flipperzero_canvas_text_width_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_text_height), MP_ROM_PTR(&flipperzero_canvas_text_height_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_draw_dot), MP_ROM_PTR(&flipperzero_canvas_draw_dot_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_draw_box), MP_ROM_PTR(&flipperzero_canvas_draw_box_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_draw_frame), MP_ROM_PTR(&flipperzero_canvas_draw_frame_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_draw_line), MP_ROM_PTR(&flipperzero_canvas_draw_line_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_draw_circle), MP_ROM_PTR(&flipperzero_canvas_draw_circle_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_draw_disc), MP_ROM_PTR(&flipperzero_canvas_draw_disc_obj)}, + {MP_ROM_QSTR(MP_QSTR_FONT_PRIMARY), MP_ROM_INT(MP_FLIPPER_FONT_PRIMARY)}, + {MP_ROM_QSTR(MP_QSTR_FONT_SECONDARY), MP_ROM_INT(MP_FLIPPER_FONT_SECONDARY)}, + {MP_ROM_QSTR(MP_QSTR_canvas_set_font), MP_ROM_PTR(&flipperzero_canvas_set_font_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_set_color), MP_ROM_PTR(&flipperzero_canvas_set_color_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_set_text), MP_ROM_PTR(&flipperzero_canvas_set_text_obj)}, + {MP_ROM_QSTR(MP_QSTR_ALIGN_BEGIN), MP_ROM_INT(MP_FLIPPER_ALIGN_BEGIN)}, + {MP_ROM_QSTR(MP_QSTR_ALIGN_CENTER), MP_ROM_INT(MP_FLIPPER_ALIGN_CENTER)}, + {MP_ROM_QSTR(MP_QSTR_ALIGN_END), MP_ROM_INT(MP_FLIPPER_ALIGN_END)}, + {MP_ROM_QSTR(MP_QSTR_canvas_set_text_align), MP_ROM_PTR(&flipperzero_canvas_set_text_align_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_update), MP_ROM_PTR(&flipperzero_canvas_update_obj)}, + {MP_ROM_QSTR(MP_QSTR_canvas_clear), MP_ROM_PTR(&flipperzero_canvas_clear_obj)}, + // input + {MP_ROM_QSTR(MP_QSTR_on_input), MP_ROM_PTR(&flipperzero_on_input_obj)}, + {MP_ROM_QSTR(MP_QSTR__input_trigger_handler), MP_ROM_PTR(&flipperzero_input_trigger_handler_obj)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_BUTTON_BACK), MP_ROM_INT(MP_FLIPPER_INPUT_BUTTON_BACK)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_BUTTON_OK), MP_ROM_INT(MP_FLIPPER_INPUT_BUTTON_OK)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_BUTTON_LEFT), MP_ROM_INT(MP_FLIPPER_INPUT_BUTTON_LEFT)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_BUTTON_RIGHT), MP_ROM_INT(MP_FLIPPER_INPUT_BUTTON_RIGHT)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_BUTTON_UP), MP_ROM_INT(MP_FLIPPER_INPUT_BUTTON_UP)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_BUTTON_DOWN), MP_ROM_INT(MP_FLIPPER_INPUT_BUTTON_DOWN)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_TYPE_PRESS), MP_ROM_INT(MP_FLIPPER_INPUT_TYPE_PRESS)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_TYPE_RELEASE), MP_ROM_INT(MP_FLIPPER_INPUT_TYPE_RELEASE)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_TYPE_SHORT), MP_ROM_INT(MP_FLIPPER_INPUT_TYPE_SHORT)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_TYPE_LONG), MP_ROM_INT(MP_FLIPPER_INPUT_TYPE_LONG)}, + {MP_ROM_QSTR(MP_QSTR_INPUT_TYPE_REPEAT), MP_ROM_INT(MP_FLIPPER_INPUT_TYPE_REPEAT)}, + // dialog + {MP_ROM_QSTR(MP_QSTR_dialog_message_set_text), MP_ROM_PTR(&flipperzero_dialog_message_set_text_obj)}, + {MP_ROM_QSTR(MP_QSTR_dialog_message_set_header), MP_ROM_PTR(&flipperzero_dialog_message_set_header_obj)}, + {MP_ROM_QSTR(MP_QSTR_dialog_message_set_button), MP_ROM_PTR(&flipperzero_dialog_message_set_button_obj)}, + {MP_ROM_QSTR(MP_QSTR_dialog_message_show), MP_ROM_PTR(&flipperzero_dialog_message_show_obj)}, + {MP_ROM_QSTR(MP_QSTR_dialog_message_clear), MP_ROM_PTR(&flipperzero_dialog_message_clear_obj)}, + // gpio - pins + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PC0), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PC0)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PC1), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PC1)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PC3), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PC3)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PB2), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PB2)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PB3), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PB3)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PA4), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PA4)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PA6), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PA6)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PIN_PA7), MP_ROM_INT(MP_FLIPPER_GPIO_PIN_PA7)}, + // gpio - modes + {MP_ROM_QSTR(MP_QSTR_GPIO_MODE_INPUT), MP_ROM_INT(MP_FLIPPER_GPIO_MODE_INPUT)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_MODE_OUTPUT_PUSH_PULL), MP_ROM_INT(MP_FLIPPER_GPIO_MODE_OUTPUT_PUSH_PULL)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_MODE_OUTPUT_OPEN_DRAIN), MP_ROM_INT(MP_FLIPPER_GPIO_MODE_OUTPUT_OPEN_DRAIN)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_MODE_ANALOG), MP_ROM_INT(MP_FLIPPER_GPIO_MODE_ANALOG)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_MODE_INTERRUPT_RISE), MP_ROM_INT(MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_MODE_INTERRUPT_FALL), MP_ROM_INT(MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL)}, + // gpio - pull + {MP_ROM_QSTR(MP_QSTR_GPIO_PULL_NO), MP_ROM_INT(MP_FLIPPER_GPIO_PULL_NO)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PULL_UP), MP_ROM_INT(MP_FLIPPER_GPIO_PULL_UP)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_PULL_DOWN), MP_ROM_INT(MP_FLIPPER_GPIO_PULL_DOWN)}, + // gpio - speed + {MP_ROM_QSTR(MP_QSTR_GPIO_SPEED_LOW), MP_ROM_INT(MP_FLIPPER_GPIO_SPEED_LOW)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_SPEED_MEDIUM), MP_ROM_INT(MP_FLIPPER_GPIO_SPEED_MEDIUM)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_SPEED_HIGH), MP_ROM_INT(MP_FLIPPER_GPIO_SPEED_HIGH)}, + {MP_ROM_QSTR(MP_QSTR_GPIO_SPEED_VERY_HIGH), MP_ROM_INT(MP_FLIPPER_GPIO_SPEED_VERY_HIGH)}, + // gpio - functions + {MP_ROM_QSTR(MP_QSTR_gpio_init_pin), MP_ROM_PTR(&flipperzero_gpio_init_pin_obj)}, + {MP_ROM_QSTR(MP_QSTR_gpio_deinit_pin), MP_ROM_PTR(&flipperzero_gpio_deinit_pin_obj)}, + {MP_ROM_QSTR(MP_QSTR_gpio_set_pin), MP_ROM_PTR(&flipperzero_gpio_set_pin_obj)}, + {MP_ROM_QSTR(MP_QSTR_gpio_get_pin), MP_ROM_PTR(&flipperzero_gpio_get_pin_obj)}, + {MP_ROM_QSTR(MP_QSTR_on_gpio), MP_ROM_PTR(&flipperzero_on_gpio_obj)}, + {MP_ROM_QSTR(MP_QSTR__gpio_trigger_handler), MP_ROM_PTR(&flipperzero_gpio_trigger_handler_obj)}, + // adc - functions + {MP_ROM_QSTR(MP_QSTR_adc_read_pin_value), MP_ROM_PTR(&flipperzero_adc_read_pin_value_obj)}, + {MP_ROM_QSTR(MP_QSTR_adc_read_pin_voltage), MP_ROM_PTR(&flipperzero_adc_read_pin_voltage_obj)}, + // pwm - functions + {MP_ROM_QSTR(MP_QSTR_pwm_start), MP_ROM_PTR(&flipperzero_pwm_start_obj)}, + {MP_ROM_QSTR(MP_QSTR_pwm_stop), MP_ROM_PTR(&flipperzero_pwm_stop_obj)}, + {MP_ROM_QSTR(MP_QSTR_pwm_is_running), MP_ROM_PTR(&flipperzero_pwm_is_running_obj)}, + // infrared - functions + {MP_ROM_QSTR(MP_QSTR_infrared_receive), MP_ROM_PTR(&flipperzero_infrared_receive_obj)}, + {MP_ROM_QSTR(MP_QSTR_infrared_transmit), MP_ROM_PTR(&flipperzero_infrared_transmit_obj)}, + {MP_ROM_QSTR(MP_QSTR_infrared_is_busy), MP_ROM_PTR(&flipperzero_infrared_is_busy_obj)}, +}; +static MP_DEFINE_CONST_DICT(flipperzero_module_globals, flipperzero_module_globals_table); + +const mp_obj_module_t flipperzero_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t*)&flipperzero_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_flipperzero, flipperzero_module); + +void mp_flipper_on_input(uint16_t button, uint16_t type) { + if(mp_flipper_on_input_callback != NULL) { + uint16_t flags = button | type; + mp_obj_t flags_obj = mp_obj_new_int_from_uint(flags); + + mp_sched_schedule(&flipperzero_input_trigger_handler_obj, flags_obj); + } +} + +void mp_flipper_on_gpio(void* ctx) { + if(mp_flipper_on_gpio_callback != NULL) { + mp_obj_t pin_obj = mp_obj_new_int_from_uint((uint8_t)ctx); + + mp_sched_schedule(&flipperzero_gpio_trigger_handler_obj, pin_obj); + } +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modflipperzero.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modflipperzero.h new file mode 100644 index 00000000..d3cef0a1 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modflipperzero.h @@ -0,0 +1,260 @@ +#pragma once + +#include +#include +#include +#include + +#define MP_FLIPPER_LED_RED (1 << 0) +#define MP_FLIPPER_LED_GREEN (1 << 1) +#define MP_FLIPPER_LED_BLUE (1 << 2) +#define MP_FLIPPER_LED_BACKLIGHT (1 << 3) + +#define MP_FLIPPER_COLOR_BLACK (1 << 0) +#define MP_FLIPPER_COLOR_WHITE (1 << 1) + +#define MP_FLIPPER_INPUT_BUTTON_UP (1 << 0) +#define MP_FLIPPER_INPUT_BUTTON_DOWN (1 << 1) +#define MP_FLIPPER_INPUT_BUTTON_RIGHT (1 << 2) +#define MP_FLIPPER_INPUT_BUTTON_LEFT (1 << 3) +#define MP_FLIPPER_INPUT_BUTTON_OK (1 << 4) +#define MP_FLIPPER_INPUT_BUTTON_BACK (1 << 5) +#define MP_FLIPPER_INPUT_BUTTON ((1 << 6) - 1) + +#define MP_FLIPPER_INPUT_TYPE_PRESS (1 << 6) +#define MP_FLIPPER_INPUT_TYPE_RELEASE (1 << 7) +#define MP_FLIPPER_INPUT_TYPE_SHORT (1 << 8) +#define MP_FLIPPER_INPUT_TYPE_LONG (1 << 9) +#define MP_FLIPPER_INPUT_TYPE_REPEAT (1 << 10) +#define MP_FLIPPER_INPUT_TYPE ((1 << 11) - 1 - MP_FLIPPER_INPUT_BUTTON) + +#define MP_FLIPPER_ALIGN_BEGIN (1 << 0) +#define MP_FLIPPER_ALIGN_CENTER (1 << 1) +#define MP_FLIPPER_ALIGN_END (1 << 2) + +#define MP_FLIPPER_FONT_PRIMARY (1 << 0) +#define MP_FLIPPER_FONT_SECONDARY (1 << 1) + +void mp_flipper_light_set(uint8_t raw_light, uint8_t brightness); +void mp_flipper_light_blink_start(uint8_t raw_light, uint8_t brightness, uint16_t on_time, uint16_t period); +void mp_flipper_light_blink_set_color(uint8_t raw_light); +void mp_flipper_light_blink_stop(); + +void mp_flipper_vibro(bool state); + +/* +Python script for notes generation + +# coding: utf-8 +# Python script for notes generation + +from typing import List + +note_names: List = ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B'] +base_note: float = 16.3515979 +cf: float = 2 ** (1.0 / 12) + +note: float = base_note +for octave in range(9): + for name in note_names: + print(f"#define MP_FLIPPER_SPEAKER_NOTE_{name}{octave} MICROPY_FLOAT_CONST({round(note, 2)})") + note = note * cf +*/ + +#define MP_FLIPPER_SPEAKER_NOTE_C0 MICROPY_FLOAT_CONST(16.35) +#define MP_FLIPPER_SPEAKER_NOTE_CS0 MICROPY_FLOAT_CONST(17.32) +#define MP_FLIPPER_SPEAKER_NOTE_D0 MICROPY_FLOAT_CONST(18.35) +#define MP_FLIPPER_SPEAKER_NOTE_DS0 MICROPY_FLOAT_CONST(19.45) +#define MP_FLIPPER_SPEAKER_NOTE_E0 MICROPY_FLOAT_CONST(20.6) +#define MP_FLIPPER_SPEAKER_NOTE_F0 MICROPY_FLOAT_CONST(21.83) +#define MP_FLIPPER_SPEAKER_NOTE_FS0 MICROPY_FLOAT_CONST(23.12) +#define MP_FLIPPER_SPEAKER_NOTE_G0 MICROPY_FLOAT_CONST(24.5) +#define MP_FLIPPER_SPEAKER_NOTE_GS0 MICROPY_FLOAT_CONST(25.96) +#define MP_FLIPPER_SPEAKER_NOTE_A0 MICROPY_FLOAT_CONST(27.5) +#define MP_FLIPPER_SPEAKER_NOTE_AS0 MICROPY_FLOAT_CONST(29.14) +#define MP_FLIPPER_SPEAKER_NOTE_B0 MICROPY_FLOAT_CONST(30.87) +#define MP_FLIPPER_SPEAKER_NOTE_C1 MICROPY_FLOAT_CONST(32.7) +#define MP_FLIPPER_SPEAKER_NOTE_CS1 MICROPY_FLOAT_CONST(34.65) +#define MP_FLIPPER_SPEAKER_NOTE_D1 MICROPY_FLOAT_CONST(36.71) +#define MP_FLIPPER_SPEAKER_NOTE_DS1 MICROPY_FLOAT_CONST(38.89) +#define MP_FLIPPER_SPEAKER_NOTE_E1 MICROPY_FLOAT_CONST(41.2) +#define MP_FLIPPER_SPEAKER_NOTE_F1 MICROPY_FLOAT_CONST(43.65) +#define MP_FLIPPER_SPEAKER_NOTE_FS1 MICROPY_FLOAT_CONST(46.25) +#define MP_FLIPPER_SPEAKER_NOTE_G1 MICROPY_FLOAT_CONST(49.0) +#define MP_FLIPPER_SPEAKER_NOTE_GS1 MICROPY_FLOAT_CONST(51.91) +#define MP_FLIPPER_SPEAKER_NOTE_A1 MICROPY_FLOAT_CONST(55.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS1 MICROPY_FLOAT_CONST(58.27) +#define MP_FLIPPER_SPEAKER_NOTE_B1 MICROPY_FLOAT_CONST(61.74) +#define MP_FLIPPER_SPEAKER_NOTE_C2 MICROPY_FLOAT_CONST(65.41) +#define MP_FLIPPER_SPEAKER_NOTE_CS2 MICROPY_FLOAT_CONST(69.3) +#define MP_FLIPPER_SPEAKER_NOTE_D2 MICROPY_FLOAT_CONST(73.42) +#define MP_FLIPPER_SPEAKER_NOTE_DS2 MICROPY_FLOAT_CONST(77.78) +#define MP_FLIPPER_SPEAKER_NOTE_E2 MICROPY_FLOAT_CONST(82.41) +#define MP_FLIPPER_SPEAKER_NOTE_F2 MICROPY_FLOAT_CONST(87.31) +#define MP_FLIPPER_SPEAKER_NOTE_FS2 MICROPY_FLOAT_CONST(92.5) +#define MP_FLIPPER_SPEAKER_NOTE_G2 MICROPY_FLOAT_CONST(98.0) +#define MP_FLIPPER_SPEAKER_NOTE_GS2 MICROPY_FLOAT_CONST(103.83) +#define MP_FLIPPER_SPEAKER_NOTE_A2 MICROPY_FLOAT_CONST(110.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS2 MICROPY_FLOAT_CONST(116.54) +#define MP_FLIPPER_SPEAKER_NOTE_B2 MICROPY_FLOAT_CONST(123.47) +#define MP_FLIPPER_SPEAKER_NOTE_C3 MICROPY_FLOAT_CONST(130.81) +#define MP_FLIPPER_SPEAKER_NOTE_CS3 MICROPY_FLOAT_CONST(138.59) +#define MP_FLIPPER_SPEAKER_NOTE_D3 MICROPY_FLOAT_CONST(146.83) +#define MP_FLIPPER_SPEAKER_NOTE_DS3 MICROPY_FLOAT_CONST(155.56) +#define MP_FLIPPER_SPEAKER_NOTE_E3 MICROPY_FLOAT_CONST(164.81) +#define MP_FLIPPER_SPEAKER_NOTE_F3 MICROPY_FLOAT_CONST(174.61) +#define MP_FLIPPER_SPEAKER_NOTE_FS3 MICROPY_FLOAT_CONST(185.0) +#define MP_FLIPPER_SPEAKER_NOTE_G3 MICROPY_FLOAT_CONST(196.0) +#define MP_FLIPPER_SPEAKER_NOTE_GS3 MICROPY_FLOAT_CONST(207.65) +#define MP_FLIPPER_SPEAKER_NOTE_A3 MICROPY_FLOAT_CONST(220.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS3 MICROPY_FLOAT_CONST(233.08) +#define MP_FLIPPER_SPEAKER_NOTE_B3 MICROPY_FLOAT_CONST(246.94) +#define MP_FLIPPER_SPEAKER_NOTE_C4 MICROPY_FLOAT_CONST(261.63) +#define MP_FLIPPER_SPEAKER_NOTE_CS4 MICROPY_FLOAT_CONST(277.18) +#define MP_FLIPPER_SPEAKER_NOTE_D4 MICROPY_FLOAT_CONST(293.66) +#define MP_FLIPPER_SPEAKER_NOTE_DS4 MICROPY_FLOAT_CONST(311.13) +#define MP_FLIPPER_SPEAKER_NOTE_E4 MICROPY_FLOAT_CONST(329.63) +#define MP_FLIPPER_SPEAKER_NOTE_F4 MICROPY_FLOAT_CONST(349.23) +#define MP_FLIPPER_SPEAKER_NOTE_FS4 MICROPY_FLOAT_CONST(369.99) +#define MP_FLIPPER_SPEAKER_NOTE_G4 MICROPY_FLOAT_CONST(392.0) +#define MP_FLIPPER_SPEAKER_NOTE_GS4 MICROPY_FLOAT_CONST(415.3) +#define MP_FLIPPER_SPEAKER_NOTE_A4 MICROPY_FLOAT_CONST(440.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS4 MICROPY_FLOAT_CONST(466.16) +#define MP_FLIPPER_SPEAKER_NOTE_B4 MICROPY_FLOAT_CONST(493.88) +#define MP_FLIPPER_SPEAKER_NOTE_C5 MICROPY_FLOAT_CONST(523.25) +#define MP_FLIPPER_SPEAKER_NOTE_CS5 MICROPY_FLOAT_CONST(554.37) +#define MP_FLIPPER_SPEAKER_NOTE_D5 MICROPY_FLOAT_CONST(587.33) +#define MP_FLIPPER_SPEAKER_NOTE_DS5 MICROPY_FLOAT_CONST(622.25) +#define MP_FLIPPER_SPEAKER_NOTE_E5 MICROPY_FLOAT_CONST(659.26) +#define MP_FLIPPER_SPEAKER_NOTE_F5 MICROPY_FLOAT_CONST(698.46) +#define MP_FLIPPER_SPEAKER_NOTE_FS5 MICROPY_FLOAT_CONST(739.99) +#define MP_FLIPPER_SPEAKER_NOTE_G5 MICROPY_FLOAT_CONST(783.99) +#define MP_FLIPPER_SPEAKER_NOTE_GS5 MICROPY_FLOAT_CONST(830.61) +#define MP_FLIPPER_SPEAKER_NOTE_A5 MICROPY_FLOAT_CONST(880.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS5 MICROPY_FLOAT_CONST(932.33) +#define MP_FLIPPER_SPEAKER_NOTE_B5 MICROPY_FLOAT_CONST(987.77) +#define MP_FLIPPER_SPEAKER_NOTE_C6 MICROPY_FLOAT_CONST(1046.5) +#define MP_FLIPPER_SPEAKER_NOTE_CS6 MICROPY_FLOAT_CONST(1108.73) +#define MP_FLIPPER_SPEAKER_NOTE_D6 MICROPY_FLOAT_CONST(1174.66) +#define MP_FLIPPER_SPEAKER_NOTE_DS6 MICROPY_FLOAT_CONST(1244.51) +#define MP_FLIPPER_SPEAKER_NOTE_E6 MICROPY_FLOAT_CONST(1318.51) +#define MP_FLIPPER_SPEAKER_NOTE_F6 MICROPY_FLOAT_CONST(1396.91) +#define MP_FLIPPER_SPEAKER_NOTE_FS6 MICROPY_FLOAT_CONST(1479.98) +#define MP_FLIPPER_SPEAKER_NOTE_G6 MICROPY_FLOAT_CONST(1567.98) +#define MP_FLIPPER_SPEAKER_NOTE_GS6 MICROPY_FLOAT_CONST(1661.22) +#define MP_FLIPPER_SPEAKER_NOTE_A6 MICROPY_FLOAT_CONST(1760.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS6 MICROPY_FLOAT_CONST(1864.66) +#define MP_FLIPPER_SPEAKER_NOTE_B6 MICROPY_FLOAT_CONST(1975.53) +#define MP_FLIPPER_SPEAKER_NOTE_C7 MICROPY_FLOAT_CONST(2093.0) +#define MP_FLIPPER_SPEAKER_NOTE_CS7 MICROPY_FLOAT_CONST(2217.46) +#define MP_FLIPPER_SPEAKER_NOTE_D7 MICROPY_FLOAT_CONST(2349.32) +#define MP_FLIPPER_SPEAKER_NOTE_DS7 MICROPY_FLOAT_CONST(2489.02) +#define MP_FLIPPER_SPEAKER_NOTE_E7 MICROPY_FLOAT_CONST(2637.02) +#define MP_FLIPPER_SPEAKER_NOTE_F7 MICROPY_FLOAT_CONST(2793.83) +#define MP_FLIPPER_SPEAKER_NOTE_FS7 MICROPY_FLOAT_CONST(2959.96) +#define MP_FLIPPER_SPEAKER_NOTE_G7 MICROPY_FLOAT_CONST(3135.96) +#define MP_FLIPPER_SPEAKER_NOTE_GS7 MICROPY_FLOAT_CONST(3322.44) +#define MP_FLIPPER_SPEAKER_NOTE_A7 MICROPY_FLOAT_CONST(3520.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS7 MICROPY_FLOAT_CONST(3729.31) +#define MP_FLIPPER_SPEAKER_NOTE_B7 MICROPY_FLOAT_CONST(3951.07) +#define MP_FLIPPER_SPEAKER_NOTE_C8 MICROPY_FLOAT_CONST(4186.01) +#define MP_FLIPPER_SPEAKER_NOTE_CS8 MICROPY_FLOAT_CONST(4434.92) +#define MP_FLIPPER_SPEAKER_NOTE_D8 MICROPY_FLOAT_CONST(4698.64) +#define MP_FLIPPER_SPEAKER_NOTE_DS8 MICROPY_FLOAT_CONST(4978.03) +#define MP_FLIPPER_SPEAKER_NOTE_E8 MICROPY_FLOAT_CONST(5274.04) +#define MP_FLIPPER_SPEAKER_NOTE_F8 MICROPY_FLOAT_CONST(5587.65) +#define MP_FLIPPER_SPEAKER_NOTE_FS8 MICROPY_FLOAT_CONST(5919.91) +#define MP_FLIPPER_SPEAKER_NOTE_G8 MICROPY_FLOAT_CONST(6271.93) +#define MP_FLIPPER_SPEAKER_NOTE_GS8 MICROPY_FLOAT_CONST(6644.88) +#define MP_FLIPPER_SPEAKER_NOTE_A8 MICROPY_FLOAT_CONST(7040.0) +#define MP_FLIPPER_SPEAKER_NOTE_AS8 MICROPY_FLOAT_CONST(7458.62) +#define MP_FLIPPER_SPEAKER_NOTE_B8 MICROPY_FLOAT_CONST(7902.13) + +#define MP_FLIPPER_SPEAKER_VOLUME_MIN MICROPY_FLOAT_CONST(0.0) +#define MP_FLIPPER_SPEAKER_VOLUME_MAX MICROPY_FLOAT_CONST(1.0) + +bool mp_flipper_speaker_start(float frequency, float volume); +bool mp_flipper_speaker_set_volume(float volume); +bool mp_flipper_speaker_stop(); + +uint8_t mp_flipper_canvas_width(); +uint8_t mp_flipper_canvas_height(); +uint8_t mp_flipper_canvas_text_width(const char* text); +uint8_t mp_flipper_canvas_text_height(); +void mp_flipper_canvas_draw_dot(uint8_t x, uint8_t y); +void mp_flipper_canvas_draw_box(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r); +void mp_flipper_canvas_draw_frame(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r); +void mp_flipper_canvas_draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1); +void mp_flipper_canvas_draw_circle(uint8_t x, uint8_t y, uint8_t r); +void mp_flipper_canvas_draw_disc(uint8_t x, uint8_t y, uint8_t r); +void mp_flipper_canvas_set_font(uint8_t font); +void mp_flipper_canvas_set_color(uint8_t color); +void mp_flipper_canvas_set_text(uint8_t x, uint8_t y, const char* text); +void mp_flipper_canvas_set_text_align(uint8_t x, uint8_t y); +void mp_flipper_canvas_update(); +void mp_flipper_canvas_clear(); + +void mp_flipper_on_input(uint16_t button, uint16_t type); + +void mp_flipper_dialog_message_set_text(const char* text, uint8_t x, uint8_t y, uint8_t h, uint8_t v); +void mp_flipper_dialog_message_set_header(const char* text, uint8_t x, uint8_t y, uint8_t h, uint8_t v); +void mp_flipper_dialog_message_set_button(const char* text, uint8_t button); +uint8_t mp_flipper_dialog_message_show(); +void mp_flipper_dialog_message_clear(); + +#define MP_FLIPPER_GPIO_PIN_PC0 (0) +#define MP_FLIPPER_GPIO_PIN_PC1 (1) +#define MP_FLIPPER_GPIO_PIN_PC3 (2) +#define MP_FLIPPER_GPIO_PIN_PB2 (3) +#define MP_FLIPPER_GPIO_PIN_PB3 (4) +#define MP_FLIPPER_GPIO_PIN_PA4 (5) +#define MP_FLIPPER_GPIO_PIN_PA6 (6) +#define MP_FLIPPER_GPIO_PIN_PA7 (7) + +#define MP_FLIPPER_GPIO_PINS (8) + +#define MP_FLIPPER_GPIO_MODE_INPUT (1 << 0) +#define MP_FLIPPER_GPIO_MODE_OUTPUT_PUSH_PULL (1 << 1) +#define MP_FLIPPER_GPIO_MODE_OUTPUT_OPEN_DRAIN (1 << 2) +#define MP_FLIPPER_GPIO_MODE_ANALOG (1 << 3) +#define MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE (1 << 4) +#define MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL (1 << 5) + +#define MP_FLIPPER_GPIO_PULL_NO (0) +#define MP_FLIPPER_GPIO_PULL_UP (1) +#define MP_FLIPPER_GPIO_PULL_DOWN (2) + +#define MP_FLIPPER_GPIO_SPEED_LOW (0) +#define MP_FLIPPER_GPIO_SPEED_MEDIUM (1) +#define MP_FLIPPER_GPIO_SPEED_HIGH (2) +#define MP_FLIPPER_GPIO_SPEED_VERY_HIGH (3) + +bool mp_flipper_gpio_init_pin(uint8_t raw_pin, uint8_t raw_mode, uint8_t raw_pull, uint8_t raw_speed); +void mp_flipper_gpio_deinit_pin(uint8_t raw_pin); +void mp_flipper_gpio_set_pin(uint8_t raw_pin, bool state); +bool mp_flipper_gpio_get_pin(uint8_t raw_pin); +void mp_flipper_on_gpio(void* ctx); + +uint16_t mp_flipper_adc_read_pin(uint8_t raw_pin); +float mp_flipper_adc_convert_to_voltage(uint16_t value); + +bool mp_flipper_pwm_start(uint8_t raw_pin, uint32_t frequency, uint8_t duty); +void mp_flipper_pwm_stop(uint8_t raw_pin); +bool mp_flipper_pwm_is_running(uint8_t raw_pin); + +#define MP_FLIPPER_INFRARED_RX_DEFAULT_TIMEOUT (1000000) +#define MP_FLIPPER_INFRARED_TX_DEFAULT_FREQUENCY (38000) +#define MP_FLIPPER_INFRARED_TX_DEFAULT_DUTY_CYCLE (0.33) + +typedef uint32_t (*mp_flipper_infrared_signal_tx_provider)(void* signal, const size_t index); + +uint32_t* mp_flipper_infrared_receive(uint32_t timeout, size_t* length); +bool mp_flipper_infrared_transmit( + void* signal, + size_t length, + mp_flipper_infrared_signal_tx_provider callback, + uint32_t repeat, + uint32_t frequency, + float duty, + bool use_external_pin); +bool mp_flipper_infrared_is_busy(); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modrandom.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modrandom.h new file mode 100644 index 00000000..f11359e4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modrandom.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +uint32_t mp_flipper_seed_init(); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modtime.c b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modtime.c new file mode 100644 index 00000000..4186838f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modtime.c @@ -0,0 +1,34 @@ +#include "py/mphal.h" +#include "py/obj.h" + +#include "mp_flipper_modtime.h" + +mp_obj_t mp_time_time_get(void) { + uint32_t timestamp = mp_flipper_get_timestamp(); + + return mp_obj_new_int_from_uint(timestamp); +} + +uint64_t mp_hal_time_ns(void) { + return mp_flipper_get_timestamp() * 1000; +} + +mp_uint_t mp_hal_ticks_ms(void) { + return mp_flipper_get_tick_frequency() / 1000; +} + +mp_uint_t mp_hal_ticks_us(void) { + return mp_flipper_get_tick_frequency() / 1000000; +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return mp_flipper_get_tick(); +} + +void mp_hal_delay_ms(mp_uint_t ms) { + mp_flipper_delay_ms(ms); +} + +void mp_hal_delay_us(mp_uint_t us) { + mp_flipper_delay_us(us); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modtime.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modtime.h new file mode 100644 index 00000000..45a69fa9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_modtime.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +uint32_t mp_flipper_get_timestamp(); + +uint32_t mp_flipper_get_tick_frequency(); + +uint32_t mp_flipper_get_tick(); + +void mp_flipper_delay_ms(uint32_t ms); + +void mp_flipper_delay_us(uint32_t us); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_runtime.c b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_runtime.c new file mode 100644 index 00000000..3f88db4c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_runtime.c @@ -0,0 +1,101 @@ +#include + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/persistentcode.h" +#include "py/gc.h" +#include "py/stackctrl.h" +#include "shared/runtime/gchelper.h" + +#include "mp_flipper_runtime.h" +#include "mp_flipper_halport.h" + +const char* mp_flipper_root_module_path; + +void* mp_flipper_context; + +void mp_flipper_set_root_module_path(const char* path) { + mp_flipper_root_module_path = path; +} + +void mp_flipper_init(void* heap, size_t heap_size, size_t stack_size, void* stack_top) { + mp_flipper_context = mp_flipper_context_alloc(); + + mp_stack_set_top(stack_top); + mp_stack_set_limit(stack_size); + + gc_init(heap, (uint8_t*)heap + heap_size); + + mp_init(); +} + +void mp_flipper_exec_mpy_file(const char* file_path) { +#ifdef MP_FLIPPER_MPY_SUPPORT +#if MP_FLIPPER_IS_RUNTIME + nlr_buf_t nlr; + + if(nlr_push(&nlr) == 0) { + do { + // check if file exists + if(mp_flipper_import_stat(file_path) == MP_FLIPPER_IMPORT_STAT_NO_EXIST) { + mp_raise_OSError_with_filename(MP_ENOENT, file_path); + + break; + } + + // Execute the given .mpy file + mp_module_context_t* context = m_new_obj(mp_module_context_t); + context->module.globals = mp_globals_get(); + mp_compiled_module_t compiled_module; + compiled_module.context = context; + mp_raw_code_load_file(qstr_from_str(file_path), &compiled_module); + mp_obj_t f = mp_make_function_from_proto_fun(compiled_module.rc, context, MP_OBJ_NULL); + mp_call_function_0(f); + } while(false); + nlr_pop(); + } else { + // Uncaught exception: print it out. + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +#endif +#endif +} + +void mp_flipper_deinit() { + mp_deinit(); + + mp_flipper_context_free(mp_flipper_context); +} + +// Called if an exception is raised outside all C exception-catching handlers. +void nlr_jump_fail(void* val) { + mp_flipper_nlr_jump_fail(val); +} + +#if MICROPY_ENABLE_GC +// Run a garbage collection cycle. +void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); +} +#endif + +#ifndef NDEBUG +// Used when debugging is enabled. +void __assert_func(const char* file, int line, const char* func, const char* expr) { + mp_flipper_assert(file, line, func, expr); +} + +void NORETURN __fatal_error(const char* msg) { + mp_flipper_fatal_error(msg); +} +#endif + +void mp_flipper_raise_os_error(int error) { + mp_raise_OSError(error); +} + +void mp_flipper_raise_os_error_with_filename(int error, const char* filename) { + mp_raise_OSError_with_filename(error, filename); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_runtime.h b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_runtime.h new file mode 100644 index 00000000..efa28dca --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mp_flipper_runtime.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "py/mperrno.h" + +#include "mpconfigport.h" + +extern const char* mp_flipper_root_module_path; + +extern void* mp_flipper_context; + +void mp_flipper_set_root_module_path(const char* path); + +void mp_flipper_init(void* memory, size_t memory_size, size_t stack_size, void* stack_top); +void mp_flipper_exec_mpy_file(const char* file_path); +void mp_flipper_save_file(const char* file_path, const char* data, size_t size); +void mp_flipper_deinit(); +void mp_flipper_nlr_jump_fail(void* value); +void mp_flipper_assert(const char* file, int line, const char* func, const char* expr); +void mp_flipper_fatal_error(const char* msg); +void mp_flipper_raise_os_error(int error); +void mp_flipper_raise_os_error_with_filename(int error, const char* filename); +const char* mp_flipper_print_get_data(void* data); +size_t mp_flipper_print_get_data_length(void* data); +void* mp_flipper_print_data_alloc(); +void mp_flipper_print_strn(void* data, const char* str, size_t length); +void mp_flipper_print_data_free(void* data); +void* mp_flipper_context_alloc(); +void mp_flipper_context_free(void* context); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/mpconfigport.h b/non_catalog_apps/mp_flipper/lib/micropython/mpconfigport.h new file mode 100644 index 00000000..1f87805b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/mpconfigport.h @@ -0,0 +1,7 @@ +#pragma once + +#define MP_FLIPPER_COMPILER +#define MP_FLIPPER_RUNTIME +#define MP_FLIPPER_SPLIT_HEAP + +#include "mp_flipper_config.h" diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/argcheck.c b/non_catalog_apps/mp_flipper/lib/micropython/py/argcheck.c new file mode 100644 index 00000000..35b116ec --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/argcheck.c @@ -0,0 +1,148 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig) { + // TODO maybe take the function name as an argument so we can print nicer error messages + + // The reverse of MP_OBJ_FUN_MAKE_SIG + bool takes_kw = sig & 1; + size_t n_args_min = sig >> 17; + size_t n_args_max = (sig >> 1) & 0xffff; + + if (n_kw && !takes_kw) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + mp_raise_TypeError(MP_ERROR_TEXT("function doesn't take keyword arguments")); + #endif + } + + if (n_args_min == n_args_max) { + if (n_args != n_args_min) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), + n_args_min, n_args); + #endif + } + } else { + if (n_args < n_args_min) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function missing %d required positional arguments"), + n_args_min - n_args); + #endif + } else if (n_args > n_args_max) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function expected at most %d arguments, got %d"), + n_args_max, n_args); + #endif + } + } +} + +void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals) { + size_t pos_found = 0, kws_found = 0; + for (size_t i = 0; i < n_allowed; i++) { + mp_obj_t given_arg; + if (i < n_pos) { + if (allowed[i].flags & MP_ARG_KW_ONLY) { + goto extra_positional; + } + pos_found++; + given_arg = pos[i]; + } else { + mp_map_elem_t *kw = mp_map_lookup(kws, MP_OBJ_NEW_QSTR(allowed[i].qst), MP_MAP_LOOKUP); + if (kw == NULL) { + if (allowed[i].flags & MP_ARG_REQUIRED) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("'%q' argument required"), allowed[i].qst); + #endif + } + out_vals[i] = allowed[i].defval; + continue; + } else { + kws_found++; + given_arg = kw->value; + } + } + if ((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_BOOL) { + out_vals[i].u_bool = mp_obj_is_true(given_arg); + } else if ((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_INT) { + out_vals[i].u_int = mp_obj_get_int(given_arg); + } else { + assert((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_OBJ); + out_vals[i].u_obj = given_arg; + } + } + if (pos_found < n_pos) { + extra_positional: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + // TODO better error message + mp_raise_TypeError(MP_ERROR_TEXT("extra positional arguments given")); + #endif + } + if (kws_found < kws->used) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + // TODO better error message + mp_raise_TypeError(MP_ERROR_TEXT("extra keyword arguments given")); + #endif + } +} + +void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals) { + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_pos); + mp_arg_parse_all(n_pos, args, &kw_args, n_allowed, allowed, out_vals); +} + +NORETURN void mp_arg_error_terse_mismatch(void) { + mp_raise_TypeError(MP_ERROR_TEXT("argument num/types mismatch")); +} + +#if MICROPY_CPYTHON_COMPAT +NORETURN void mp_arg_error_unimpl_kw(void) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("keyword argument(s) not implemented - use normal args instead")); +} +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmarm.c b/non_catalog_apps/mp_flipper/lib/micropython/py/asmarm.c new file mode 100644 index 00000000..60064907 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmarm.c @@ -0,0 +1,399 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Fabian Vogt + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpconfig.h" + +// wrapper around everything in this file +#if MICROPY_EMIT_ARM + +#include "py/asmarm.h" + +#define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000) + +// Insert word into instruction flow +static void emit(asm_arm_t *as, uint op) { + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4); + if (c != NULL) { + *(uint32_t *)c = op; + } +} + +// Insert word into instruction flow, add "ALWAYS" condition code +static void emit_al(asm_arm_t *as, uint op) { + emit(as, op | ASM_ARM_CC_AL); +} + +// Basic instructions without condition code +static uint asm_arm_op_push(uint reglist) { + // stmfd sp!, {reglist} + return 0x92d0000 | (reglist & 0xFFFF); +} + +static uint asm_arm_op_pop(uint reglist) { + // ldmfd sp!, {reglist} + return 0x8bd0000 | (reglist & 0xFFFF); +} + +static uint asm_arm_op_mov_reg(uint rd, uint rn) { + // mov rd, rn + return 0x1a00000 | (rd << 12) | rn; +} + +static uint asm_arm_op_mov_imm(uint rd, uint imm) { + // mov rd, #imm + return 0x3a00000 | (rd << 12) | imm; +} + +static uint asm_arm_op_mvn_imm(uint rd, uint imm) { + // mvn rd, #imm + return 0x3e00000 | (rd << 12) | imm; +} + +static uint asm_arm_op_mvn_reg(uint rd, uint rm) { + // mvn rd, rm + return 0x1e00000 | (rd << 12) | rm; +} + +static uint asm_arm_op_add_imm(uint rd, uint rn, uint imm) { + // add rd, rn, #imm + return 0x2800000 | (rn << 16) | (rd << 12) | (imm & 0xFF); +} + +static uint asm_arm_op_add_reg(uint rd, uint rn, uint rm) { + // add rd, rn, rm + return 0x0800000 | (rn << 16) | (rd << 12) | rm; +} + +static uint asm_arm_op_sub_imm(uint rd, uint rn, uint imm) { + // sub rd, rn, #imm + return 0x2400000 | (rn << 16) | (rd << 12) | (imm & 0xFF); +} + +static uint asm_arm_op_sub_reg(uint rd, uint rn, uint rm) { + // sub rd, rn, rm + return 0x0400000 | (rn << 16) | (rd << 12) | rm; +} + +static uint asm_arm_op_rsb_imm(uint rd, uint rn, uint imm) { + // rsb rd, rn, #imm + return 0x2600000 | (rn << 16) | (rd << 12) | (imm & 0xFF); +} + +static uint asm_arm_op_mul_reg(uint rd, uint rm, uint rs) { + // mul rd, rm, rs + assert(rd != rm); + return 0x0000090 | (rd << 16) | (rs << 8) | rm; +} + +static uint asm_arm_op_and_reg(uint rd, uint rn, uint rm) { + // and rd, rn, rm + return 0x0000000 | (rn << 16) | (rd << 12) | rm; +} + +static uint asm_arm_op_eor_reg(uint rd, uint rn, uint rm) { + // eor rd, rn, rm + return 0x0200000 | (rn << 16) | (rd << 12) | rm; +} + +static uint asm_arm_op_orr_reg(uint rd, uint rn, uint rm) { + // orr rd, rn, rm + return 0x1800000 | (rn << 16) | (rd << 12) | rm; +} + +void asm_arm_bkpt(asm_arm_t *as) { + // bkpt #0 + emit_al(as, 0x1200070); +} + +// locals: +// - stored on the stack in ascending order +// - numbered 0 through num_locals-1 +// - SP points to first local +// +// | SP +// v +// l0 l1 l2 ... l(n-1) +// ^ ^ +// | low address | high address in RAM + +void asm_arm_entry(asm_arm_t *as, int num_locals) { + assert(num_locals >= 0); + + as->stack_adjust = 0; + as->push_reglist = 1 << ASM_ARM_REG_R1 + | 1 << ASM_ARM_REG_R2 + | 1 << ASM_ARM_REG_R3 + | 1 << ASM_ARM_REG_R4 + | 1 << ASM_ARM_REG_R5 + | 1 << ASM_ARM_REG_R6 + | 1 << ASM_ARM_REG_R7 + | 1 << ASM_ARM_REG_R8; + + // Only adjust the stack if there are more locals than usable registers + if (num_locals > 3) { + as->stack_adjust = num_locals * 4; + // Align stack to 8 bytes + if (num_locals & 1) { + as->stack_adjust += 4; + } + } + + emit_al(as, asm_arm_op_push(as->push_reglist | 1 << ASM_ARM_REG_LR)); + if (as->stack_adjust > 0) { + emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); + } +} + +void asm_arm_exit(asm_arm_t *as) { + if (as->stack_adjust > 0) { + emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); + } + + emit_al(as, asm_arm_op_pop(as->push_reglist | (1 << ASM_ARM_REG_PC))); +} + +void asm_arm_push(asm_arm_t *as, uint reglist) { + emit_al(as, asm_arm_op_push(reglist)); +} + +void asm_arm_pop(asm_arm_t *as, uint reglist) { + emit_al(as, asm_arm_op_pop(reglist)); +} + +void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src) { + emit_al(as, asm_arm_op_mov_reg(reg_dest, reg_src)); +} + +size_t asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm) { + // Insert immediate into code and jump over it + emit_al(as, 0x59f0000 | (rd << 12)); // ldr rd, [pc] + emit_al(as, 0xa000000); // b pc + size_t loc = mp_asm_base_get_code_pos(&as->base); + emit(as, imm); + return loc; +} + +void asm_arm_mov_reg_i32_optimised(asm_arm_t *as, uint rd, int imm) { + // TODO: There are more variants of immediate values + if ((imm & 0xFF) == imm) { + emit_al(as, asm_arm_op_mov_imm(rd, imm)); + } else if (imm < 0 && imm >= -256) { + // mvn is "move not", not "move negative" + emit_al(as, asm_arm_op_mvn_imm(rd, ~imm)); + } else { + asm_arm_mov_reg_i32(as, rd, imm); + } +} + +void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd) { + // str rd, [sp, #local_num*4] + emit_al(as, 0x58d0000 | (rd << 12) | (local_num << 2)); +} + +void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num) { + // ldr rd, [sp, #local_num*4] + emit_al(as, 0x59d0000 | (rd << 12) | (local_num << 2)); +} + +void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm) { + // cmp rd, #imm + emit_al(as, 0x3500000 | (rd << 16) | (imm & 0xFF)); +} + +void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn) { + // cmp rd, rn + emit_al(as, 0x1500000 | (rd << 16) | rn); +} + +void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond) { + emit(as, asm_arm_op_mov_imm(rd, 1) | cond); // movCOND rd, #1 + emit(as, asm_arm_op_mov_imm(rd, 0) | (cond ^ (1 << 28))); // mov!COND rd, #0 +} + +void asm_arm_mvn_reg_reg(asm_arm_t *as, uint rd, uint rm) { + // mvn rd, rm + // computes: rd := ~rm + emit_al(as, asm_arm_op_mvn_reg(rd, rm)); +} + +void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { + // add rd, rn, rm + emit_al(as, asm_arm_op_add_reg(rd, rn, rm)); +} + +void asm_arm_rsb_reg_reg_imm(asm_arm_t *as, uint rd, uint rn, uint imm) { + // rsb rd, rn, #imm + // computes: rd := #imm - rn + emit_al(as, asm_arm_op_rsb_imm(rd, rn, imm)); +} + +void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { + // sub rd, rn, rm + emit_al(as, asm_arm_op_sub_reg(rd, rn, rm)); +} + +void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rs, uint rm) { + // rs and rm are swapped because of restriction rd!=rm + // mul rd, rm, rs + emit_al(as, asm_arm_op_mul_reg(rd, rm, rs)); +} + +void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { + // and rd, rn, rm + emit_al(as, asm_arm_op_and_reg(rd, rn, rm)); +} + +void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { + // eor rd, rn, rm + emit_al(as, asm_arm_op_eor_reg(rd, rn, rm)); +} + +void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { + // orr rd, rn, rm + emit_al(as, asm_arm_op_orr_reg(rd, rn, rm)); +} + +void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) { + // add rd, sp, #local_num*4 + emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2)); +} + +void asm_arm_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, uint label) { + assert(label < as->base.max_num_labels); + mp_uint_t dest = as->base.label_offsets[label]; + mp_int_t rel = dest - as->base.code_offset; + rel -= 12 + 8; // adjust for load of rel, and then PC+8 prefetch of add_reg_reg_reg + + // To load rel int reg_dest, insert immediate into code and jump over it + emit_al(as, 0x59f0000 | (reg_dest << 12)); // ldr rd, [pc] + emit_al(as, 0xa000000); // b pc + emit(as, rel); + + // Do reg_dest += PC + asm_arm_add_reg_reg_reg(as, reg_dest, reg_dest, ASM_ARM_REG_PC); +} + +void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs) { + // mov rd, rd, lsl rs + emit_al(as, 0x1a00010 | (rd << 12) | (rs << 8) | rd); +} + +void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs) { + // mov rd, rd, lsr rs + emit_al(as, 0x1a00030 | (rd << 12) | (rs << 8) | rd); +} + +void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs) { + // mov rd, rd, asr rs + emit_al(as, 0x1a00050 | (rd << 12) | (rs << 8) | rd); +} + +void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + // ldr rd, [rn, #off] + emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset); +} + +void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) { + // ldrh rd, [rn] + emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12)); +} + +void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + // ldrh rd, [rn, #off] + emit_al(as, 0x1f000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); +} + +void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) { + // ldrb rd, [rn] + emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12)); +} + +void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset) { + // str rd, [rm, #off] + emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset); +} + +void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm) { + // strh rd, [rm] + emit_al(as, 0x1c000b0 | (rm << 16) | (rd << 12)); +} + +void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm) { + // strb rd, [rm] + emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12)); +} + +void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // str rd, [rm, rn, lsl #2] + emit_al(as, 0x7800100 | (rm << 16) | (rd << 12) | rn); +} + +void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // strh doesn't support scaled register index + emit_al(as, 0x1a00080 | (ASM_ARM_REG_R8 << 12) | rn); // mov r8, rn, lsl #1 + emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | ASM_ARM_REG_R8); // strh rd, [rm, r8] +} + +void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // strb rd, [rm, rn] + emit_al(as, 0x7c00000 | (rm << 16) | (rd << 12) | rn); +} + +void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) { + assert(label < as->base.max_num_labels); + mp_uint_t dest = as->base.label_offsets[label]; + mp_int_t rel = dest - as->base.code_offset; + rel -= 8; // account for instruction prefetch, PC is 8 bytes ahead of this instruction + rel >>= 2; // in ARM mode the branch target is 32-bit aligned, so the 2 LSB are omitted + + if (SIGNED_FIT24(rel)) { + emit(as, cond | 0xa000000 | (rel & 0xffffff)); + } else { + printf("asm_arm_bcc: branch does not fit in 24 bits\n"); + } +} + +void asm_arm_b_label(asm_arm_t *as, uint label) { + asm_arm_bcc_label(as, ASM_ARM_CC_AL, label); +} + +void asm_arm_bl_ind(asm_arm_t *as, uint fun_id, uint reg_temp) { + // The table offset should fit into the ldr instruction + assert(fun_id < (0x1000 / 4)); + emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_LR, ASM_ARM_REG_PC)); // mov lr, pc + emit_al(as, 0x597f000 | (fun_id << 2)); // ldr pc, [r7, #fun_id*4] +} + +void asm_arm_bx_reg(asm_arm_t *as, uint reg_src) { + emit_al(as, 0x012fff10 | reg_src); +} + +#endif // MICROPY_EMIT_ARM diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmarm.h b/non_catalog_apps/mp_flipper/lib/micropython/py/asmarm.h new file mode 100644 index 00000000..4a4253ae --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmarm.h @@ -0,0 +1,220 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Fabian Vogt + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_ASMARM_H +#define MICROPY_INCLUDED_PY_ASMARM_H + +#include "py/misc.h" +#include "py/asmbase.h" + +#define ASM_ARM_REG_R0 (0) +#define ASM_ARM_REG_R1 (1) +#define ASM_ARM_REG_R2 (2) +#define ASM_ARM_REG_R3 (3) +#define ASM_ARM_REG_R4 (4) +#define ASM_ARM_REG_R5 (5) +#define ASM_ARM_REG_R6 (6) +#define ASM_ARM_REG_R7 (7) +#define ASM_ARM_REG_R8 (8) +#define ASM_ARM_REG_R9 (9) +#define ASM_ARM_REG_R10 (10) +#define ASM_ARM_REG_R11 (11) +#define ASM_ARM_REG_R12 (12) +#define ASM_ARM_REG_R13 (13) +#define ASM_ARM_REG_R14 (14) +#define ASM_ARM_REG_R15 (15) +#define ASM_ARM_REG_SP (ASM_ARM_REG_R13) +#define ASM_ARM_REG_LR (ASM_ARM_REG_R14) +#define ASM_ARM_REG_PC (ASM_ARM_REG_R15) + +#define ASM_ARM_CC_EQ (0x0 << 28) +#define ASM_ARM_CC_NE (0x1 << 28) +#define ASM_ARM_CC_CS (0x2 << 28) +#define ASM_ARM_CC_CC (0x3 << 28) +#define ASM_ARM_CC_MI (0x4 << 28) +#define ASM_ARM_CC_PL (0x5 << 28) +#define ASM_ARM_CC_VS (0x6 << 28) +#define ASM_ARM_CC_VC (0x7 << 28) +#define ASM_ARM_CC_HI (0x8 << 28) +#define ASM_ARM_CC_LS (0x9 << 28) +#define ASM_ARM_CC_GE (0xa << 28) +#define ASM_ARM_CC_LT (0xb << 28) +#define ASM_ARM_CC_GT (0xc << 28) +#define ASM_ARM_CC_LE (0xd << 28) +#define ASM_ARM_CC_AL (0xe << 28) + +typedef struct _asm_arm_t { + mp_asm_base_t base; + uint push_reglist; + uint stack_adjust; +} asm_arm_t; + +static inline void asm_arm_end_pass(asm_arm_t *as) { + (void)as; +} + +void asm_arm_entry(asm_arm_t *as, int num_locals); +void asm_arm_exit(asm_arm_t *as); + +void asm_arm_bkpt(asm_arm_t *as); + +// mov +void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src); +size_t asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm); +void asm_arm_mov_reg_i32_optimised(asm_arm_t *as, uint rd, int imm); +void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd); +void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num); +void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond); + +// compare +void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm); +void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn); + +// arithmetic +void asm_arm_mvn_reg_reg(asm_arm_t *as, uint rd, uint rm); +void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm); +void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm); +void asm_arm_rsb_reg_reg_imm(asm_arm_t *as, uint rd, uint rn, uint imm); +void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm); +void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm); +void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm); +void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm); +void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num); +void asm_arm_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, uint label); +void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs); +void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs); +void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs); + +// memory +void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset); +void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn); +void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset); +void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn); +void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset); +void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm); +void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm); +// store to array +void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); +void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); +void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); + +// stack +void asm_arm_push(asm_arm_t *as, uint reglist); +void asm_arm_pop(asm_arm_t *as, uint reglist); + +// control flow +void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label); +void asm_arm_b_label(asm_arm_t *as, uint label); +void asm_arm_bl_ind(asm_arm_t *as, uint fun_id, uint reg_temp); +void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); + +// Holds a pointer to mp_fun_table +#define ASM_ARM_REG_FUN_TABLE ASM_ARM_REG_R7 + +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (4) + +#define REG_RET ASM_ARM_REG_R0 +#define REG_ARG_1 ASM_ARM_REG_R0 +#define REG_ARG_2 ASM_ARM_REG_R1 +#define REG_ARG_3 ASM_ARM_REG_R2 +#define REG_ARG_4 ASM_ARM_REG_R3 + +#define REG_TEMP0 ASM_ARM_REG_R0 +#define REG_TEMP1 ASM_ARM_REG_R1 +#define REG_TEMP2 ASM_ARM_REG_R2 + +#define REG_LOCAL_1 ASM_ARM_REG_R4 +#define REG_LOCAL_2 ASM_ARM_REG_R5 +#define REG_LOCAL_3 ASM_ARM_REG_R6 +#define REG_LOCAL_NUM (3) + +// Holds a pointer to mp_fun_table +#define REG_FUN_TABLE ASM_ARM_REG_FUN_TABLE + +#define ASM_T asm_arm_t +#define ASM_END_PASS asm_arm_end_pass +#define ASM_ENTRY asm_arm_entry +#define ASM_EXIT asm_arm_exit + +#define ASM_JUMP asm_arm_b_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ + do { \ + asm_arm_cmp_reg_i8(as, reg, 0); \ + asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \ + do { \ + asm_arm_cmp_reg_i8(as, reg, 0); \ + asm_arm_bcc_label(as, ASM_ARM_CC_NE, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_arm_cmp_reg_reg(as, reg1, reg2); \ + asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \ + } while (0) +#define ASM_JUMP_REG(as, reg) asm_arm_bx_reg((as), (reg)) +#define ASM_CALL_IND(as, idx) asm_arm_bl_ind(as, idx, ASM_ARM_REG_R3) + +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_arm_mov_local_reg((as), (local_num), (reg_src)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32_optimised((as), (reg_dest), (imm)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_arm_mov_reg_local((as), (reg_dest), (local_num)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_arm_mov_reg_local_addr((as), (reg_dest), (local_num)) +#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_arm_mov_reg_pcrel((as), (reg_dest), (label)) + +#define ASM_NOT_REG(as, reg_dest) asm_arm_mvn_reg_reg((as), (reg_dest), (reg_dest)) +#define ASM_NEG_REG(as, reg_dest) asm_arm_rsb_reg_reg_imm((as), (reg_dest), (reg_dest), 0) +#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift)) +#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_arm_lsr_reg_reg((as), (reg_dest), (reg_shift)) +#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_arm_orr_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_arm_eor_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_arm_and_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_arm_add_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_arm_sub_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_arm_mul_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_arm_ldrb_reg_reg((as), (reg_dest), (reg_base)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_arm_ldrh_reg_reg((as), (reg_dest), (reg_base)) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_arm_ldrh_reg_reg_offset((as), (reg_dest), (reg_base), 2 * (uint16_offset)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) + +#define ASM_STORE_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_str_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_value, reg_base) asm_arm_strb_reg_reg((as), (reg_value), (reg_base)) +#define ASM_STORE16_REG_REG(as, reg_value, reg_base) asm_arm_strh_reg_reg((as), (reg_value), (reg_base)) +#define ASM_STORE32_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) + +#endif // GENERIC_ASM_API + +#endif // MICROPY_INCLUDED_PY_ASMARM_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmbase.c b/non_catalog_apps/mp_flipper/lib/micropython/py/asmbase.c new file mode 100644 index 00000000..cf64e3f3 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmbase.c @@ -0,0 +1,113 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 "py/obj.h" +#include "py/misc.h" +#include "py/asmbase.h" + +#if MICROPY_EMIT_MACHINE_CODE + +void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels) { + as->max_num_labels = max_num_labels; + as->label_offsets = m_new(size_t, max_num_labels); +} + +void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) { + if (free_code) { + MP_PLAT_FREE_EXEC(as->code_base, as->code_size); + } + m_del(size_t, as->label_offsets, as->max_num_labels); +} + +void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { + if (pass < MP_ASM_PASS_EMIT) { + // Reset labels so we can detect backwards jumps (and verify unique assignment) + memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t)); + } else { + // allocating executable RAM is platform specific + MP_PLAT_ALLOC_EXEC(as->code_offset, (void **)&as->code_base, &as->code_size); + assert(as->code_base != NULL); + } + as->pass = pass; + as->suppress = false; + as->code_offset = 0; +} + +// all functions must go through this one to emit bytes +// if as->pass < MP_ASM_PASS_EMIT, then this function just counts the number +// of bytes needed and returns NULL, and callers should not store any data +// It also returns NULL if generated code should be suppressed at this point. +uint8_t *mp_asm_base_get_cur_to_write_bytes(void *as_in, size_t num_bytes_to_write) { + mp_asm_base_t *as = as_in; + uint8_t *c = NULL; + if (as->suppress) { + return c; + } + if (as->pass == MP_ASM_PASS_EMIT) { + assert(as->code_offset + num_bytes_to_write <= as->code_size); + c = as->code_base + as->code_offset; + } + as->code_offset += num_bytes_to_write; + return c; +} + +void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) { + assert(label < as->max_num_labels); + + // Assigning a label ends any dead-code region, and all following machine + // code should be emitted (until another mp_asm_base_suppress_code() call). + as->suppress = false; + + if (as->pass < MP_ASM_PASS_EMIT) { + // assign label offset + assert(as->label_offsets[label] == (size_t)-1); + as->label_offsets[label] = as->code_offset; + } else { + // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT + assert(as->label_offsets[label] == as->code_offset); + } +} + +// align must be a multiple of 2 +void mp_asm_base_align(mp_asm_base_t *as, unsigned int align) { + as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); +} + +// this function assumes a little endian machine +void mp_asm_base_data(mp_asm_base_t *as, unsigned int bytesize, uintptr_t val) { + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(as, bytesize); + if (c != NULL) { + for (unsigned int i = 0; i < bytesize; i++) { + *c++ = val; + val >>= 8; + } + } +} + +#endif // MICROPY_EMIT_MACHINE_CODE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmbase.h b/non_catalog_apps/mp_flipper/lib/micropython/py/asmbase.h new file mode 100644 index 00000000..352d2f54 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmbase.h @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_ASMBASE_H +#define MICROPY_INCLUDED_PY_ASMBASE_H + +#include +#include + +#define MP_ASM_PASS_COMPUTE (1) +#define MP_ASM_PASS_EMIT (2) + +typedef struct _mp_asm_base_t { + uint8_t pass; + + // Set to true using mp_asm_base_suppress_code() if the code generator + // should suppress emitted code due to it being dead code. + bool suppress; + + size_t code_offset; + size_t code_size; + uint8_t *code_base; + + size_t max_num_labels; + size_t *label_offsets; +} mp_asm_base_t; + +void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels); +void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code); +void mp_asm_base_start_pass(mp_asm_base_t *as, int pass); +uint8_t *mp_asm_base_get_cur_to_write_bytes(void *as, size_t num_bytes_to_write); +void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label); +void mp_asm_base_align(mp_asm_base_t *as, unsigned int align); +void mp_asm_base_data(mp_asm_base_t *as, unsigned int bytesize, uintptr_t val); + +static inline void mp_asm_base_suppress_code(mp_asm_base_t *as) { + as->suppress = true; +} + +static inline size_t mp_asm_base_get_code_pos(mp_asm_base_t *as) { + return as->code_offset; +} + +static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) { + return as->code_size; +} + +static inline void *mp_asm_base_get_code(mp_asm_base_t *as) { + #if defined(MP_PLAT_COMMIT_EXEC) + return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size, NULL); + #else + return as->code_base; + #endif +} + +#endif // MICROPY_INCLUDED_PY_ASMBASE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmthumb.c b/non_catalog_apps/mp_flipper/lib/micropython/py/asmthumb.c new file mode 100644 index 00000000..0df79e5f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmthumb.c @@ -0,0 +1,592 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpconfig.h" + +// wrapper around everything in this file +#if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB + +#include "py/mpstate.h" +#include "py/asmthumb.h" + +#ifdef _MSC_VER +#include + +static uint32_t mp_clz(uint32_t x) { + unsigned long lz = 0; + return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0; +} + +static uint32_t mp_ctz(uint32_t x) { + unsigned long tz = 0; + return _BitScanForward(&tz, x) ? tz : 0; +} +#else +#define mp_clz(x) __builtin_clz(x) +#define mp_ctz(x) __builtin_ctz(x) +#endif + +#define UNSIGNED_FIT5(x) ((uint32_t)(x) < 32) +#define UNSIGNED_FIT7(x) ((uint32_t)(x) < 128) +#define UNSIGNED_FIT8(x) (((x) & 0xffffff00) == 0) +#define UNSIGNED_FIT16(x) (((x) & 0xffff0000) == 0) +#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80) +#define SIGNED_FIT9(x) (((x) & 0xffffff00) == 0) || (((x) & 0xffffff00) == 0xffffff00) +#define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800) +#define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000) + +// Note: these actually take an imm12 but the high-bit is not encoded here +#define OP_ADD_W_RRI_HI(reg_src) (0xf200 | (reg_src)) +#define OP_ADD_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff)) +#define OP_SUB_W_RRI_HI(reg_src) (0xf2a0 | (reg_src)) +#define OP_SUB_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff)) + +#define OP_LDR_W_HI(reg_base) (0xf8d0 | (reg_base)) +#define OP_LDR_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) + +#define OP_LDRH_W_HI(reg_base) (0xf8b0 | (reg_base)) +#define OP_LDRH_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) + +static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) { + return mp_asm_base_get_cur_to_write_bytes(&as->base, n); +} + +/* +static void asm_thumb_write_byte_1(asm_thumb_t *as, byte b1) { + byte *c = asm_thumb_get_cur_to_write_bytes(as, 1); + c[0] = b1; +} +*/ + +/* +#define IMM32_L0(x) ((x) & 0xff) +#define IMM32_L1(x) (((x) >> 8) & 0xff) +#define IMM32_L2(x) (((x) >> 16) & 0xff) +#define IMM32_L3(x) (((x) >> 24) & 0xff) + +static void asm_thumb_write_word32(asm_thumb_t *as, int w32) { + byte *c = asm_thumb_get_cur_to_write_bytes(as, 4); + c[0] = IMM32_L0(w32); + c[1] = IMM32_L1(w32); + c[2] = IMM32_L2(w32); + c[3] = IMM32_L3(w32); +} +*/ + +// rlolist is a bit map indicating desired lo-registers +#define OP_PUSH_RLIST(rlolist) (0xb400 | (rlolist)) +#define OP_PUSH_RLIST_LR(rlolist) (0xb400 | 0x0100 | (rlolist)) +#define OP_POP_RLIST(rlolist) (0xbc00 | (rlolist)) +#define OP_POP_RLIST_PC(rlolist) (0xbc00 | 0x0100 | (rlolist)) + +// The number of words must fit in 7 unsigned bits +#define OP_ADD_SP(num_words) (0xb000 | (num_words)) +#define OP_SUB_SP(num_words) (0xb080 | (num_words)) + +// locals: +// - stored on the stack in ascending order +// - numbered 0 through num_locals-1 +// - SP points to first local +// +// | SP +// v +// l0 l1 l2 ... l(n-1) +// ^ ^ +// | low address | high address in RAM + +void asm_thumb_entry(asm_thumb_t *as, int num_locals) { + assert(num_locals >= 0); + + // If this Thumb machine code is run from ARM state then add a prelude + // to switch to Thumb state for the duration of the function. + #if MICROPY_DYNAMIC_COMPILER || MICROPY_EMIT_ARM || (defined(__arm__) && !defined(__thumb2__) && !defined(__thumb__)) + #if MICROPY_DYNAMIC_COMPILER + if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_ARMV6) + #endif + { + asm_thumb_op32(as, 0x4010, 0xe92d); // push {r4, lr} + asm_thumb_op32(as, 0xe009, 0xe28f); // add lr, pc, 8 + 1 + asm_thumb_op32(as, 0xff3e, 0xe12f); // blx lr + asm_thumb_op32(as, 0x4010, 0xe8bd); // pop {r4, lr} + asm_thumb_op32(as, 0xff1e, 0xe12f); // bx lr + } + #endif + + // work out what to push and how many extra spaces to reserve on stack + // so that we have enough for all locals and it's aligned an 8-byte boundary + // we push extra regs (r1, r2, r3) to help do the stack adjustment + // we probably should just always subtract from sp, since this would be more efficient + // for push rlist, lowest numbered register at the lowest address + uint reglist; + uint stack_adjust; + // don't pop r0 because it's used for return value + switch (num_locals) { + case 0: + reglist = 0xf2; + stack_adjust = 0; + break; + + case 1: + reglist = 0xf2; + stack_adjust = 0; + break; + + case 2: + reglist = 0xfe; + stack_adjust = 0; + break; + + case 3: + reglist = 0xfe; + stack_adjust = 0; + break; + + default: + reglist = 0xfe; + stack_adjust = ((num_locals - 3) + 1) & (~1); + break; + } + asm_thumb_op16(as, OP_PUSH_RLIST_LR(reglist)); + if (stack_adjust > 0) { + if (asm_thumb_allow_armv7m(as)) { + if (UNSIGNED_FIT7(stack_adjust)) { + asm_thumb_op16(as, OP_SUB_SP(stack_adjust)); + } else { + asm_thumb_op32(as, OP_SUB_W_RRI_HI(ASM_THUMB_REG_SP), OP_SUB_W_RRI_LO(ASM_THUMB_REG_SP, stack_adjust * 4)); + } + } else { + int adj = stack_adjust; + // we don't expect the stack_adjust to be massive + while (!UNSIGNED_FIT7(adj)) { + asm_thumb_op16(as, OP_SUB_SP(127)); + adj -= 127; + } + asm_thumb_op16(as, OP_SUB_SP(adj)); + } + } + as->push_reglist = reglist; + as->stack_adjust = stack_adjust; +} + +void asm_thumb_exit(asm_thumb_t *as) { + if (as->stack_adjust > 0) { + if (asm_thumb_allow_armv7m(as)) { + if (UNSIGNED_FIT7(as->stack_adjust)) { + asm_thumb_op16(as, OP_ADD_SP(as->stack_adjust)); + } else { + asm_thumb_op32(as, OP_ADD_W_RRI_HI(ASM_THUMB_REG_SP), OP_ADD_W_RRI_LO(ASM_THUMB_REG_SP, as->stack_adjust * 4)); + } + } else { + int adj = as->stack_adjust; + // we don't expect the stack_adjust to be massive + while (!UNSIGNED_FIT7(adj)) { + asm_thumb_op16(as, OP_ADD_SP(127)); + adj -= 127; + } + asm_thumb_op16(as, OP_ADD_SP(adj)); + } + } + asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist)); +} + +static mp_uint_t get_label_dest(asm_thumb_t *as, uint label) { + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; +} + +void asm_thumb_op16(asm_thumb_t *as, uint op) { + byte *c = asm_thumb_get_cur_to_write_bytes(as, 2); + if (c != NULL) { + // little endian + c[0] = op; + c[1] = op >> 8; + } +} + +void asm_thumb_op32(asm_thumb_t *as, uint op1, uint op2) { + byte *c = asm_thumb_get_cur_to_write_bytes(as, 4); + if (c != NULL) { + // little endian, op1 then op2 + c[0] = op1; + c[1] = op1 >> 8; + c[2] = op2; + c[3] = op2 >> 8; + } +} + +#define OP_FORMAT_4(op, rlo_dest, rlo_src) ((op) | ((rlo_src) << 3) | (rlo_dest)) + +void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_src < ASM_THUMB_REG_R8); + asm_thumb_op16(as, OP_FORMAT_4(op, rlo_dest, rlo_src)); +} + +void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src) { + uint op_lo; + if (reg_src < 8) { + op_lo = reg_src << 3; + } else { + op_lo = 0x40 | ((reg_src - 8) << 3); + } + if (reg_dest < 8) { + op_lo |= reg_dest; + } else { + op_lo |= 0x80 | (reg_dest - 8); + } + // mov reg_dest, reg_src + asm_thumb_op16(as, 0x4600 | op_lo); +} + +// if loading lo half with movw, the i16 value will be zero extended into the r32 register! +void asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src) { + assert(reg_dest < ASM_THUMB_REG_R15); + // mov[wt] reg_dest, #i16_src + asm_thumb_op32(as, mov_op | ((i16_src >> 1) & 0x0400) | ((i16_src >> 12) & 0xf), ((i16_src << 4) & 0x7000) | (reg_dest << 8) | (i16_src & 0xff)); +} + +static void asm_thumb_mov_rlo_i16(asm_thumb_t *as, uint rlo_dest, int i16_src) { + asm_thumb_mov_rlo_i8(as, rlo_dest, (i16_src >> 8) & 0xff); + asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, 8); + asm_thumb_add_rlo_i8(as, rlo_dest, i16_src & 0xff); +} + +#define OP_B_N(byte_offset) (0xe000 | (((byte_offset) >> 1) & 0x07ff)) + +bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + asm_thumb_op16(as, OP_B_N(rel)); + return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT12(rel); +} + +#define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff)) + +// all these bit-arithmetic operations need coverage testing! +#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 14) & 0x003f)) +#define OP_BCC_W_LO(byte_offset) (0x8000 | ((byte_offset) & 0x2000) | (((byte_offset) >> 1) & 0x0fff)) + +bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + if (!wide) { + asm_thumb_op16(as, OP_BCC_N(cond, rel)); + return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT9(rel); + } else if (asm_thumb_allow_armv7m(as)) { + asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); + return true; + } else { + // this method should not be called for ARMV6M + return false; + } +} + +#define OP_BL_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff)) +#define OP_BL_LO(byte_offset) (0xf800 | (((byte_offset) >> 1) & 0x07ff)) + +bool asm_thumb_bl_label(asm_thumb_t *as, uint label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + asm_thumb_op32(as, OP_BL_HI(rel), OP_BL_LO(rel)); + return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT23(rel); +} + +size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) { + // movw, movt does it in 8 bytes + // ldr [pc, #], dw does it in 6 bytes, but we might not reach to end of code for dw + + size_t loc = mp_asm_base_get_code_pos(&as->base); + + if (asm_thumb_allow_armv7m(as)) { + asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); + asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVT, reg_dest, i32 >> 16); + } else { + // should only be called with lo reg for ARMV6M + assert(reg_dest < ASM_THUMB_REG_R8); + + // sanity check that generated code is aligned + assert(!as->base.code_base || !(3u & (uintptr_t)as->base.code_base)); + + // basically: + // (nop) + // ldr reg_dest, _data + // b 1f + // _data: .word i32 + // 1: + if (as->base.code_offset & 2u) { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + } + asm_thumb_ldr_rlo_pcrel_i8(as, reg_dest, 0); + asm_thumb_op16(as, OP_B_N(2)); + asm_thumb_op16(as, i32 & 0xffff); + asm_thumb_op16(as, i32 >> 16); + } + + return loc; +} + +void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) { + if (reg_dest < 8 && UNSIGNED_FIT8(i32)) { + asm_thumb_mov_rlo_i8(as, reg_dest, i32); + } else if (asm_thumb_allow_armv7m(as)) { + if (UNSIGNED_FIT16(i32)) { + asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); + } else { + asm_thumb_mov_reg_i32(as, reg_dest, i32); + } + } else { + uint rlo_dest = reg_dest; + assert(rlo_dest < ASM_THUMB_REG_R8); // should never be called for ARMV6M + + bool negate = i32 < 0 && ((i32 + i32) & 0xffffffffu); // don't negate 0x80000000 + if (negate) { + i32 = -i32; + } + + uint clz = mp_clz(i32); + uint ctz = i32 ? mp_ctz(i32) : 0; + assert(clz + ctz <= 32); + if (clz + ctz >= 24) { + asm_thumb_mov_rlo_i8(as, rlo_dest, (i32 >> ctz) & 0xff); + asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, ctz); + } else if (UNSIGNED_FIT16(i32)) { + asm_thumb_mov_rlo_i16(as, rlo_dest, i32); + } else { + if (negate) { + // no point in negating if we're storing in 32 bit anyway + negate = false; + i32 = -i32; + } + asm_thumb_mov_reg_i32(as, rlo_dest, i32); + } + if (negate) { + asm_thumb_neg_rlo_rlo(as, rlo_dest, rlo_dest); + } + } +} + +#define OP_STR_TO_SP_OFFSET(rlo_dest, word_offset) (0x9000 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff)) +#define OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset) (0x9800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff)) + +static void asm_thumb_mov_local_check(asm_thumb_t *as, int word_offset) { + if (as->base.pass >= MP_ASM_PASS_EMIT) { + assert(word_offset >= 0); + if (!UNSIGNED_FIT8(word_offset)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("too many locals for native method")); + } + } +} + +void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num, uint rlo_src) { + assert(rlo_src < ASM_THUMB_REG_R8); + int word_offset = local_num; + asm_thumb_mov_local_check(as, word_offset); + asm_thumb_op16(as, OP_STR_TO_SP_OFFSET(rlo_src, word_offset)); +} + +void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) { + assert(rlo_dest < ASM_THUMB_REG_R8); + int word_offset = local_num; + asm_thumb_mov_local_check(as, word_offset); + asm_thumb_op16(as, OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset)); +} + +#define OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset) (0xa800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff)) + +void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) { + assert(rlo_dest < ASM_THUMB_REG_R8); + int word_offset = local_num; + assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); + asm_thumb_op16(as, OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset)); +} + +void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + rel |= 1; // to stay in Thumb state when jumping to this address + if (asm_thumb_allow_armv7m(as)) { + rel -= 6 + 4; // adjust for mov_reg_i16, sxth_rlo_rlo and then PC+4 prefetch of add_reg_reg + asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, rlo_dest, rel); // 4 bytes + asm_thumb_sxth_rlo_rlo(as, rlo_dest, rlo_dest); // 2 bytes + } else { + rel -= 8 + 4; // adjust for four instructions and then PC+4 prefetch of add_reg_reg + // 6 bytes + asm_thumb_mov_rlo_i16(as, rlo_dest, rel); + // 2 bytes - not always needed, but we want to keep the size the same + asm_thumb_sxth_rlo_rlo(as, rlo_dest, rlo_dest); + } + asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes +} + +// ARMv7-M only +static inline void asm_thumb_ldr_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { + asm_thumb_op32(as, OP_LDR_W_HI(reg_base), OP_LDR_W_LO(reg_dest, word_offset * 4)); +} + +// emits code for: reg_dest = reg_base + offset << offset_shift +static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint offset_shift) { + if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8) { + if (offset << offset_shift < 256) { + if (reg_dest != reg_base) { + asm_thumb_mov_reg_reg(as, reg_dest, reg_base); + } + asm_thumb_add_rlo_i8(as, reg_dest, offset << offset_shift); + } else if (UNSIGNED_FIT8(offset) && reg_dest != reg_base) { + asm_thumb_mov_rlo_i8(as, reg_dest, offset); + asm_thumb_lsl_rlo_rlo_i5(as, reg_dest, reg_dest, offset_shift); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_base); + } else if (reg_dest != reg_base) { + asm_thumb_mov_rlo_i16(as, reg_dest, offset << offset_shift); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_dest); + } else { + uint reg_other = reg_dest ^ 7; + asm_thumb_op16(as, OP_PUSH_RLIST((1 << reg_other))); + asm_thumb_mov_rlo_i16(as, reg_other, offset << offset_shift); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_other); + asm_thumb_op16(as, OP_POP_RLIST((1 << reg_other))); + } + } else { + assert(0); // should never be called for ARMV6M + } +} + +void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { + if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(word_offset)) { + asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_base, word_offset); + } else if (asm_thumb_allow_armv7m(as)) { + asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset); + } else { + asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, word_offset - 31, 2); + asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + } +} + +// ARMv7-M only +static inline void asm_thumb_ldrh_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) { + asm_thumb_op32(as, OP_LDRH_W_HI(reg_base), OP_LDRH_W_LO(reg_dest, uint16_offset * 2)); +} + +void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) { + if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(uint16_offset)) { + asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_base, uint16_offset); + } else if (asm_thumb_allow_armv7m(as)) { + asm_thumb_ldrh_reg_reg_i12(as, reg_dest, reg_base, uint16_offset); + } else { + asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, uint16_offset - 31, 1); + asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + } +} + +// this could be wrong, because it should have a range of +/- 16MiB... +#define OP_BW_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff)) +#define OP_BW_LO(byte_offset) (0xb800 | (((byte_offset) >> 1) & 0x07ff)) + +void asm_thumb_b_label(asm_thumb_t *as, uint label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + + if (dest != (mp_uint_t)-1 && rel <= -4) { + // is a backwards jump, so we know the size of the jump on the first pass + // calculate rel assuming 12 bit relative jump + if (SIGNED_FIT12(rel)) { + asm_thumb_op16(as, OP_B_N(rel)); + return; + } + } + + // is a large backwards jump, or a forwards jump (that must be assumed large) + + if (asm_thumb_allow_armv7m(as)) { + asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel)); + } else { + if (SIGNED_FIT12(rel)) { + // this code path has to be the same number of instructions irrespective of rel + asm_thumb_op16(as, OP_B_N(rel)); + } else { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + if (dest != (mp_uint_t)-1) { + // we have an actual branch > 12 bits; this is not handled yet + mp_raise_NotImplementedError(MP_ERROR_TEXT("native method too big")); + } + } + } +} + +void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + + if (dest != (mp_uint_t)-1 && rel <= -4) { + // is a backwards jump, so we know the size of the jump on the first pass + // calculate rel assuming 9 bit relative jump + if (SIGNED_FIT9(rel)) { + asm_thumb_op16(as, OP_BCC_N(cond, rel)); + return; + } + } + + // is a large backwards jump, or a forwards jump (that must be assumed large) + + if (asm_thumb_allow_armv7m(as)) { + asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); + } else { + // reverse the sense of the branch to jump over a longer branch + asm_thumb_op16(as, OP_BCC_N(cond ^ 1, 0)); + asm_thumb_b_label(as, label); + } +} + +void asm_thumb_bcc_rel9(asm_thumb_t *as, int cond, int rel) { + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + assert(SIGNED_FIT9(rel)); + asm_thumb_op16(as, OP_BCC_N(cond, rel)); +} + +void asm_thumb_b_rel12(asm_thumb_t *as, int rel) { + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + assert(SIGNED_FIT12(rel)); + asm_thumb_op16(as, OP_B_N(rel)); +} + +#define OP_BLX(reg) (0x4780 | ((reg) << 3)) +#define OP_SVC(arg) (0xdf00 | (arg)) + +void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp) { + // Load ptr to function from table, indexed by fun_id, then call it + asm_thumb_ldr_reg_reg_i12_optimised(as, reg_temp, ASM_THUMB_REG_FUN_TABLE, fun_id); + asm_thumb_op16(as, OP_BLX(reg_temp)); +} + +#endif // MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmthumb.h b/non_catalog_apps/mp_flipper/lib/micropython/py/asmthumb.h new file mode 100644 index 00000000..a9e68d7a --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmthumb.h @@ -0,0 +1,436 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_ASMTHUMB_H +#define MICROPY_INCLUDED_PY_ASMTHUMB_H + +#include +#include "py/misc.h" +#include "py/asmbase.h" +#include "py/persistentcode.h" + +#define ASM_THUMB_REG_R0 (0) +#define ASM_THUMB_REG_R1 (1) +#define ASM_THUMB_REG_R2 (2) +#define ASM_THUMB_REG_R3 (3) +#define ASM_THUMB_REG_R4 (4) +#define ASM_THUMB_REG_R5 (5) +#define ASM_THUMB_REG_R6 (6) +#define ASM_THUMB_REG_R7 (7) +#define ASM_THUMB_REG_R8 (8) +#define ASM_THUMB_REG_R9 (9) +#define ASM_THUMB_REG_R10 (10) +#define ASM_THUMB_REG_R11 (11) +#define ASM_THUMB_REG_R12 (12) +#define ASM_THUMB_REG_R13 (13) +#define ASM_THUMB_REG_R14 (14) +#define ASM_THUMB_REG_R15 (15) +#define ASM_THUMB_REG_SP (ASM_THUMB_REG_R13) +#define ASM_THUMB_REG_LR (REG_R14) + +#define ASM_THUMB_CC_EQ (0x0) +#define ASM_THUMB_CC_NE (0x1) +#define ASM_THUMB_CC_CS (0x2) +#define ASM_THUMB_CC_CC (0x3) +#define ASM_THUMB_CC_MI (0x4) +#define ASM_THUMB_CC_PL (0x5) +#define ASM_THUMB_CC_VS (0x6) +#define ASM_THUMB_CC_VC (0x7) +#define ASM_THUMB_CC_HI (0x8) +#define ASM_THUMB_CC_LS (0x9) +#define ASM_THUMB_CC_GE (0xa) +#define ASM_THUMB_CC_LT (0xb) +#define ASM_THUMB_CC_GT (0xc) +#define ASM_THUMB_CC_LE (0xd) + +typedef struct _asm_thumb_t { + mp_asm_base_t base; + uint32_t push_reglist; + uint32_t stack_adjust; +} asm_thumb_t; + +#if MICROPY_DYNAMIC_COMPILER + +static inline bool asm_thumb_allow_armv7m(asm_thumb_t *as) { + return MP_NATIVE_ARCH_ARMV7M <= mp_dynamic_compiler.native_arch + && mp_dynamic_compiler.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP; +} + +#else + +static inline bool asm_thumb_allow_armv7m(asm_thumb_t *as) { + return MICROPY_EMIT_THUMB_ARMV7M; +} + +#endif + +static inline void asm_thumb_end_pass(asm_thumb_t *as) { + (void)as; +} + +void asm_thumb_entry(asm_thumb_t *as, int num_locals); +void asm_thumb_exit(asm_thumb_t *as); + +// argument order follows ARM, in general dest is first +// note there is a difference between movw and mov.w, and many others! + +#define ASM_THUMB_OP_IT (0xbf00) +#define ASM_THUMB_OP_ITE_EQ (0xbf0c) +#define ASM_THUMB_OP_ITE_NE (0xbf14) +#define ASM_THUMB_OP_ITE_CS (0xbf2c) +#define ASM_THUMB_OP_ITE_CC (0xbf34) +#define ASM_THUMB_OP_ITE_MI (0xbf4c) +#define ASM_THUMB_OP_ITE_PL (0xbf54) +#define ASM_THUMB_OP_ITE_VS (0xbf6c) +#define ASM_THUMB_OP_ITE_VC (0xbf74) +#define ASM_THUMB_OP_ITE_HI (0xbf8c) +#define ASM_THUMB_OP_ITE_LS (0xbf94) +#define ASM_THUMB_OP_ITE_GE (0xbfac) +#define ASM_THUMB_OP_ITE_LT (0xbfb4) +#define ASM_THUMB_OP_ITE_GT (0xbfcc) +#define ASM_THUMB_OP_ITE_LE (0xbfd4) + +#define ASM_THUMB_OP_NOP (0xbf00) +#define ASM_THUMB_OP_WFI (0xbf30) +#define ASM_THUMB_OP_CPSID_I (0xb672) // cpsid i, disable irq +#define ASM_THUMB_OP_CPSIE_I (0xb662) // cpsie i, enable irq + +void asm_thumb_op16(asm_thumb_t *as, uint op); +void asm_thumb_op32(asm_thumb_t *as, uint op1, uint op2); + +static inline void asm_thumb_it_cc(asm_thumb_t *as, uint cc, uint mask) { + asm_thumb_op16(as, ASM_THUMB_OP_IT | (cc << 4) | mask); +} + +// FORMAT 1: move shifted register + +#define ASM_THUMB_FORMAT_1_LSL (0x0000) +#define ASM_THUMB_FORMAT_1_LSR (0x0800) +#define ASM_THUMB_FORMAT_1_ASR (0x1000) + +#define ASM_THUMB_FORMAT_1_ENCODE(op, rlo_dest, rlo_src, offset) \ + ((op) | ((offset) << 6) | ((rlo_src) << 3) | (rlo_dest)) + +static inline void asm_thumb_format_1(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src, uint offset) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_src < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_1_ENCODE(op, rlo_dest, rlo_src, offset)); +} + +// FORMAT 2: add/subtract + +#define ASM_THUMB_FORMAT_2_ADD (0x1800) +#define ASM_THUMB_FORMAT_2_SUB (0x1a00) +#define ASM_THUMB_FORMAT_2_REG_OPERAND (0x0000) +#define ASM_THUMB_FORMAT_2_IMM_OPERAND (0x0400) + +#define ASM_THUMB_FORMAT_2_ENCODE(op, rlo_dest, rlo_src, src_b) \ + ((op) | ((src_b) << 6) | ((rlo_src) << 3) | (rlo_dest)) + +static inline void asm_thumb_format_2(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src, int src_b) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_src < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_2_ENCODE(op, rlo_dest, rlo_src, src_b)); +} + +static inline void asm_thumb_add_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b) { + asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_ADD | ASM_THUMB_FORMAT_2_REG_OPERAND, rlo_dest, rlo_src_a, rlo_src_b); +} +static inline void asm_thumb_add_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, int i3_src) { + asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_ADD | ASM_THUMB_FORMAT_2_IMM_OPERAND, rlo_dest, rlo_src_a, i3_src); +} +static inline void asm_thumb_sub_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b) { + asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_SUB | ASM_THUMB_FORMAT_2_REG_OPERAND, rlo_dest, rlo_src_a, rlo_src_b); +} +static inline void asm_thumb_sub_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, int i3_src) { + asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_SUB | ASM_THUMB_FORMAT_2_IMM_OPERAND, rlo_dest, rlo_src_a, i3_src); +} + +// FORMAT 3: move/compare/add/subtract immediate +// These instructions all do zero extension of the i8 value + +#define ASM_THUMB_FORMAT_3_MOV (0x2000) +#define ASM_THUMB_FORMAT_3_CMP (0x2800) +#define ASM_THUMB_FORMAT_3_ADD (0x3000) +#define ASM_THUMB_FORMAT_3_SUB (0x3800) +#define ASM_THUMB_FORMAT_3_LDR (0x4800) + +#define ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8) ((op) | ((rlo) << 8) | (i8)) + +static inline void asm_thumb_format_3(asm_thumb_t *as, uint op, uint rlo, int i8) { + assert(rlo < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8)); +} + +static inline void asm_thumb_mov_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { + asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_MOV, rlo, i8); +} +static inline void asm_thumb_cmp_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { + asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_CMP, rlo, i8); +} +static inline void asm_thumb_add_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { + asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_ADD, rlo, i8); +} +static inline void asm_thumb_sub_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { + asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_SUB, rlo, i8); +} +static inline void asm_thumb_ldr_rlo_pcrel_i8(asm_thumb_t *as, uint rlo, uint i8) { + asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_LDR, rlo, i8); +} + +// FORMAT 4: ALU operations + +#define ASM_THUMB_FORMAT_4_AND (0x4000) +#define ASM_THUMB_FORMAT_4_EOR (0x4040) +#define ASM_THUMB_FORMAT_4_LSL (0x4080) +#define ASM_THUMB_FORMAT_4_LSR (0x40c0) +#define ASM_THUMB_FORMAT_4_ASR (0x4100) +#define ASM_THUMB_FORMAT_4_ADC (0x4140) +#define ASM_THUMB_FORMAT_4_SBC (0x4180) +#define ASM_THUMB_FORMAT_4_ROR (0x41c0) +#define ASM_THUMB_FORMAT_4_TST (0x4200) +#define ASM_THUMB_FORMAT_4_NEG (0x4240) +#define ASM_THUMB_FORMAT_4_CMP (0x4280) +#define ASM_THUMB_FORMAT_4_CMN (0x42c0) +#define ASM_THUMB_FORMAT_4_ORR (0x4300) +#define ASM_THUMB_FORMAT_4_MUL (0x4340) +#define ASM_THUMB_FORMAT_4_BIC (0x4380) +#define ASM_THUMB_FORMAT_4_MVN (0x43c0) + +void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src); + +static inline void asm_thumb_cmp_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_CMP, rlo_dest, rlo_src); +} +static inline void asm_thumb_mvn_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_MVN, rlo_dest, rlo_src); +} +static inline void asm_thumb_neg_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_NEG, rlo_dest, rlo_src); +} + +// FORMAT 5: hi register operations (add, cmp, mov, bx) +// For add/cmp/mov, at least one of the args must be a high register + +#define ASM_THUMB_FORMAT_5_ADD (0x4400) +#define ASM_THUMB_FORMAT_5_BX (0x4700) + +#define ASM_THUMB_FORMAT_5_ENCODE(op, r_dest, r_src) \ + ((op) | ((r_dest) << 4 & 0x0080) | ((r_src) << 3) | ((r_dest) & 0x0007)) + +static inline void asm_thumb_format_5(asm_thumb_t *as, uint op, uint r_dest, uint r_src) { + asm_thumb_op16(as, ASM_THUMB_FORMAT_5_ENCODE(op, r_dest, r_src)); +} + +static inline void asm_thumb_add_reg_reg(asm_thumb_t *as, uint r_dest, uint r_src) { + asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_ADD, r_dest, r_src); +} +static inline void asm_thumb_bx_reg(asm_thumb_t *as, uint r_src) { + asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_BX, 0, r_src); +} + +// FORMAT 9: load/store with immediate offset +// For word transfers the offset must be aligned, and >>2 + +// FORMAT 10: load/store halfword +// The offset must be aligned, and >>1 +// The load is zero extended into the register + +#define ASM_THUMB_FORMAT_9_STR (0x6000) +#define ASM_THUMB_FORMAT_9_LDR (0x6800) +#define ASM_THUMB_FORMAT_9_WORD_TRANSFER (0x0000) +#define ASM_THUMB_FORMAT_9_BYTE_TRANSFER (0x1000) + +#define ASM_THUMB_FORMAT_10_STRH (0x8000) +#define ASM_THUMB_FORMAT_10_LDRH (0x8800) + +#define ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset) \ + ((op) | (((offset) << 6) & 0x07c0) | ((rlo_base) << 3) | (rlo_dest)) + +static inline void asm_thumb_format_9_10(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_base, uint offset) { + asm_thumb_op16(as, ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset)); +} + +static inline void asm_thumb_str_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint word_offset) { + asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_src, rlo_base, word_offset); +} +static inline void asm_thumb_strb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint byte_offset) { + asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_src, rlo_base, byte_offset); +} +static inline void asm_thumb_strh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint uint16_offset) { + asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_STRH, rlo_src, rlo_base, uint16_offset); +} +static inline void asm_thumb_ldr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint word_offset) { + asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_dest, rlo_base, word_offset); +} +static inline void asm_thumb_ldrb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset) { + asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_dest, rlo_base, byte_offset); +} +static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint uint16_offset) { + asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, uint16_offset); +} +static inline void asm_thumb_lsl_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { + asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_LSL, rlo_dest, rlo_src, shift); +} +static inline void asm_thumb_asr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { + asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_ASR, rlo_dest, rlo_src, shift); +} + +// FORMAT 11: sign/zero extend + +#define ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src) \ + ((op) | ((rlo_src) << 3) | (rlo_dest)) + +#define ASM_THUMB_FORMAT_11_SXTH (0xb200) +#define ASM_THUMB_FORMAT_11_SXTB (0xb240) +#define ASM_THUMB_FORMAT_11_UXTH (0xb280) +#define ASM_THUMB_FORMAT_11_UXTB (0xb2c0) + +static inline void asm_thumb_format_11(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_src < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src)); +} + +static inline void asm_thumb_sxth_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_11(as, ASM_THUMB_FORMAT_11_SXTH, rlo_dest, rlo_src); +} + +// TODO convert these to above format style + +#define ASM_THUMB_OP_MOVW (0xf240) +#define ASM_THUMB_OP_MOVT (0xf2c0) + +void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src); +void asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src); + +// these return true if the destination is in range, false otherwise +bool asm_thumb_b_n_label(asm_thumb_t *as, uint label); +bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide); +bool asm_thumb_bl_label(asm_thumb_t *as, uint label); + +size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32_src); // convenience +void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32_src); // convenience +void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num_dest, uint rlo_src); // convenience +void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience +void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience +void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label); + +void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset); // convenience +void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset); // convenience + +void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch +void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch +void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenience +void asm_thumb_bcc_rel9(asm_thumb_t *as, int cc, int rel); +void asm_thumb_b_rel12(asm_thumb_t *as, int rel); + +// Holds a pointer to mp_fun_table +#define ASM_THUMB_REG_FUN_TABLE ASM_THUMB_REG_R7 + +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (4) + +#define REG_RET ASM_THUMB_REG_R0 +#define REG_ARG_1 ASM_THUMB_REG_R0 +#define REG_ARG_2 ASM_THUMB_REG_R1 +#define REG_ARG_3 ASM_THUMB_REG_R2 +#define REG_ARG_4 ASM_THUMB_REG_R3 +// rest of args go on stack + +#define REG_TEMP0 ASM_THUMB_REG_R0 +#define REG_TEMP1 ASM_THUMB_REG_R1 +#define REG_TEMP2 ASM_THUMB_REG_R2 + +#define REG_LOCAL_1 ASM_THUMB_REG_R4 +#define REG_LOCAL_2 ASM_THUMB_REG_R5 +#define REG_LOCAL_3 ASM_THUMB_REG_R6 +#define REG_LOCAL_NUM (3) + +#define REG_FUN_TABLE ASM_THUMB_REG_FUN_TABLE + +#define ASM_T asm_thumb_t +#define ASM_END_PASS asm_thumb_end_pass +#define ASM_ENTRY asm_thumb_entry +#define ASM_EXIT asm_thumb_exit + +#define ASM_JUMP asm_thumb_b_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ + do { \ + asm_thumb_cmp_rlo_i8(as, reg, 0); \ + asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \ + do { \ + asm_thumb_cmp_rlo_i8(as, reg, 0); \ + asm_thumb_bcc_label(as, ASM_THUMB_CC_NE, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_thumb_cmp_rlo_rlo(as, reg1, reg2); \ + asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \ + } while (0) +#define ASM_JUMP_REG(as, reg) asm_thumb_bx_reg((as), (reg)) +#define ASM_CALL_IND(as, idx) asm_thumb_bl_ind(as, idx, ASM_THUMB_REG_R3) + +#define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (local_num)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_thumb_mov_reg_local_addr((as), (reg_dest), (local_num)) +#define ASM_MOV_REG_PCREL(as, rlo_dest, label) asm_thumb_mov_reg_pcrel((as), (rlo_dest), (label)) + +#define ASM_NOT_REG(as, reg_dest) asm_thumb_mvn_rlo_rlo((as), (reg_dest), (reg_dest)) +#define ASM_NEG_REG(as, reg_dest) asm_thumb_neg_rlo_rlo((as), (reg_dest), (reg_dest)) +#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift)) +#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSR, (reg_dest), (reg_shift)) +#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ORR, (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_EOR, (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_AND, (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_thumb_add_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_thumb_sub_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_MUL, (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrb_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrh_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_thumb_ldrh_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (uint16_offset)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) + +#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_thumb_strb_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_thumb_strh_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + +#endif // MICROPY_INCLUDED_PY_ASMTHUMB_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmx64.c b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx64.c new file mode 100644 index 00000000..d9f33cfb --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx64.c @@ -0,0 +1,642 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpconfig.h" + +// wrapper around everything in this file +#if MICROPY_EMIT_X64 + +#include "py/asmx64.h" + +/* all offsets are measured in multiples of 8 bytes */ +#define WORD_SIZE (8) + +#define OPCODE_NOP (0x90) +#define OPCODE_PUSH_R64 (0x50) /* +rq */ +#define OPCODE_PUSH_I64 (0x68) +#define OPCODE_PUSH_M64 (0xff) /* /6 */ +#define OPCODE_POP_R64 (0x58) /* +rq */ +#define OPCODE_RET (0xc3) +#define OPCODE_MOV_I8_TO_R8 (0xb0) /* +rb */ +#define OPCODE_MOV_I64_TO_R64 (0xb8) /* +rq */ +#define OPCODE_MOV_I32_TO_RM32 (0xc7) +#define OPCODE_MOV_R8_TO_RM8 (0x88) /* /r */ +#define OPCODE_MOV_R64_TO_RM64 (0x89) /* /r */ +#define OPCODE_MOV_RM64_TO_R64 (0x8b) /* /r */ +#define OPCODE_MOVZX_RM8_TO_R64 (0xb6) /* 0x0f 0xb6/r */ +#define OPCODE_MOVZX_RM16_TO_R64 (0xb7) /* 0x0f 0xb7/r */ +#define OPCODE_LEA_MEM_TO_R64 (0x8d) /* /r */ +#define OPCODE_NOT_RM64 (0xf7) /* /2 */ +#define OPCODE_NEG_RM64 (0xf7) /* /3 */ +#define OPCODE_AND_R64_TO_RM64 (0x21) /* /r */ +#define OPCODE_OR_R64_TO_RM64 (0x09) /* /r */ +#define OPCODE_XOR_R64_TO_RM64 (0x31) /* /r */ +#define OPCODE_ADD_R64_TO_RM64 (0x01) /* /r */ +#define OPCODE_ADD_I32_TO_RM32 (0x81) /* /0 */ +#define OPCODE_ADD_I8_TO_RM32 (0x83) /* /0 */ +#define OPCODE_SUB_R64_FROM_RM64 (0x29) +#define OPCODE_SUB_I32_FROM_RM64 (0x81) /* /5 */ +#define OPCODE_SUB_I8_FROM_RM64 (0x83) /* /5 */ +// #define OPCODE_SHL_RM32_BY_I8 (0xc1) /* /4 */ +// #define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */ +// #define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */ +#define OPCODE_SHL_RM64_CL (0xd3) /* /4 */ +#define OPCODE_SHR_RM64_CL (0xd3) /* /5 */ +#define OPCODE_SAR_RM64_CL (0xd3) /* /7 */ +// #define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */ +// #define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */ +#define OPCODE_CMP_R64_WITH_RM64 (0x39) /* /r */ +// #define OPCODE_CMP_RM32_WITH_R32 (0x3b) +#define OPCODE_TEST_R8_WITH_RM8 (0x84) /* /r */ +#define OPCODE_TEST_R64_WITH_RM64 (0x85) /* /r */ +#define OPCODE_JMP_REL8 (0xeb) +#define OPCODE_JMP_REL32 (0xe9) +#define OPCODE_JMP_RM64 (0xff) /* /4 */ +#define OPCODE_JCC_REL8 (0x70) /* | jcc type */ +#define OPCODE_JCC_REL32_A (0x0f) +#define OPCODE_JCC_REL32_B (0x80) /* | jcc type */ +#define OPCODE_SETCC_RM8_A (0x0f) +#define OPCODE_SETCC_RM8_B (0x90) /* | jcc type, /0 */ +#define OPCODE_CALL_REL32 (0xe8) +#define OPCODE_CALL_RM32 (0xff) /* /2 */ +#define OPCODE_LEAVE (0xc9) + +#define MODRM_R64(x) (((x) & 0x7) << 3) +#define MODRM_RM_DISP0 (0x00) +#define MODRM_RM_DISP8 (0x40) +#define MODRM_RM_DISP32 (0x80) +#define MODRM_RM_REG (0xc0) +#define MODRM_RM_R64(x) ((x) & 0x7) + +#define OP_SIZE_PREFIX (0x66) + +#define REX_PREFIX (0x40) +#define REX_W (0x08) // width +#define REX_R (0x04) // register +#define REX_X (0x02) // index +#define REX_B (0x01) // base +#define REX_W_FROM_R64(r64) ((r64) >> 0 & 0x08) +#define REX_R_FROM_R64(r64) ((r64) >> 1 & 0x04) +#define REX_X_FROM_R64(r64) ((r64) >> 2 & 0x02) +#define REX_B_FROM_R64(r64) ((r64) >> 3 & 0x01) + +#define IMM32_L0(x) ((x) & 0xff) +#define IMM32_L1(x) (((x) >> 8) & 0xff) +#define IMM32_L2(x) (((x) >> 16) & 0xff) +#define IMM32_L3(x) (((x) >> 24) & 0xff) +#define IMM64_L4(x) (((x) >> 32) & 0xff) +#define IMM64_L5(x) (((x) >> 40) & 0xff) +#define IMM64_L6(x) (((x) >> 48) & 0xff) +#define IMM64_L7(x) (((x) >> 56) & 0xff) + +#define UNSIGNED_FIT8(x) (((x) & 0xffffffffffffff00) == 0) +#define UNSIGNED_FIT32(x) (((x) & 0xffffffff00000000) == 0) +#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80) + +static inline byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int n) { + return mp_asm_base_get_cur_to_write_bytes(&as->base, n); +} + +static void asm_x64_write_byte_1(asm_x64_t *as, byte b1) { + byte *c = asm_x64_get_cur_to_write_bytes(as, 1); + if (c != NULL) { + c[0] = b1; + } +} + +static void asm_x64_write_byte_2(asm_x64_t *as, byte b1, byte b2) { + byte *c = asm_x64_get_cur_to_write_bytes(as, 2); + if (c != NULL) { + c[0] = b1; + c[1] = b2; + } +} + +static void asm_x64_write_byte_3(asm_x64_t *as, byte b1, byte b2, byte b3) { + byte *c = asm_x64_get_cur_to_write_bytes(as, 3); + if (c != NULL) { + c[0] = b1; + c[1] = b2; + c[2] = b3; + } +} + +static void asm_x64_write_word32(asm_x64_t *as, int w32) { + byte *c = asm_x64_get_cur_to_write_bytes(as, 4); + if (c != NULL) { + c[0] = IMM32_L0(w32); + c[1] = IMM32_L1(w32); + c[2] = IMM32_L2(w32); + c[3] = IMM32_L3(w32); + } +} + +static void asm_x64_write_word64(asm_x64_t *as, int64_t w64) { + byte *c = asm_x64_get_cur_to_write_bytes(as, 8); + if (c != NULL) { + c[0] = IMM32_L0(w64); + c[1] = IMM32_L1(w64); + c[2] = IMM32_L2(w64); + c[3] = IMM32_L3(w64); + c[4] = IMM64_L4(w64); + c[5] = IMM64_L5(w64); + c[6] = IMM64_L6(w64); + c[7] = IMM64_L7(w64); + } +} + +/* unused +static void asm_x64_write_word32_to(asm_x64_t *as, int offset, int w32) { + byte* c; + assert(offset + 4 <= as->code_size); + c = as->code_base + offset; + c[0] = IMM32_L0(w32); + c[1] = IMM32_L1(w32); + c[2] = IMM32_L2(w32); + c[3] = IMM32_L3(w32); +} +*/ + +static void asm_x64_write_r64_disp(asm_x64_t *as, int r64, int disp_r64, int disp_offset) { + uint8_t rm_disp; + if (disp_offset == 0 && (disp_r64 & 7) != ASM_X64_REG_RBP) { + rm_disp = MODRM_RM_DISP0; + } else if (SIGNED_FIT8(disp_offset)) { + rm_disp = MODRM_RM_DISP8; + } else { + rm_disp = MODRM_RM_DISP32; + } + asm_x64_write_byte_1(as, MODRM_R64(r64) | rm_disp | MODRM_RM_R64(disp_r64)); + if ((disp_r64 & 7) == ASM_X64_REG_RSP) { + // Special case for rsp and r12, they need a SIB byte + asm_x64_write_byte_1(as, 0x24); + } + if (rm_disp == MODRM_RM_DISP8) { + asm_x64_write_byte_1(as, IMM32_L0(disp_offset)); + } else if (rm_disp == MODRM_RM_DISP32) { + asm_x64_write_word32(as, disp_offset); + } +} + +static void asm_x64_generic_r64_r64(asm_x64_t *as, int dest_r64, int src_r64, int op) { + asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), op, MODRM_R64(src_r64) | MODRM_RM_REG | MODRM_RM_R64(dest_r64)); +} + +void asm_x64_nop(asm_x64_t *as) { + asm_x64_write_byte_1(as, OPCODE_NOP); +} + +void asm_x64_push_r64(asm_x64_t *as, int src_r64) { + if (src_r64 < 8) { + asm_x64_write_byte_1(as, OPCODE_PUSH_R64 | src_r64); + } else { + asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_PUSH_R64 | (src_r64 & 7)); + } +} + +/* +void asm_x64_push_i32(asm_x64_t *as, int src_i32) { + asm_x64_write_byte_1(as, OPCODE_PUSH_I64); + asm_x64_write_word32(as, src_i32); // will be sign extended to 64 bits +} +*/ + +/* +void asm_x64_push_disp(asm_x64_t *as, int src_r64, int src_offset) { + assert(src_r64 < 8); + asm_x64_write_byte_1(as, OPCODE_PUSH_M64); + asm_x64_write_r64_disp(as, 6, src_r64, src_offset); +} +*/ + +void asm_x64_pop_r64(asm_x64_t *as, int dest_r64) { + if (dest_r64 < 8) { + asm_x64_write_byte_1(as, OPCODE_POP_R64 | dest_r64); + } else { + asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_POP_R64 | (dest_r64 & 7)); + } +} + +static void asm_x64_ret(asm_x64_t *as) { + asm_x64_write_byte_1(as, OPCODE_RET); +} + +void asm_x64_mov_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) { + asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_MOV_R64_TO_RM64); +} + +void asm_x64_mov_r8_to_mem8(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) { + if (src_r64 < 8 && dest_r64 < 8) { + asm_x64_write_byte_1(as, OPCODE_MOV_R8_TO_RM8); + } else { + asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R8_TO_RM8); + } + asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp); +} + +void asm_x64_mov_r16_to_mem16(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) { + if (src_r64 < 8 && dest_r64 < 8) { + asm_x64_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R64_TO_RM64); + } else { + asm_x64_write_byte_3(as, OP_SIZE_PREFIX, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64); + } + asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp); +} + +void asm_x64_mov_r32_to_mem32(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) { + if (src_r64 < 8 && dest_r64 < 8) { + asm_x64_write_byte_1(as, OPCODE_MOV_R64_TO_RM64); + } else { + asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64); + } + asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp); +} + +void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) { + // use REX prefix for 64 bit operation + asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64); + asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp); +} + +void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { + if (src_r64 < 8 && dest_r64 < 8) { + asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R64); + } else { + asm_x64_write_byte_3(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 0x0f, OPCODE_MOVZX_RM8_TO_R64); + } + asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); +} + +void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { + if (src_r64 < 8 && dest_r64 < 8) { + asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R64); + } else { + asm_x64_write_byte_3(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 0x0f, OPCODE_MOVZX_RM16_TO_R64); + } + asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); +} + +void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { + if (src_r64 < 8 && dest_r64 < 8) { + asm_x64_write_byte_1(as, OPCODE_MOV_RM64_TO_R64); + } else { + asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_MOV_RM64_TO_R64); + } + asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); +} + +void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { + // use REX prefix for 64 bit operation + asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_MOV_RM64_TO_R64); + asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); +} + +static void asm_x64_lea_disp_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { + // use REX prefix for 64 bit operation + asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_LEA_MEM_TO_R64); + asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); +} + +/* +void asm_x64_mov_i8_to_r8(asm_x64_t *as, int src_i8, int dest_r64) { + assert(dest_r64 < 8); + asm_x64_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r64, src_i8); +} +*/ + +size_t asm_x64_mov_i32_to_r64(asm_x64_t *as, int src_i32, int dest_r64) { + // cpu defaults to i32 to r64, with zero extension + if (dest_r64 < 8) { + asm_x64_write_byte_1(as, OPCODE_MOV_I64_TO_R64 | dest_r64); + } else { + asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7)); + } + size_t loc = mp_asm_base_get_code_pos(&as->base); + asm_x64_write_word32(as, src_i32); + return loc; +} + +void asm_x64_mov_i64_to_r64(asm_x64_t *as, int64_t src_i64, int dest_r64) { + // cpu defaults to i32 to r64 + // to mov i64 to r64 need to use REX prefix + asm_x64_write_byte_2(as, + REX_PREFIX | REX_W | (dest_r64 < 8 ? 0 : REX_B), + OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7)); + asm_x64_write_word64(as, src_i64); +} + +void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r64) { + // TODO use movzx, movsx if possible + if (UNSIGNED_FIT32(src_i64)) { + // 5 bytes + asm_x64_mov_i32_to_r64(as, src_i64 & 0xffffffff, dest_r64); + } else { + // 10 bytes + asm_x64_mov_i64_to_r64(as, src_i64, dest_r64); + } +} + +void asm_x64_not_r64(asm_x64_t *as, int dest_r64) { + asm_x64_generic_r64_r64(as, dest_r64, 2, OPCODE_NOT_RM64); +} + +void asm_x64_neg_r64(asm_x64_t *as, int dest_r64) { + asm_x64_generic_r64_r64(as, dest_r64, 3, OPCODE_NEG_RM64); +} + +void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) { + asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_AND_R64_TO_RM64); +} + +void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) { + asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_OR_R64_TO_RM64); +} + +void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) { + asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_XOR_R64_TO_RM64); +} + +void asm_x64_shl_r64_cl(asm_x64_t *as, int dest_r64) { + asm_x64_generic_r64_r64(as, dest_r64, 4, OPCODE_SHL_RM64_CL); +} + +void asm_x64_shr_r64_cl(asm_x64_t *as, int dest_r64) { + asm_x64_generic_r64_r64(as, dest_r64, 5, OPCODE_SHR_RM64_CL); +} + +void asm_x64_sar_r64_cl(asm_x64_t *as, int dest_r64) { + asm_x64_generic_r64_r64(as, dest_r64, 7, OPCODE_SAR_RM64_CL); +} + +void asm_x64_add_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) { + asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_ADD_R64_TO_RM64); +} + +void asm_x64_sub_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) { + asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_SUB_R64_FROM_RM64); +} + +void asm_x64_mul_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) { + // imul reg64, reg/mem64 -- 0x0f 0xaf /r + asm_x64_write_byte_1(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64)); + asm_x64_write_byte_3(as, 0x0f, 0xaf, MODRM_R64(dest_r64) | MODRM_RM_REG | MODRM_RM_R64(src_r64)); +} + +/* +void asm_x64_sub_i32_from_r32(asm_x64_t *as, int src_i32, int dest_r32) { + if (SIGNED_FIT8(src_i32)) { + // defaults to 32 bit operation + asm_x64_write_byte_2(as, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32)); + asm_x64_write_byte_1(as, src_i32 & 0xff); + } else { + // defaults to 32 bit operation + asm_x64_write_byte_2(as, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32)); + asm_x64_write_word32(as, src_i32); + } +} +*/ + +static void asm_x64_sub_r64_i32(asm_x64_t *as, int dest_r64, int src_i32) { + assert(dest_r64 < 8); + if (SIGNED_FIT8(src_i32)) { + // use REX prefix for 64 bit operation + asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64)); + asm_x64_write_byte_1(as, src_i32 & 0xff); + } else { + // use REX prefix for 64 bit operation + asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64)); + asm_x64_write_word32(as, src_i32); + } +} + +/* +void asm_x64_shl_r32_by_imm(asm_x64_t *as, int r32, int imm) { + asm_x64_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(r32)); + asm_x64_write_byte_1(as, imm); +} + +void asm_x64_shr_r32_by_imm(asm_x64_t *as, int r32, int imm) { + asm_x64_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(r32)); + asm_x64_write_byte_1(as, imm); +} + +void asm_x64_sar_r32_by_imm(asm_x64_t *as, int r32, int imm) { + asm_x64_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(r32)); + asm_x64_write_byte_1(as, imm); +} +*/ + +void asm_x64_cmp_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b) { + asm_x64_generic_r64_r64(as, src_r64_b, src_r64_a, OPCODE_CMP_R64_WITH_RM64); +} + +/* +void asm_x64_cmp_i32_with_r32(asm_x64_t *as, int src_i32, int src_r32) { + if (SIGNED_FIT8(src_i32)) { + asm_x64_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32)); + asm_x64_write_byte_1(as, src_i32 & 0xff); + } else { + asm_x64_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32)); + asm_x64_write_word32(as, src_i32); + } +} +*/ + +void asm_x64_test_r8_with_r8(asm_x64_t *as, int src_r64_a, int src_r64_b) { + assert(src_r64_a < 8); + assert(src_r64_b < 8); + asm_x64_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R64(src_r64_a) | MODRM_RM_REG | MODRM_RM_R64(src_r64_b)); +} + +void asm_x64_test_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b) { + asm_x64_generic_r64_r64(as, src_r64_b, src_r64_a, OPCODE_TEST_R64_WITH_RM64); +} + +void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8) { + assert(dest_r8 < 8); + asm_x64_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R64(0) | MODRM_RM_REG | MODRM_RM_R64(dest_r8)); +} + +void asm_x64_jmp_reg(asm_x64_t *as, int src_r64) { + assert(src_r64 < 8); + asm_x64_write_byte_2(as, OPCODE_JMP_RM64, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(src_r64)); +} + +static mp_uint_t get_label_dest(asm_x64_t *as, mp_uint_t label) { + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; +} + +void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + if (dest != (mp_uint_t)-1 && rel < 0) { + // is a backwards jump, so we know the size of the jump on the first pass + // calculate rel assuming 8 bit relative jump + rel -= 2; + if (SIGNED_FIT8(rel)) { + asm_x64_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff); + } else { + rel += 2; + goto large_jump; + } + } else { + // is a forwards jump, so need to assume it's large + large_jump: + rel -= 5; + asm_x64_write_byte_1(as, OPCODE_JMP_REL32); + asm_x64_write_word32(as, rel); + } +} + +void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + if (dest != (mp_uint_t)-1 && rel < 0) { + // is a backwards jump, so we know the size of the jump on the first pass + // calculate rel assuming 8 bit relative jump + rel -= 2; + if (SIGNED_FIT8(rel)) { + asm_x64_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff); + } else { + rel += 2; + goto large_jump; + } + } else { + // is a forwards jump, so need to assume it's large + large_jump: + rel -= 6; + asm_x64_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type); + asm_x64_write_word32(as, rel); + } +} + +void asm_x64_entry(asm_x64_t *as, int num_locals) { + assert(num_locals >= 0); + asm_x64_push_r64(as, ASM_X64_REG_RBP); + asm_x64_push_r64(as, ASM_X64_REG_RBX); + asm_x64_push_r64(as, ASM_X64_REG_R12); + asm_x64_push_r64(as, ASM_X64_REG_R13); + num_locals |= 1; // make it odd so stack is aligned on 16 byte boundary + asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, num_locals * WORD_SIZE); + as->num_locals = num_locals; +} + +void asm_x64_exit(asm_x64_t *as) { + asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, -as->num_locals * WORD_SIZE); + asm_x64_pop_r64(as, ASM_X64_REG_R13); + asm_x64_pop_r64(as, ASM_X64_REG_R12); + asm_x64_pop_r64(as, ASM_X64_REG_RBX); + asm_x64_pop_r64(as, ASM_X64_REG_RBP); + asm_x64_ret(as); +} + +// locals: +// - stored on the stack in ascending order +// - numbered 0 through as->num_locals-1 +// - RSP points to the first local +// +// | RSP +// v +// l0 l1 l2 ... l(n-1) +// ^ ^ +// | low address | high address in RAM +// +static int asm_x64_local_offset_from_rsp(asm_x64_t *as, int local_num) { + (void)as; + // Stack is full descending, RSP points to local0 + return local_num * WORD_SIZE; +} + +void asm_x64_mov_local_to_r64(asm_x64_t *as, int src_local_num, int dest_r64) { + asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, src_local_num), dest_r64); +} + +void asm_x64_mov_r64_to_local(asm_x64_t *as, int src_r64, int dest_local_num) { + asm_x64_mov_r64_to_mem64(as, src_r64, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, dest_local_num)); +} + +void asm_x64_mov_local_addr_to_r64(asm_x64_t *as, int local_num, int dest_r64) { + int offset = asm_x64_local_offset_from_rsp(as, local_num); + if (offset == 0) { + asm_x64_mov_r64_r64(as, dest_r64, ASM_X64_REG_RSP); + } else { + asm_x64_lea_disp_to_r64(as, ASM_X64_REG_RSP, offset, dest_r64); + } +} + +void asm_x64_mov_reg_pcrel(asm_x64_t *as, int dest_r64, mp_uint_t label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - (as->base.code_offset + 7); + asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64), OPCODE_LEA_MEM_TO_R64, MODRM_R64(dest_r64) | MODRM_RM_R64(5)); + asm_x64_write_word32(as, rel); +} + +/* +void asm_x64_push_local(asm_x64_t *as, int local_num) { + asm_x64_push_disp(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, local_num)); +} + +void asm_x64_push_local_addr(asm_x64_t *as, int local_num, int temp_r64) { + asm_x64_mov_r64_r64(as, temp_r64, ASM_X64_REG_RSP); + asm_x64_add_i32_to_r32(as, asm_x64_local_offset_from_rsp(as, local_num), temp_r64); + asm_x64_push_r64(as, temp_r64); +} +*/ + +/* + can't use these because code might be relocated when resized + +void asm_x64_call(asm_x64_t *as, void* func) { + asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP); + asm_x64_write_byte_1(as, OPCODE_CALL_REL32); + asm_x64_write_word32(as, func - (void*)(as->code_cur + 4)); + asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP); +} + +void asm_x64_call_i1(asm_x64_t *as, void* func, int i1) { + asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP); + asm_x64_sub_i32_from_r32(as, 12, ASM_X64_REG_RSP); + asm_x64_push_i32(as, i1); + asm_x64_write_byte_1(as, OPCODE_CALL_REL32); + asm_x64_write_word32(as, func - (void*)(as->code_cur + 4)); + asm_x64_add_i32_to_r32(as, 16, ASM_X64_REG_RSP); + asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP); +} +*/ + +void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r64) { + assert(temp_r64 < 8); + asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_FUN_TABLE, fun_id * WORD_SIZE, temp_r64); + asm_x64_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R64(2) | MODRM_RM_REG | MODRM_RM_R64(temp_r64)); +} + +#endif // MICROPY_EMIT_X64 diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmx64.h b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx64.h new file mode 100644 index 00000000..c63e3179 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx64.h @@ -0,0 +1,223 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_ASMX64_H +#define MICROPY_INCLUDED_PY_ASMX64_H + +#include "py/mpconfig.h" +#include "py/misc.h" +#include "py/asmbase.h" + +// AMD64 calling convention is: +// - args pass in: RDI, RSI, RDX, RCX, R08, R09 +// - return value in RAX +// - stack must be aligned on a 16-byte boundary before all calls +// - RAX, RCX, RDX, RSI, RDI, R08, R09, R10, R11 are caller-save +// - RBX, RBP, R12, R13, R14, R15 are callee-save + +// In the functions below, argument order follows x86 docs and generally +// the destination is the first argument. +// NOTE: this is a change from the old convention used in this file and +// some functions still use the old (reverse) convention. + +#define ASM_X64_REG_RAX (0) +#define ASM_X64_REG_RCX (1) +#define ASM_X64_REG_RDX (2) +#define ASM_X64_REG_RBX (3) +#define ASM_X64_REG_RSP (4) +#define ASM_X64_REG_RBP (5) +#define ASM_X64_REG_RSI (6) +#define ASM_X64_REG_RDI (7) +#define ASM_X64_REG_R08 (8) +#define ASM_X64_REG_R09 (9) +#define ASM_X64_REG_R10 (10) +#define ASM_X64_REG_R11 (11) +#define ASM_X64_REG_R12 (12) +#define ASM_X64_REG_R13 (13) +#define ASM_X64_REG_R14 (14) +#define ASM_X64_REG_R15 (15) + +// condition codes, used for jcc and setcc (despite their j-name!) +#define ASM_X64_CC_JB (0x2) // below, unsigned +#define ASM_X64_CC_JAE (0x3) // above or equal, unsigned +#define ASM_X64_CC_JZ (0x4) +#define ASM_X64_CC_JE (0x4) +#define ASM_X64_CC_JNZ (0x5) +#define ASM_X64_CC_JNE (0x5) +#define ASM_X64_CC_JBE (0x6) // below or equal, unsigned +#define ASM_X64_CC_JA (0x7) // above, unsigned +#define ASM_X64_CC_JL (0xc) // less, signed +#define ASM_X64_CC_JGE (0xd) // greater or equal, signed +#define ASM_X64_CC_JLE (0xe) // less or equal, signed +#define ASM_X64_CC_JG (0xf) // greater, signed + +typedef struct _asm_x64_t { + mp_asm_base_t base; + int num_locals; +} asm_x64_t; + +static inline void asm_x64_end_pass(asm_x64_t *as) { + (void)as; +} + +void asm_x64_nop(asm_x64_t *as); +void asm_x64_push_r64(asm_x64_t *as, int src_r64); +void asm_x64_pop_r64(asm_x64_t *as, int dest_r64); +void asm_x64_mov_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); +size_t asm_x64_mov_i32_to_r64(asm_x64_t *as, int src_i32, int dest_r64); +void asm_x64_mov_i64_to_r64(asm_x64_t *as, int64_t src_i64, int dest_r64); +void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r64); +void asm_x64_mov_r8_to_mem8(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp); +void asm_x64_mov_r16_to_mem16(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp); +void asm_x64_mov_r32_to_mem32(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp); +void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp); +void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64); +void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64); +void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64); +void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64); +void asm_x64_not_r64(asm_x64_t *as, int dest_r64); +void asm_x64_neg_r64(asm_x64_t *as, int dest_r64); +void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); +void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); +void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); +void asm_x64_shl_r64_cl(asm_x64_t *as, int dest_r64); +void asm_x64_shr_r64_cl(asm_x64_t *as, int dest_r64); +void asm_x64_sar_r64_cl(asm_x64_t *as, int dest_r64); +void asm_x64_add_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); +void asm_x64_sub_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); +void asm_x64_mul_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); +void asm_x64_cmp_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b); +void asm_x64_test_r8_with_r8(asm_x64_t *as, int src_r64_a, int src_r64_b); +void asm_x64_test_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b); +void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8); +void asm_x64_jmp_reg(asm_x64_t *as, int src_r64); +void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label); +void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label); +void asm_x64_entry(asm_x64_t *as, int num_locals); +void asm_x64_exit(asm_x64_t *as); +void asm_x64_mov_local_to_r64(asm_x64_t *as, int src_local_num, int dest_r64); +void asm_x64_mov_r64_to_local(asm_x64_t *as, int src_r64, int dest_local_num); +void asm_x64_mov_local_addr_to_r64(asm_x64_t *as, int local_num, int dest_r64); +void asm_x64_mov_reg_pcrel(asm_x64_t *as, int dest_r64, mp_uint_t label); +void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); + +// Holds a pointer to mp_fun_table +#define ASM_X64_REG_FUN_TABLE ASM_X64_REG_RBP + +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (8) + +#define REG_RET ASM_X64_REG_RAX +#define REG_ARG_1 ASM_X64_REG_RDI +#define REG_ARG_2 ASM_X64_REG_RSI +#define REG_ARG_3 ASM_X64_REG_RDX +#define REG_ARG_4 ASM_X64_REG_RCX +#define REG_ARG_5 ASM_X64_REG_R08 + +// caller-save +#define REG_TEMP0 ASM_X64_REG_RAX +#define REG_TEMP1 ASM_X64_REG_RDI +#define REG_TEMP2 ASM_X64_REG_RSI + +// callee-save +#define REG_LOCAL_1 ASM_X64_REG_RBX +#define REG_LOCAL_2 ASM_X64_REG_R12 +#define REG_LOCAL_3 ASM_X64_REG_R13 +#define REG_LOCAL_NUM (3) + +// Holds a pointer to mp_fun_table +#define REG_FUN_TABLE ASM_X64_REG_FUN_TABLE + +#define ASM_T asm_x64_t +#define ASM_END_PASS asm_x64_end_pass +#define ASM_ENTRY asm_x64_entry +#define ASM_EXIT asm_x64_exit + +#define ASM_JUMP asm_x64_jmp_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ + do { \ + if (bool_test) { \ + asm_x64_test_r8_with_r8((as), (reg), (reg)); \ + } else { \ + asm_x64_test_r64_with_r64((as), (reg), (reg)); \ + } \ + asm_x64_jcc_label(as, ASM_X64_CC_JZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \ + do { \ + if (bool_test) { \ + asm_x64_test_r8_with_r8((as), (reg), (reg)); \ + } else { \ + asm_x64_test_r64_with_r64((as), (reg), (reg)); \ + } \ + asm_x64_jcc_label(as, ASM_X64_CC_JNZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_x64_cmp_r64_with_r64(as, reg1, reg2); \ + asm_x64_jcc_label(as, ASM_X64_CC_JE, label); \ + } while (0) +#define ASM_JUMP_REG(as, reg) asm_x64_jmp_reg((as), (reg)) +#define ASM_CALL_IND(as, idx) asm_x64_call_ind(as, idx, ASM_X64_REG_RAX) + +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x64_mov_r64_to_local((as), (reg_src), (local_num)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_optimised((as), (imm), (reg_dest)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x64_mov_local_to_r64((as), (local_num), (reg_dest)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x64_mov_local_addr_to_r64((as), (local_num), (reg_dest)) +#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x64_mov_reg_pcrel((as), (reg_dest), (label)) + +#define ASM_NOT_REG(as, reg) asm_x64_not_r64((as), (reg)) +#define ASM_NEG_REG(as, reg) asm_x64_neg_r64((as), (reg)) +#define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg)) +#define ASM_LSR_REG(as, reg) asm_x64_shr_r64_cl((as), (reg)) +#define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x64_or_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x64_xor_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x64_and_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x64_add_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x64_sub_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x64_mul_r64_r64((as), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem64_to_r64((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (word_offset), (reg_dest)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem8_to_r64zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 2 * (uint16_offset), (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 0, (reg_dest)) + +#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + +#endif // MICROPY_INCLUDED_PY_ASMX64_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmx86.c b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx86.c new file mode 100644 index 00000000..4acac1b4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx86.c @@ -0,0 +1,545 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 "py/mpconfig.h" + +// wrapper around everything in this file +#if MICROPY_EMIT_X86 + +#include "py/asmx86.h" + +/* all offsets are measured in multiples of 4 bytes */ +#define WORD_SIZE (4) + +#define OPCODE_NOP (0x90) +#define OPCODE_PUSH_R32 (0x50) +// #define OPCODE_PUSH_I32 (0x68) +// #define OPCODE_PUSH_M32 (0xff) /* /6 */ +#define OPCODE_POP_R32 (0x58) +#define OPCODE_RET (0xc3) +// #define OPCODE_MOV_I8_TO_R8 (0xb0) /* +rb */ +#define OPCODE_MOV_I32_TO_R32 (0xb8) +// #define OPCODE_MOV_I32_TO_RM32 (0xc7) +#define OPCODE_MOV_R8_TO_RM8 (0x88) /* /r */ +#define OPCODE_MOV_R32_TO_RM32 (0x89) /* /r */ +#define OPCODE_MOV_RM32_TO_R32 (0x8b) /* /r */ +#define OPCODE_MOVZX_RM8_TO_R32 (0xb6) /* 0x0f 0xb6/r */ +#define OPCODE_MOVZX_RM16_TO_R32 (0xb7) /* 0x0f 0xb7/r */ +#define OPCODE_LEA_MEM_TO_R32 (0x8d) /* /r */ +#define OPCODE_NOT_RM32 (0xf7) /* /2 */ +#define OPCODE_NEG_RM32 (0xf7) /* /3 */ +#define OPCODE_AND_R32_TO_RM32 (0x21) /* /r */ +#define OPCODE_OR_R32_TO_RM32 (0x09) /* /r */ +#define OPCODE_XOR_R32_TO_RM32 (0x31) /* /r */ +#define OPCODE_ADD_R32_TO_RM32 (0x01) +#define OPCODE_ADD_I32_TO_RM32 (0x81) /* /0 */ +#define OPCODE_ADD_I8_TO_RM32 (0x83) /* /0 */ +#define OPCODE_SUB_R32_FROM_RM32 (0x29) +#define OPCODE_SUB_I32_FROM_RM32 (0x81) /* /5 */ +#define OPCODE_SUB_I8_FROM_RM32 (0x83) /* /5 */ +// #define OPCODE_SHL_RM32_BY_I8 (0xc1) /* /4 */ +// #define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */ +// #define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */ +#define OPCODE_SHL_RM32_CL (0xd3) /* /4 */ +#define OPCODE_SHR_RM32_CL (0xd3) /* /5 */ +#define OPCODE_SAR_RM32_CL (0xd3) /* /7 */ +// #define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */ +// #define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */ +#define OPCODE_CMP_R32_WITH_RM32 (0x39) +// #define OPCODE_CMP_RM32_WITH_R32 (0x3b) +#define OPCODE_TEST_R8_WITH_RM8 (0x84) /* /r */ +#define OPCODE_TEST_R32_WITH_RM32 (0x85) /* /r */ +#define OPCODE_JMP_REL8 (0xeb) +#define OPCODE_JMP_REL32 (0xe9) +#define OPCODE_JMP_RM32 (0xff) /* /4 */ +#define OPCODE_JCC_REL8 (0x70) /* | jcc type */ +#define OPCODE_JCC_REL32_A (0x0f) +#define OPCODE_JCC_REL32_B (0x80) /* | jcc type */ +#define OPCODE_SETCC_RM8_A (0x0f) +#define OPCODE_SETCC_RM8_B (0x90) /* | jcc type, /0 */ +#define OPCODE_CALL_REL32 (0xe8) +#define OPCODE_CALL_RM32 (0xff) /* /2 */ +#define OPCODE_LEAVE (0xc9) + +#define MODRM_R32(x) ((x) << 3) +#define MODRM_RM_DISP0 (0x00) +#define MODRM_RM_DISP8 (0x40) +#define MODRM_RM_DISP32 (0x80) +#define MODRM_RM_REG (0xc0) +#define MODRM_RM_R32(x) (x) + +#define OP_SIZE_PREFIX (0x66) + +#define IMM32_L0(x) ((x) & 0xff) +#define IMM32_L1(x) (((x) >> 8) & 0xff) +#define IMM32_L2(x) (((x) >> 16) & 0xff) +#define IMM32_L3(x) (((x) >> 24) & 0xff) + +#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80) + +static void asm_x86_write_byte_1(asm_x86_t *as, byte b1) { + byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 1); + if (c != NULL) { + c[0] = b1; + } +} + +static void asm_x86_write_byte_2(asm_x86_t *as, byte b1, byte b2) { + byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2); + if (c != NULL) { + c[0] = b1; + c[1] = b2; + } +} + +static void asm_x86_write_byte_3(asm_x86_t *as, byte b1, byte b2, byte b3) { + byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3); + if (c != NULL) { + c[0] = b1; + c[1] = b2; + c[2] = b3; + } +} + +static void asm_x86_write_word32(asm_x86_t *as, int w32) { + byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4); + if (c != NULL) { + c[0] = IMM32_L0(w32); + c[1] = IMM32_L1(w32); + c[2] = IMM32_L2(w32); + c[3] = IMM32_L3(w32); + } +} + +static void asm_x86_write_r32_disp(asm_x86_t *as, int r32, int disp_r32, int disp_offset) { + uint8_t rm_disp; + if (disp_offset == 0 && disp_r32 != ASM_X86_REG_EBP) { + rm_disp = MODRM_RM_DISP0; + } else if (SIGNED_FIT8(disp_offset)) { + rm_disp = MODRM_RM_DISP8; + } else { + rm_disp = MODRM_RM_DISP32; + } + asm_x86_write_byte_1(as, MODRM_R32(r32) | rm_disp | MODRM_RM_R32(disp_r32)); + if (disp_r32 == ASM_X86_REG_ESP) { + // Special case for esp, it needs a SIB byte + asm_x86_write_byte_1(as, 0x24); + } + if (rm_disp == MODRM_RM_DISP8) { + asm_x86_write_byte_1(as, IMM32_L0(disp_offset)); + } else if (rm_disp == MODRM_RM_DISP32) { + asm_x86_write_word32(as, disp_offset); + } +} + +static void asm_x86_generic_r32_r32(asm_x86_t *as, int dest_r32, int src_r32, int op) { + asm_x86_write_byte_2(as, op, MODRM_R32(src_r32) | MODRM_RM_REG | MODRM_RM_R32(dest_r32)); +} + +#if 0 +static void asm_x86_nop(asm_x86_t *as) { + asm_x86_write_byte_1(as, OPCODE_NOP); +} +#endif + +static void asm_x86_push_r32(asm_x86_t *as, int src_r32) { + asm_x86_write_byte_1(as, OPCODE_PUSH_R32 | src_r32); +} + +#if 0 +void asm_x86_push_i32(asm_x86_t *as, int src_i32) { + asm_x86_write_byte_1(as, OPCODE_PUSH_I32); + asm_x86_write_word32(as, src_i32); +} + +void asm_x86_push_disp(asm_x86_t *as, int src_r32, int src_offset) { + asm_x86_write_byte_1(as, OPCODE_PUSH_M32); + asm_x86_write_r32_disp(as, 6, src_r32, src_offset); +} +#endif + +static void asm_x86_pop_r32(asm_x86_t *as, int dest_r32) { + asm_x86_write_byte_1(as, OPCODE_POP_R32 | dest_r32); +} + +static void asm_x86_ret(asm_x86_t *as) { + asm_x86_write_byte_1(as, OPCODE_RET); +} + +void asm_x86_mov_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) { + asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_MOV_R32_TO_RM32); +} + +void asm_x86_mov_r8_to_mem8(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) { + asm_x86_write_byte_1(as, OPCODE_MOV_R8_TO_RM8); + asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp); +} + +void asm_x86_mov_r16_to_mem16(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) { + asm_x86_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R32_TO_RM32); + asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp); +} + +void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) { + asm_x86_write_byte_1(as, OPCODE_MOV_R32_TO_RM32); + asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp); +} + +void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) { + asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R32); + asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp); +} + +void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) { + asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R32); + asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp); +} + +void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) { + asm_x86_write_byte_1(as, OPCODE_MOV_RM32_TO_R32); + asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp); +} + +static void asm_x86_lea_disp_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) { + asm_x86_write_byte_1(as, OPCODE_LEA_MEM_TO_R32); + asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp); +} + +#if 0 +void asm_x86_mov_i8_to_r8(asm_x86_t *as, int src_i8, int dest_r32) { + asm_x86_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r32, src_i8); +} +#endif + +size_t asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32) { + asm_x86_write_byte_1(as, OPCODE_MOV_I32_TO_R32 | dest_r32); + size_t loc = mp_asm_base_get_code_pos(&as->base); + asm_x86_write_word32(as, src_i32); + return loc; +} + +void asm_x86_not_r32(asm_x86_t *as, int dest_r32) { + asm_x86_generic_r32_r32(as, dest_r32, 2, OPCODE_NOT_RM32); +} + +void asm_x86_neg_r32(asm_x86_t *as, int dest_r32) { + asm_x86_generic_r32_r32(as, dest_r32, 3, OPCODE_NEG_RM32); +} + +void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) { + asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_AND_R32_TO_RM32); +} + +void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) { + asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_OR_R32_TO_RM32); +} + +void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) { + asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_XOR_R32_TO_RM32); +} + +void asm_x86_shl_r32_cl(asm_x86_t *as, int dest_r32) { + asm_x86_generic_r32_r32(as, dest_r32, 4, OPCODE_SHL_RM32_CL); +} + +void asm_x86_shr_r32_cl(asm_x86_t *as, int dest_r32) { + asm_x86_generic_r32_r32(as, dest_r32, 5, OPCODE_SHR_RM32_CL); +} + +void asm_x86_sar_r32_cl(asm_x86_t *as, int dest_r32) { + asm_x86_generic_r32_r32(as, dest_r32, 7, OPCODE_SAR_RM32_CL); +} + +void asm_x86_add_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) { + asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_ADD_R32_TO_RM32); +} + +static void asm_x86_add_i32_to_r32(asm_x86_t *as, int src_i32, int dest_r32) { + if (SIGNED_FIT8(src_i32)) { + asm_x86_write_byte_2(as, OPCODE_ADD_I8_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32)); + asm_x86_write_byte_1(as, src_i32 & 0xff); + } else { + asm_x86_write_byte_2(as, OPCODE_ADD_I32_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32)); + asm_x86_write_word32(as, src_i32); + } +} + +void asm_x86_sub_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) { + asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_SUB_R32_FROM_RM32); +} + +static void asm_x86_sub_r32_i32(asm_x86_t *as, int dest_r32, int src_i32) { + if (SIGNED_FIT8(src_i32)) { + // defaults to 32 bit operation + asm_x86_write_byte_2(as, OPCODE_SUB_I8_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32)); + asm_x86_write_byte_1(as, src_i32 & 0xff); + } else { + // defaults to 32 bit operation + asm_x86_write_byte_2(as, OPCODE_SUB_I32_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32)); + asm_x86_write_word32(as, src_i32); + } +} + +void asm_x86_mul_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) { + // imul reg32, reg/mem32 -- 0x0f 0xaf /r + asm_x86_write_byte_3(as, 0x0f, 0xaf, MODRM_R32(dest_r32) | MODRM_RM_REG | MODRM_RM_R32(src_r32)); +} + +#if 0 +/* shifts not tested */ +void asm_x86_shl_r32_by_imm(asm_x86_t *as, int r32, int imm) { + asm_x86_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R32(4) | MODRM_RM_REG | MODRM_RM_R32(r32)); + asm_x86_write_byte_1(as, imm); +} + +void asm_x86_shr_r32_by_imm(asm_x86_t *as, int r32, int imm) { + asm_x86_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(r32)); + asm_x86_write_byte_1(as, imm); +} + +void asm_x86_sar_r32_by_imm(asm_x86_t *as, int r32, int imm) { + asm_x86_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(r32)); + asm_x86_write_byte_1(as, imm); +} +#endif + +void asm_x86_cmp_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b) { + asm_x86_generic_r32_r32(as, src_r32_b, src_r32_a, OPCODE_CMP_R32_WITH_RM32); +} + +#if 0 +void asm_x86_cmp_i32_with_r32(asm_x86_t *as, int src_i32, int src_r32) { + if (SIGNED_FIT8(src_i32)) { + asm_x86_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32)); + asm_x86_write_byte_1(as, src_i32 & 0xff); + } else { + asm_x86_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32)); + asm_x86_write_word32(as, src_i32); + } +} +#endif + +void asm_x86_test_r8_with_r8(asm_x86_t *as, int src_r32_a, int src_r32_b) { + asm_x86_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R32(src_r32_a) | MODRM_RM_REG | MODRM_RM_R32(src_r32_b)); +} + +void asm_x86_test_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b) { + asm_x86_generic_r32_r32(as, src_r32_b, src_r32_a, OPCODE_TEST_R32_WITH_RM32); +} + +void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8) { + asm_x86_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r8)); +} + +void asm_x86_jmp_reg(asm_x86_t *as, int src_r32) { + asm_x86_write_byte_2(as, OPCODE_JMP_RM32, MODRM_R32(4) | MODRM_RM_REG | MODRM_RM_R32(src_r32)); +} + +static mp_uint_t get_label_dest(asm_x86_t *as, mp_uint_t label) { + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; +} + +void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + if (dest != (mp_uint_t)-1 && rel < 0) { + // is a backwards jump, so we know the size of the jump on the first pass + // calculate rel assuming 8 bit relative jump + rel -= 2; + if (SIGNED_FIT8(rel)) { + asm_x86_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff); + } else { + rel += 2; + goto large_jump; + } + } else { + // is a forwards jump, so need to assume it's large + large_jump: + rel -= 5; + asm_x86_write_byte_1(as, OPCODE_JMP_REL32); + asm_x86_write_word32(as, rel); + } +} + +void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label) { + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + if (dest != (mp_uint_t)-1 && rel < 0) { + // is a backwards jump, so we know the size of the jump on the first pass + // calculate rel assuming 8 bit relative jump + rel -= 2; + if (SIGNED_FIT8(rel)) { + asm_x86_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff); + } else { + rel += 2; + goto large_jump; + } + } else { + // is a forwards jump, so need to assume it's large + large_jump: + rel -= 6; + asm_x86_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type); + asm_x86_write_word32(as, rel); + } +} + +void asm_x86_entry(asm_x86_t *as, int num_locals) { + assert(num_locals >= 0); + asm_x86_push_r32(as, ASM_X86_REG_EBP); + asm_x86_push_r32(as, ASM_X86_REG_EBX); + asm_x86_push_r32(as, ASM_X86_REG_ESI); + asm_x86_push_r32(as, ASM_X86_REG_EDI); + num_locals |= 3; // make it odd so stack is aligned on 16 byte boundary + asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, num_locals * WORD_SIZE); + as->num_locals = num_locals; +} + +void asm_x86_exit(asm_x86_t *as) { + asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, -as->num_locals * WORD_SIZE); + asm_x86_pop_r32(as, ASM_X86_REG_EDI); + asm_x86_pop_r32(as, ASM_X86_REG_ESI); + asm_x86_pop_r32(as, ASM_X86_REG_EBX); + asm_x86_pop_r32(as, ASM_X86_REG_EBP); + asm_x86_ret(as); +} + +static int asm_x86_arg_offset_from_esp(asm_x86_t *as, size_t arg_num) { + // Above esp are: locals, 4 saved registers, return eip, arguments + return (as->num_locals + 4 + 1 + arg_num) * WORD_SIZE; +} + +#if 0 +void asm_x86_push_arg(asm_x86_t *as, int src_arg_num) { + asm_x86_push_disp(as, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, src_arg_num)); +} +#endif + +void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32) { + asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, src_arg_num), dest_r32); +} + +#if 0 +void asm_x86_mov_r32_to_arg(asm_x86_t *as, int src_r32, int dest_arg_num) { + asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, dest_arg_num)); +} +#endif + +// locals: +// - stored on the stack in ascending order +// - numbered 0 through as->num_locals-1 +// - ESP points to the first local +// +// | ESP +// v +// l0 l1 l2 ... l(n-1) +// ^ ^ +// | low address | high address in RAM +// +static int asm_x86_local_offset_from_esp(asm_x86_t *as, int local_num) { + (void)as; + // Stack is full descending, ESP points to local0 + return local_num * WORD_SIZE; +} + +void asm_x86_mov_local_to_r32(asm_x86_t *as, int src_local_num, int dest_r32) { + asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(as, src_local_num), dest_r32); +} + +void asm_x86_mov_r32_to_local(asm_x86_t *as, int src_r32, int dest_local_num) { + asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(as, dest_local_num)); +} + +void asm_x86_mov_local_addr_to_r32(asm_x86_t *as, int local_num, int dest_r32) { + int offset = asm_x86_local_offset_from_esp(as, local_num); + if (offset == 0) { + asm_x86_mov_r32_r32(as, dest_r32, ASM_X86_REG_ESP); + } else { + asm_x86_lea_disp_to_r32(as, ASM_X86_REG_ESP, offset, dest_r32); + } +} + +void asm_x86_mov_reg_pcrel(asm_x86_t *as, int dest_r32, mp_uint_t label) { + asm_x86_write_byte_1(as, OPCODE_CALL_REL32); + asm_x86_write_word32(as, 0); + mp_uint_t dest = get_label_dest(as, label); + mp_int_t rel = dest - as->base.code_offset; + asm_x86_pop_r32(as, dest_r32); + // PC rel is usually a forward reference, so need to assume it's large + asm_x86_write_byte_2(as, OPCODE_ADD_I32_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32)); + asm_x86_write_word32(as, rel); +} + +#if 0 +void asm_x86_push_local(asm_x86_t *as, int local_num) { + asm_x86_push_disp(as, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(as, local_num)); +} + +void asm_x86_push_local_addr(asm_x86_t *as, int local_num, int temp_r32) { + asm_x86_mov_r32_r32(as, temp_r32, ASM_X86_REG_ESP); + asm_x86_add_i32_to_r32(as, asm_x86_local_offset_from_esp(as, local_num), temp_r32); + asm_x86_push_r32(as, temp_r32); +} +#endif + +void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r32) { + assert(n_args <= 4); + + // Align stack on 16-byte boundary during the call + unsigned int align = ((n_args + 3) & ~3) - n_args; + if (align) { + asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, align * WORD_SIZE); + } + + if (n_args > 3) { + asm_x86_push_r32(as, ASM_X86_REG_ARG_4); + } + if (n_args > 2) { + asm_x86_push_r32(as, ASM_X86_REG_ARG_3); + } + if (n_args > 1) { + asm_x86_push_r32(as, ASM_X86_REG_ARG_2); + } + if (n_args > 0) { + asm_x86_push_r32(as, ASM_X86_REG_ARG_1); + } + + // Load the pointer to the function and make the call + asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_FUN_TABLE, fun_id * WORD_SIZE, temp_r32); + asm_x86_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R32(2) | MODRM_RM_REG | MODRM_RM_R32(temp_r32)); + + // the caller must clean up the stack + if (n_args > 0) { + asm_x86_add_i32_to_r32(as, (n_args + align) * WORD_SIZE, ASM_X86_REG_ESP); + } +} + +#endif // MICROPY_EMIT_X86 diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmx86.h b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx86.h new file mode 100644 index 00000000..027d4415 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmx86.h @@ -0,0 +1,218 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_ASMX86_H +#define MICROPY_INCLUDED_PY_ASMX86_H + +#include "py/mpconfig.h" +#include "py/misc.h" +#include "py/asmbase.h" + +// x86 cdecl calling convention is: +// - args passed on the stack in reverse order +// - return value in EAX +// - caller cleans up the stack after a call +// - stack must be aligned to 16-byte boundary before all calls +// - EAX, ECX, EDX are caller-save +// - EBX, ESI, EDI, EBP, ESP, EIP are callee-save + +// In the functions below, argument order follows x86 docs and generally +// the destination is the first argument. +// NOTE: this is a change from the old convention used in this file and +// some functions still use the old (reverse) convention. + +#define ASM_X86_REG_EAX (0) +#define ASM_X86_REG_ECX (1) +#define ASM_X86_REG_EDX (2) +#define ASM_X86_REG_EBX (3) +#define ASM_X86_REG_ESP (4) +#define ASM_X86_REG_EBP (5) +#define ASM_X86_REG_ESI (6) +#define ASM_X86_REG_EDI (7) + +// x86 passes values on the stack, but the emitter is register based, so we need +// to define registers that can temporarily hold the function arguments. They +// need to be defined here so that asm_x86_call_ind can push them onto the stack +// before the call. +#define ASM_X86_REG_ARG_1 ASM_X86_REG_EAX +#define ASM_X86_REG_ARG_2 ASM_X86_REG_ECX +#define ASM_X86_REG_ARG_3 ASM_X86_REG_EDX +#define ASM_X86_REG_ARG_4 ASM_X86_REG_EBX + +// condition codes, used for jcc and setcc (despite their j-name!) +#define ASM_X86_CC_JB (0x2) // below, unsigned +#define ASM_X86_CC_JAE (0x3) // above or equal, unsigned +#define ASM_X86_CC_JZ (0x4) +#define ASM_X86_CC_JE (0x4) +#define ASM_X86_CC_JNZ (0x5) +#define ASM_X86_CC_JNE (0x5) +#define ASM_X86_CC_JBE (0x6) // below or equal, unsigned +#define ASM_X86_CC_JA (0x7) // above, unsigned +#define ASM_X86_CC_JL (0xc) // less, signed +#define ASM_X86_CC_JGE (0xd) // greater or equal, signed +#define ASM_X86_CC_JLE (0xe) // less or equal, signed +#define ASM_X86_CC_JG (0xf) // greater, signed + +typedef struct _asm_x86_t { + mp_asm_base_t base; + int num_locals; +} asm_x86_t; + +static inline void asm_x86_end_pass(asm_x86_t *as) { + (void)as; +} + +void asm_x86_mov_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); +size_t asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32); +void asm_x86_mov_r8_to_mem8(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp); +void asm_x86_mov_r16_to_mem16(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp); +void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp); +void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32); +void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32); +void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32); +void asm_x86_not_r32(asm_x86_t *as, int dest_r32); +void asm_x86_neg_r32(asm_x86_t *as, int dest_r32); +void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); +void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); +void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); +void asm_x86_shl_r32_cl(asm_x86_t *as, int dest_r32); +void asm_x86_shr_r32_cl(asm_x86_t *as, int dest_r32); +void asm_x86_sar_r32_cl(asm_x86_t *as, int dest_r32); +void asm_x86_add_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); +void asm_x86_sub_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); +void asm_x86_mul_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); +void asm_x86_cmp_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b); +void asm_x86_test_r8_with_r8(asm_x86_t *as, int src_r32_a, int src_r32_b); +void asm_x86_test_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b); +void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8); +void asm_x86_jmp_reg(asm_x86_t *as, int src_r86); +void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label); +void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label); +void asm_x86_entry(asm_x86_t *as, int num_locals); +void asm_x86_exit(asm_x86_t *as); +void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32); +void asm_x86_mov_local_to_r32(asm_x86_t *as, int src_local_num, int dest_r32); +void asm_x86_mov_r32_to_local(asm_x86_t *as, int src_r32, int dest_local_num); +void asm_x86_mov_local_addr_to_r32(asm_x86_t *as, int local_num, int dest_r32); +void asm_x86_mov_reg_pcrel(asm_x86_t *as, int dest_r64, mp_uint_t label); +void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r32); + +// Holds a pointer to mp_fun_table +#define ASM_X86_REG_FUN_TABLE ASM_X86_REG_EBP + +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (4) + +#define REG_RET ASM_X86_REG_EAX +#define REG_ARG_1 ASM_X86_REG_ARG_1 +#define REG_ARG_2 ASM_X86_REG_ARG_2 +#define REG_ARG_3 ASM_X86_REG_ARG_3 +#define REG_ARG_4 ASM_X86_REG_ARG_4 + +// caller-save, so can be used as temporaries +#define REG_TEMP0 ASM_X86_REG_EAX +#define REG_TEMP1 ASM_X86_REG_ECX +#define REG_TEMP2 ASM_X86_REG_EDX + +// callee-save, so can be used as locals +#define REG_LOCAL_1 ASM_X86_REG_EBX +#define REG_LOCAL_2 ASM_X86_REG_ESI +#define REG_LOCAL_3 ASM_X86_REG_EDI +#define REG_LOCAL_NUM (3) + +// Holds a pointer to mp_fun_table +#define REG_FUN_TABLE ASM_X86_REG_FUN_TABLE + +#define ASM_T asm_x86_t +#define ASM_END_PASS asm_x86_end_pass +#define ASM_ENTRY asm_x86_entry +#define ASM_EXIT asm_x86_exit + +#define ASM_JUMP asm_x86_jmp_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ + do { \ + if (bool_test) { \ + asm_x86_test_r8_with_r8(as, reg, reg); \ + } else { \ + asm_x86_test_r32_with_r32(as, reg, reg); \ + } \ + asm_x86_jcc_label(as, ASM_X86_CC_JZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \ + do { \ + if (bool_test) { \ + asm_x86_test_r8_with_r8(as, reg, reg); \ + } else { \ + asm_x86_test_r32_with_r32(as, reg, reg); \ + } \ + asm_x86_jcc_label(as, ASM_X86_CC_JNZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_x86_cmp_r32_with_r32(as, reg1, reg2); \ + asm_x86_jcc_label(as, ASM_X86_CC_JE, label); \ + } while (0) +#define ASM_JUMP_REG(as, reg) asm_x86_jmp_reg((as), (reg)) +#define ASM_CALL_IND(as, idx) asm_x86_call_ind(as, idx, mp_f_n_args[idx], ASM_X86_REG_EAX) + +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x86_mov_r32_to_local((as), (reg_src), (local_num)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x86_mov_local_to_r32((as), (local_num), (reg_dest)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x86_mov_local_addr_to_r32((as), (local_num), (reg_dest)) +#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x86_mov_reg_pcrel((as), (reg_dest), (label)) + +#define ASM_NOT_REG(as, reg) asm_x86_not_r32((as), (reg)) +#define ASM_NEG_REG(as, reg) asm_x86_neg_r32((as), (reg)) +#define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg)) +#define ASM_LSR_REG(as, reg) asm_x86_shr_r32_cl((as), (reg)) +#define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x86_or_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x86_xor_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x86_and_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x86_add_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x86_sub_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x86_mul_r32_r32((as), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (word_offset), (reg_dest)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem8_to_r32zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 2 * (uint16_offset), (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) + +#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + +#endif // MICROPY_INCLUDED_PY_ASMX86_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmxtensa.c b/non_catalog_apps/mp_flipper/lib/micropython/py/asmxtensa.c new file mode 100644 index 00000000..0fbe351d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmxtensa.c @@ -0,0 +1,267 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 "py/runtime.h" + +// wrapper around everything in this file +#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN + +#include "py/asmxtensa.h" + +#define WORD_SIZE (4) +#define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)) +#define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)) + +void asm_xtensa_end_pass(asm_xtensa_t *as) { + as->num_const = as->cur_const; + as->cur_const = 0; + + #if 0 + // make a hex dump of the machine code + if (as->base.pass == MP_ASM_PASS_EMIT) { + uint8_t *d = as->base.code_base; + printf("XTENSA ASM:"); + for (int i = 0; i < ((as->base.code_size + 15) & ~15); ++i) { + if (i % 16 == 0) { + printf("\n%08x:", (uint32_t)&d[i]); + } + if (i % 2 == 0) { + printf(" "); + } + printf("%02x", d[i]); + } + printf("\n"); + } + #endif +} + +void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) { + // jump over the constants + asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); + mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte + as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + + // adjust the stack-pointer to store a0, a12, a13, a14, a15 and locals, 16-byte aligned + as->stack_adjust = (((ASM_XTENSA_NUM_REGS_SAVED + num_locals) * WORD_SIZE) + 15) & ~15; + if (SIGNED_FIT8(-as->stack_adjust)) { + asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, -as->stack_adjust); + } else { + asm_xtensa_op_movi(as, ASM_XTENSA_REG_A9, as->stack_adjust); + asm_xtensa_op_sub(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A9); + } + + // save return value (a0) and callee-save registers (a12, a13, a14, a15) + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); + for (int i = 1; i < ASM_XTENSA_NUM_REGS_SAVED; ++i) { + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i); + } +} + +void asm_xtensa_exit(asm_xtensa_t *as) { + // restore registers + for (int i = ASM_XTENSA_NUM_REGS_SAVED - 1; i >= 1; --i) { + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i); + } + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); + + // restore stack-pointer and return + if (SIGNED_FIT8(as->stack_adjust)) { + asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, as->stack_adjust); + } else { + asm_xtensa_op_movi(as, ASM_XTENSA_REG_A9, as->stack_adjust); + asm_xtensa_op_add_n(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A9); + } + + asm_xtensa_op_ret_n(as); +} + +void asm_xtensa_entry_win(asm_xtensa_t *as, int num_locals) { + // jump over the constants + asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); + mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte + as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + + as->stack_adjust = 32 + ((((ASM_XTENSA_NUM_REGS_SAVED_WIN + num_locals) * WORD_SIZE) + 15) & ~15); + asm_xtensa_op_entry(as, ASM_XTENSA_REG_A1, as->stack_adjust); + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); +} + +void asm_xtensa_exit_win(asm_xtensa_t *as) { + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); + asm_xtensa_op_retw_n(as); +} + +static uint32_t get_label_dest(asm_xtensa_t *as, uint label) { + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; +} + +void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op) { + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2); + if (c != NULL) { + c[0] = op; + c[1] = op >> 8; + } +} + +void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op) { + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3); + if (c != NULL) { + c[0] = op; + c[1] = op >> 8; + c[2] = op >> 16; + } +} + +void asm_xtensa_j_label(asm_xtensa_t *as, uint label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + // we assume rel, as a signed int, fits in 18-bits + asm_xtensa_op_j(as, rel); +} + +void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT12(rel)) { + printf("ERROR: xtensa bccz out of range\n"); + } + asm_xtensa_op_bccz(as, cond, reg, rel); +} + +void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) { + printf("ERROR: xtensa bcc out of range\n"); + } + asm_xtensa_op_bcc(as, cond, reg1, reg2, rel); +} + +// convenience function; reg_dest must be different from reg_src[12] +void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2) { + asm_xtensa_op_movi_n(as, reg_dest, 1); + asm_xtensa_op_bcc(as, cond, reg_src1, reg_src2, 1); + asm_xtensa_op_movi_n(as, reg_dest, 0); +} + +size_t asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) { + // load the constant + uint32_t const_table_offset = (uint8_t *)as->const_table - as->base.code_base; + size_t loc = const_table_offset + as->cur_const * WORD_SIZE; + asm_xtensa_op_l32r(as, reg_dest, as->base.code_offset, loc); + // store the constant in the table + if (as->const_table != NULL) { + as->const_table[as->cur_const] = i32; + } + ++as->cur_const; + return loc; +} + +void asm_xtensa_mov_reg_i32_optimised(asm_xtensa_t *as, uint reg_dest, uint32_t i32) { + if (-32 <= (int)i32 && (int)i32 <= 95) { + asm_xtensa_op_movi_n(as, reg_dest, i32); + } else if (SIGNED_FIT12(i32)) { + asm_xtensa_op_movi(as, reg_dest, i32); + } else { + asm_xtensa_mov_reg_i32(as, reg_dest, i32); + } +} + +void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src) { + asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, local_num); +} + +void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num) { + asm_xtensa_op_l32i(as, reg_dest, ASM_XTENSA_REG_A1, local_num); +} + +void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num) { + uint off = local_num * WORD_SIZE; + if (SIGNED_FIT8(off)) { + asm_xtensa_op_addi(as, reg_dest, ASM_XTENSA_REG_A1, off); + } else { + asm_xtensa_op_movi(as, reg_dest, off); + asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A1); + } +} + +void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label) { + // Get relative offset from PC + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset; + rel -= 3 + 3; // account for 3 bytes of movi instruction, 3 bytes call0 adjustment + asm_xtensa_op_movi(as, reg_dest, rel); // imm has 12-bit range + + // Use call0 to get PC+3 into a0 + // call0 destination must be aligned on 4 bytes: + // - code_offset&3=0: off=0, pad=1 + // - code_offset&3=1: off=0, pad=0 + // - code_offset&3=2: off=1, pad=3 + // - code_offset&3=3: off=1, pad=2 + uint32_t off = as->base.code_offset >> 1 & 1; + uint32_t pad = (5 - as->base.code_offset) & 3; + asm_xtensa_op_call0(as, off); + mp_asm_base_get_cur_to_write_bytes(&as->base, pad); + + // Add PC to relative offset + asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A0); +} + +void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) { + if (word_offset < 16) { + asm_xtensa_op_l32i_n(as, reg_dest, reg_base, word_offset); + } else if (word_offset < 256) { + asm_xtensa_op_l32i(as, reg_dest, reg_base, word_offset); + } else { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); + } +} + +void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { + if (word_offset < 16) { + asm_xtensa_op_s32i_n(as, reg_src, reg_base, word_offset); + } else if (word_offset < 256) { + asm_xtensa_op_s32i(as, reg_src, reg_base, word_offset); + } else { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); + } +} + +void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx) { + asm_xtensa_l32i_optimised(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx); + asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0); +} + +void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx) { + asm_xtensa_l32i_optimised(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx); + asm_xtensa_op_callx8(as, ASM_XTENSA_REG_A8); +} + +#endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/asmxtensa.h b/non_catalog_apps/mp_flipper/lib/micropython/py/asmxtensa.h new file mode 100644 index 00000000..f226624a --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/asmxtensa.h @@ -0,0 +1,415 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_ASMXTENSA_H +#define MICROPY_INCLUDED_PY_ASMXTENSA_H + +#include "py/misc.h" +#include "py/asmbase.h" + +// calling conventions: +// up to 6 args in a2-a7 +// return value in a2 +// PC stored in a0 +// stack pointer is a1, stack full descending, is aligned to 16 bytes +// callee save: a1, a12, a13, a14, a15 +// caller save: a3 + +// With windowed registers, size 8: +// - a0: return PC +// - a1: stack pointer, full descending, aligned to 16 bytes +// - a2-a7: incoming args, and essentially callee save +// - a2: return value +// - a8-a15: caller save temporaries +// - a10-a15: input args to called function +// - a10: return value of called function +// note: a0-a7 are saved automatically via window shift of called function + +#define ASM_XTENSA_REG_A0 (0) +#define ASM_XTENSA_REG_A1 (1) +#define ASM_XTENSA_REG_A2 (2) +#define ASM_XTENSA_REG_A3 (3) +#define ASM_XTENSA_REG_A4 (4) +#define ASM_XTENSA_REG_A5 (5) +#define ASM_XTENSA_REG_A6 (6) +#define ASM_XTENSA_REG_A7 (7) +#define ASM_XTENSA_REG_A8 (8) +#define ASM_XTENSA_REG_A9 (9) +#define ASM_XTENSA_REG_A10 (10) +#define ASM_XTENSA_REG_A11 (11) +#define ASM_XTENSA_REG_A12 (12) +#define ASM_XTENSA_REG_A13 (13) +#define ASM_XTENSA_REG_A14 (14) +#define ASM_XTENSA_REG_A15 (15) + +// for bccz +#define ASM_XTENSA_CCZ_EQ (0) +#define ASM_XTENSA_CCZ_NE (1) + +// for bcc and setcc +#define ASM_XTENSA_CC_NONE (0) +#define ASM_XTENSA_CC_EQ (1) +#define ASM_XTENSA_CC_LT (2) +#define ASM_XTENSA_CC_LTU (3) +#define ASM_XTENSA_CC_ALL (4) +#define ASM_XTENSA_CC_BC (5) +#define ASM_XTENSA_CC_ANY (8) +#define ASM_XTENSA_CC_NE (9) +#define ASM_XTENSA_CC_GE (10) +#define ASM_XTENSA_CC_GEU (11) +#define ASM_XTENSA_CC_NALL (12) +#define ASM_XTENSA_CC_BS (13) + +// macros for encoding instructions (little endian versions) +#define ASM_XTENSA_ENCODE_RRR(op0, op1, op2, r, s, t) \ + ((((uint32_t)op2) << 20) | (((uint32_t)op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RRI4(op0, op1, r, s, t, imm4) \ + (((imm4) << 20) | ((op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RRI8(op0, r, s, t, imm8) \ + ((((uint32_t)imm8) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RI16(op0, t, imm16) \ + (((imm16) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RSR(op0, op1, op2, rs, t) \ + (((op2) << 20) | ((op1) << 16) | ((rs) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_CALL(op0, n, offset) \ + (((offset) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_CALLX(op0, op1, op2, r, s, m, n) \ + ((((uint32_t)op2) << 20) | (((uint32_t)op1) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_BRI8(op0, r, s, m, n, imm8) \ + (((imm8) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_BRI12(op0, s, m, n, imm12) \ + (((imm12) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RRRN(op0, r, s, t) \ + (((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RI7(op0, s, imm7) \ + ((((imm7) & 0xf) << 12) | ((s) << 8) | ((imm7) & 0x70) | (op0)) + +// Number of registers saved on the stack upon entry to function +#define ASM_XTENSA_NUM_REGS_SAVED (5) +#define ASM_XTENSA_NUM_REGS_SAVED_WIN (1) + +typedef struct _asm_xtensa_t { + mp_asm_base_t base; + uint32_t cur_const; + uint32_t num_const; + uint32_t *const_table; + uint32_t stack_adjust; +} asm_xtensa_t; + +void asm_xtensa_end_pass(asm_xtensa_t *as); + +void asm_xtensa_entry(asm_xtensa_t *as, int num_locals); +void asm_xtensa_exit(asm_xtensa_t *as); + +void asm_xtensa_entry_win(asm_xtensa_t *as, int num_locals); +void asm_xtensa_exit_win(asm_xtensa_t *as); + +void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op); +void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op); + +// raw instructions + +static inline void asm_xtensa_op_entry(asm_xtensa_t *as, uint reg_src, int32_t num_bytes) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, 0, 3, (num_bytes / 8) & 0xfff)); +} + +static inline void asm_xtensa_op_add_n(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(10, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_addi(asm_xtensa_t *as, uint reg_dest, uint reg_src, int imm8) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 12, reg_src, reg_dest, imm8 & 0xff)); +} + +static inline void asm_xtensa_op_and(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 1, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_bcc(asm_xtensa_t *as, uint cond, uint reg_src1, uint reg_src2, int32_t rel8) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(7, cond, reg_src1, reg_src2, rel8 & 0xff)); +} + +static inline void asm_xtensa_op_bccz(asm_xtensa_t *as, uint cond, uint reg_src, int32_t rel12) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, cond, 1, rel12 & 0xfff)); +} + +static inline void asm_xtensa_op_call0(asm_xtensa_t *as, int32_t rel18) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(5, 0, rel18 & 0x3ffff)); +} + +static inline void asm_xtensa_op_callx0(asm_xtensa_t *as, uint reg) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 0)); +} + +static inline void asm_xtensa_op_callx8(asm_xtensa_t *as, uint reg) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 2)); +} + +static inline void asm_xtensa_op_j(asm_xtensa_t *as, int32_t rel18) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(6, 0, rel18 & 0x3ffff)); +} + +static inline void asm_xtensa_op_jx(asm_xtensa_t *as, uint reg) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 2, 2)); +} + +static inline void asm_xtensa_op_l8ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint byte_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0, reg_base, reg_dest, byte_offset & 0xff)); +} + +static inline void asm_xtensa_op_l16ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint half_word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 1, reg_base, reg_dest, half_word_offset & 0xff)); +} + +static inline void asm_xtensa_op_l32i(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 2, reg_base, reg_dest, word_offset & 0xff)); +} + +static inline void asm_xtensa_op_l32i_n(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(8, word_offset & 0xf, reg_base, reg_dest)); +} + +static inline void asm_xtensa_op_l32r(asm_xtensa_t *as, uint reg_dest, uint32_t op_off, uint32_t dest_off) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RI16(1, reg_dest, ((dest_off - ((op_off + 3) & ~3)) >> 2) & 0xffff)); +} + +static inline void asm_xtensa_op_mov_n(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 0, reg_src, reg_dest)); +} + +static inline void asm_xtensa_op_movi(asm_xtensa_t *as, uint reg_dest, int32_t imm12) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 10, (imm12 >> 8) & 0xf, reg_dest, imm12 & 0xff)); +} + +// Argument must be in the range (-32 .. 95) inclusive. +static inline void asm_xtensa_op_movi_n(asm_xtensa_t *as, uint reg_dest, int imm7) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RI7(12, reg_dest, imm7)); +} + +static inline void asm_xtensa_op_mull(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 2, 8, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_neg(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 6, reg_dest, 0, reg_src)); +} + +static inline void asm_xtensa_op_or(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 2, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_ret_n(asm_xtensa_t *as) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 0)); +} + +static inline void asm_xtensa_op_retw_n(asm_xtensa_t *as) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 1)); +} + +static inline void asm_xtensa_op_s8i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint byte_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 4, reg_base, reg_src, byte_offset & 0xff)); +} + +static inline void asm_xtensa_op_s16i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint half_word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 5, reg_base, reg_src, half_word_offset & 0xff)); +} + +static inline void asm_xtensa_op_s32i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 6, reg_base, reg_src, word_offset & 0xff)); +} + +static inline void asm_xtensa_op_s32i_n(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(9, word_offset & 0xf, reg_base, reg_src)); +} + +static inline void asm_xtensa_op_sll(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 10, reg_dest, reg_src, 0)); +} + +static inline void asm_xtensa_op_srl(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 9, reg_dest, 0, reg_src)); +} + +static inline void asm_xtensa_op_sra(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 11, reg_dest, 0, reg_src)); +} + +static inline void asm_xtensa_op_ssl(asm_xtensa_t *as, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 1, reg_src, 0)); +} + +static inline void asm_xtensa_op_ssr(asm_xtensa_t *as, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 0, reg_src, 0)); +} + +static inline void asm_xtensa_op_sub(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 12, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_xor(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 3, reg_dest, reg_src_a, reg_src_b)); +} + +// convenience functions +void asm_xtensa_j_label(asm_xtensa_t *as, uint label); +void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label); +void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label); +void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2); +size_t asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32); +void asm_xtensa_mov_reg_i32_optimised(asm_xtensa_t *as, uint reg_dest, uint32_t i32); +void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src); +void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num); +void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num); +void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label); +void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset); +void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset); +void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); +void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); + +// Holds a pointer to mp_fun_table +#define ASM_XTENSA_REG_FUN_TABLE ASM_XTENSA_REG_A15 +#define ASM_XTENSA_REG_FUN_TABLE_WIN ASM_XTENSA_REG_A7 + +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (4) + +#if !GENERIC_ASM_API_WIN +// Configuration for non-windowed calls + +#define REG_RET ASM_XTENSA_REG_A2 +#define REG_ARG_1 ASM_XTENSA_REG_A2 +#define REG_ARG_2 ASM_XTENSA_REG_A3 +#define REG_ARG_3 ASM_XTENSA_REG_A4 +#define REG_ARG_4 ASM_XTENSA_REG_A5 +#define REG_ARG_5 ASM_XTENSA_REG_A6 + +#define REG_TEMP0 ASM_XTENSA_REG_A2 +#define REG_TEMP1 ASM_XTENSA_REG_A3 +#define REG_TEMP2 ASM_XTENSA_REG_A4 + +#define REG_LOCAL_1 ASM_XTENSA_REG_A12 +#define REG_LOCAL_2 ASM_XTENSA_REG_A13 +#define REG_LOCAL_3 ASM_XTENSA_REG_A14 +#define REG_LOCAL_NUM (3) + +#define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED +#define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE + +#define ASM_ENTRY(as, nlocal) asm_xtensa_entry((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx)) + +#else +// Configuration for windowed calls with window size 8 + +#define REG_PARENT_RET ASM_XTENSA_REG_A2 +#define REG_PARENT_ARG_1 ASM_XTENSA_REG_A2 +#define REG_PARENT_ARG_2 ASM_XTENSA_REG_A3 +#define REG_PARENT_ARG_3 ASM_XTENSA_REG_A4 +#define REG_PARENT_ARG_4 ASM_XTENSA_REG_A5 +#define REG_RET ASM_XTENSA_REG_A10 +#define REG_ARG_1 ASM_XTENSA_REG_A10 +#define REG_ARG_2 ASM_XTENSA_REG_A11 +#define REG_ARG_3 ASM_XTENSA_REG_A12 +#define REG_ARG_4 ASM_XTENSA_REG_A13 + +#define REG_TEMP0 ASM_XTENSA_REG_A10 +#define REG_TEMP1 ASM_XTENSA_REG_A11 +#define REG_TEMP2 ASM_XTENSA_REG_A12 + +#define REG_LOCAL_1 ASM_XTENSA_REG_A4 +#define REG_LOCAL_2 ASM_XTENSA_REG_A5 +#define REG_LOCAL_3 ASM_XTENSA_REG_A6 +#define REG_LOCAL_NUM (3) + +#define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED_WIN +#define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE_WIN + +#define ASM_ENTRY(as, nlocal) asm_xtensa_entry_win((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit_win((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind_win((as), (idx)) + +#endif + +#define ASM_T asm_xtensa_t +#define ASM_END_PASS asm_xtensa_end_pass + +#define ASM_JUMP asm_xtensa_j_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ + asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_EQ, reg, label) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \ + asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_NE, reg, label) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + asm_xtensa_bcc_reg_reg_label(as, ASM_XTENSA_CC_EQ, reg1, reg2, label) +#define ASM_JUMP_REG(as, reg) asm_xtensa_op_jx((as), (reg)) + +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), ASM_NUM_REGS_SAVED + (local_num), (reg_src)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32_optimised((as), (reg_dest), (imm)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num)) +#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_xtensa_mov_reg_pcrel((as), (reg_dest), (label)) + +#define ASM_NEG_REG(as, reg_dest) asm_xtensa_op_neg((as), (reg_dest), (reg_dest)) +#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \ + do { \ + asm_xtensa_op_ssl((as), (reg_shift)); \ + asm_xtensa_op_sll((as), (reg_dest), (reg_dest)); \ + } while (0) +#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) \ + do { \ + asm_xtensa_op_ssr((as), (reg_shift)); \ + asm_xtensa_op_srl((as), (reg_dest), (reg_dest)); \ + } while (0) +#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) \ + do { \ + asm_xtensa_op_ssr((as), (reg_shift)); \ + asm_xtensa_op_sra((as), (reg_dest), (reg_dest)); \ + } while (0) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_or((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_xor((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_and((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_add_n((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_sub((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mull((as), (reg_dest), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_l32i_optimised((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), (uint16_offset)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_s32i_optimised((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s8i((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s16i((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + +#endif // MICROPY_INCLUDED_PY_ASMXTENSA_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/bc.c b/non_catalog_apps/mp_flipper/lib/micropython/py/bc.c new file mode 100644 index 00000000..899dbd6a --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/bc.c @@ -0,0 +1,345 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/bc0.h" +#include "py/bc.h" +#include "py/objfun.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + +void mp_encode_uint(void *env, mp_encode_uint_allocator_t allocator, mp_uint_t val) { + // We store each 7 bits in a separate byte, and that's how many bytes needed + byte buf[MP_ENCODE_UINT_MAX_BYTES]; + byte *p = buf + sizeof(buf); + // We encode in little-ending order, but store in big-endian, to help decoding + do { + *--p = val & 0x7f; + val >>= 7; + } while (val != 0); + byte *c = allocator(env, buf + sizeof(buf) - p); + if (c != NULL) { + while (p != buf + sizeof(buf) - 1) { + *c++ = *p++ | 0x80; + } + *c = *p; + } +} + +mp_uint_t mp_decode_uint(const byte **ptr) { + mp_uint_t unum = 0; + byte val; + const byte *p = *ptr; + do { + val = *p++; + unum = (unum << 7) | (val & 0x7f); + } while ((val & 0x80) != 0); + *ptr = p; + return unum; +} + +// This function is used to help reduce stack usage at the caller, for the case when +// the caller doesn't need to increase the ptr argument. If ptr is a local variable +// and the caller uses mp_decode_uint(&ptr) instead of this function, then the compiler +// must allocate a slot on the stack for ptr, and this slot cannot be reused for +// anything else in the function because the pointer may have been stored in a global +// and reused later in the function. +mp_uint_t mp_decode_uint_value(const byte *ptr) { + return mp_decode_uint(&ptr); +} + +// This function is used to help reduce stack usage at the caller, for the case when +// the caller doesn't need the actual value and just wants to skip over it. +const byte *mp_decode_uint_skip(const byte *ptr) { + while ((*ptr++) & 0x80) { + } + return ptr; +} + +static NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // generic message, used also for other argument issues + (void)f; + (void)expected; + (void)given; + mp_arg_error_terse_mismatch(); + #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL + (void)f; + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), expected, given); + #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("%q() takes %d positional arguments but %d were given"), + mp_obj_fun_get_name(MP_OBJ_FROM_PTR(f)), expected, given); + #endif +} + +#if DEBUG_PRINT +static void dump_args(const mp_obj_t *a, size_t sz) { + DEBUG_printf("%p: ", a); + for (size_t i = 0; i < sz; i++) { + DEBUG_printf("%p ", a[i]); + } + DEBUG_printf("\n"); +} +#else +#define dump_args(...) (void)0 +#endif + +// On entry code_state should be allocated somewhere (stack/heap) and +// contain the following valid entries: +// - code_state->fun_bc should contain a pointer to the function object +// - code_state->ip should contain a pointer to the beginning of the prelude +// - code_state->sp should be: &code_state->state[0] - 1 +// - code_state->n_state should be the number of objects in the local state +static void mp_setup_code_state_helper(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // This function is pretty complicated. It's main aim is to be efficient in speed and RAM + // usage for the common case of positional only args. + + // get the function object that we want to set up (could be bytecode or native code) + mp_obj_fun_bc_t *self = code_state->fun_bc; + + // Get cached n_state (rather than decode it again) + size_t n_state = code_state->n_state; + + // Decode prelude + size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; + MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); + MP_BC_PRELUDE_SIZE_DECODE(code_state->ip); + (void)n_state_unused; + (void)n_exc_stack_unused; + + mp_obj_t *code_state_state = code_state->sp + 1; + code_state->exc_sp_idx = 0; + + // zero out the local stack to begin with + memset(code_state_state, 0, n_state * sizeof(*code_state->state)); + + const mp_obj_t *kwargs = args + n_args; + + // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed) + mp_obj_t *var_pos_kw_args = &code_state_state[n_state - 1 - n_pos_args - n_kwonly_args]; + + // check positional arguments + + if (n_args > n_pos_args) { + // given more than enough arguments + if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) { + fun_pos_args_mismatch(self, n_pos_args, n_args); + } + // put extra arguments in varargs tuple + *var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args); + n_args = n_pos_args; + } else { + if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { + DEBUG_printf("passing empty tuple as *args\n"); + *var_pos_kw_args-- = mp_const_empty_tuple; + } + // Apply processing and check below only if we don't have kwargs, + // otherwise, kw handling code below has own extensive checks. + if (n_kw == 0 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) { + if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) { + // given enough arguments, but may need to use some default arguments + for (size_t i = n_args; i < n_pos_args; i++) { + code_state_state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)]; + } + } else { + fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args); + } + } + } + + // copy positional args into state + for (size_t i = 0; i < n_args; i++) { + code_state_state[n_state - 1 - i] = args[i]; + } + + // check keyword arguments + + if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { + DEBUG_printf("Initial args: "); + dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + + mp_obj_t dict = MP_OBJ_NULL; + if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { + dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0? + *var_pos_kw_args = dict; + } + + for (size_t i = 0; i < n_kw; i++) { + // the keys in kwargs are expected to be qstr objects + mp_obj_t wanted_arg_name = kwargs[2 * i]; + + // get pointer to arg_names array + const uint8_t *arg_names = code_state->ip; + arg_names = mp_decode_uint_skip(arg_names); + + for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) { + qstr arg_qstr = mp_decode_uint(&arg_names); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + arg_qstr = self->context->constants.qstr_table[arg_qstr]; + #endif + if (wanted_arg_name == MP_OBJ_NEW_QSTR(arg_qstr)) { + if (code_state_state[n_state - 1 - j] != MP_OBJ_NULL) { + error_multiple: + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function got multiple values for argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name)); + } + code_state_state[n_state - 1 - j] = kwargs[2 * i + 1]; + goto continue2; + } + } + // Didn't find name match with positional args + if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("unexpected keyword argument")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("unexpected keyword argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name)); + #endif + } + mp_map_elem_t *elem = mp_map_lookup(mp_obj_dict_get_map(dict), wanted_arg_name, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem->value == MP_OBJ_NULL) { + elem->value = kwargs[2 * i + 1]; + } else { + goto error_multiple; + } + continue2:; + } + + DEBUG_printf("Args with kws flattened: "); + dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + + // fill in defaults for positional args + mp_obj_t *d = &code_state_state[n_state - n_pos_args]; + mp_obj_t *s = &self->extra_args[n_def_pos_args - 1]; + for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) { + if (*d == MP_OBJ_NULL) { + *d = *s; + } + } + + DEBUG_printf("Args after filling default positional: "); + dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + + // Check that all mandatory positional args are specified + while (d < &code_state_state[n_state]) { + if (*d++ == MP_OBJ_NULL) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function missing required positional argument #%d"), &code_state_state[n_state] - d); + } + } + + // Check that all mandatory keyword args are specified + // Fill in default kw args if we have them + const uint8_t *arg_names = mp_decode_uint_skip(code_state->ip); + for (size_t i = 0; i < n_pos_args; i++) { + arg_names = mp_decode_uint_skip(arg_names); + } + for (size_t i = 0; i < n_kwonly_args; i++) { + qstr arg_qstr = mp_decode_uint(&arg_names); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + arg_qstr = self->context->constants.qstr_table[arg_qstr]; + #endif + if (code_state_state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) { + mp_map_elem_t *elem = NULL; + if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { + elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, MP_OBJ_NEW_QSTR(arg_qstr), MP_MAP_LOOKUP); + } + if (elem != NULL) { + code_state_state[n_state - 1 - n_pos_args - i] = elem->value; + } else { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function missing required keyword argument '%q'"), arg_qstr); + } + } + } + + } else { + // no keyword arguments given + if (n_kwonly_args != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("function missing keyword-only argument")); + } + if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { + *var_pos_kw_args = mp_obj_new_dict(0); + } + } + + // jump over code info (source file, argument names and line-number mapping) + const uint8_t *ip = code_state->ip + n_info; + + // bytecode prelude: initialise closed over variables + for (; n_cell; --n_cell) { + size_t local_num = *ip++; + code_state_state[n_state - 1 - local_num] = + mp_obj_new_cell(code_state_state[n_state - 1 - local_num]); + } + + // now that we skipped over the prelude, set the ip for the VM + code_state->ip = ip; + + DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args); + dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); + dump_args(code_state_state, n_state); +} + +// On entry code_state should be allocated somewhere (stack/heap) and +// contain the following valid entries: +// - code_state->fun_bc should contain a pointer to the function object +// - code_state->n_state should be the number of objects in the local state +void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) { + code_state->ip = code_state->fun_bc->bytecode; + code_state->sp = &code_state->state[0] - 1; + #if MICROPY_STACKLESS + code_state->prev = NULL; + #endif + #if MICROPY_PY_SYS_SETTRACE + code_state->prev_state = NULL; + code_state->frame = NULL; + #endif + mp_setup_code_state_helper(code_state, n_args, n_kw, args); +} + +#if MICROPY_EMIT_NATIVE +// On entry code_state should be allocated somewhere (stack/heap) and +// contain the following valid entries: +// - code_state->fun_bc should contain a pointer to the function object +// - code_state->n_state should be the number of objects in the local state +void mp_setup_code_state_native(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) { + code_state->ip = mp_obj_fun_native_get_prelude_ptr(code_state->fun_bc); + code_state->sp = &code_state->state[0] - 1; + mp_setup_code_state_helper((mp_code_state_t *)code_state, n_args, n_kw, args); +} +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/bc.h b/non_catalog_apps/mp_flipper/lib/micropython/py/bc.h new file mode 100644 index 00000000..718ba4a6 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/bc.h @@ -0,0 +1,338 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_BC_H +#define MICROPY_INCLUDED_PY_BC_H + +#include "py/runtime.h" + +// bytecode layout: +// +// func signature : var uint +// contains six values interleaved bit-wise as: xSSSSEAA [xFSSKAED repeated] +// x = extension another byte follows +// S = n_state - 1 number of entries in Python value stack +// E = n_exc_stack number of entries in exception stack +// F = scope_flags four bits of flags, MP_SCOPE_FLAG_xxx +// A = n_pos_args number of arguments this function takes +// K = n_kwonly_args number of keyword-only arguments this function takes +// D = n_def_pos_args number of default positional arguments +// +// prelude size : var uint +// contains two values interleaved bit-wise as: xIIIIIIC repeated +// x = extension another byte follows +// I = n_info number of bytes in source info section (always > 0) +// C = n_cells number of bytes/cells in closure section +// +// source info section: +// simple_name : var qstr always exists +// argname0 : var qstr +// ... : var qstr +// argnameN : var qstr N = num_pos_args + num_kwonly_args - 1 +// +// +// closure section: +// local_num0 : byte +// ... : byte +// local_numN : byte N = n_cells-1 +// +// +// +// +// constant table layout: +// +// const0 : obj +// constN : obj + +#define MP_ENCODE_UINT_MAX_BYTES ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7) + +#define MP_BC_PRELUDE_SIG_ENCODE(S, E, scope, out_byte, out_env) \ + do { \ + /*// Get values to store in prelude */ \ + size_t F = scope->scope_flags & MP_SCOPE_FLAG_ALL_SIG; \ + size_t A = scope->num_pos_args; \ + size_t K = scope->num_kwonly_args; \ + size_t D = scope->num_def_pos_args; \ + \ + /* Adjust S to shrink range, to compress better */ \ + S -= 1; \ + \ + /* Encode prelude */ \ + /* xSSSSEAA */ \ + uint8_t z = (S & 0xf) << 3 | (E & 1) << 2 | (A & 3); \ + S >>= 4; \ + E >>= 1; \ + A >>= 2; \ + while (S | E | F | A | K | D) { \ + out_byte(out_env, 0x80 | z); \ + /* xFSSKAED */ \ + z = (F & 1) << 6 | (S & 3) << 4 | (K & 1) << 3 \ + | (A & 1) << 2 | (E & 1) << 1 | (D & 1); \ + S >>= 2; \ + E >>= 1; \ + F >>= 1; \ + A >>= 1; \ + K >>= 1; \ + D >>= 1; \ + } \ + out_byte(out_env, z); \ + } while (0) + +#define MP_BC_PRELUDE_SIG_DECODE_INTO(ip, S, E, F, A, K, D) \ + do { \ + uint8_t z = *(ip)++; \ + /* xSSSSEAA */ \ + S = (z >> 3) & 0xf; \ + E = (z >> 2) & 0x1; \ + F = 0; \ + A = z & 0x3; \ + K = 0; \ + D = 0; \ + for (unsigned n = 0; z & 0x80; ++n) { \ + z = *(ip)++; \ + /* xFSSKAED */ \ + S |= (z & 0x30) << (2 * n); \ + E |= (z & 0x02) << n; \ + F |= ((z & 0x40) >> 6) << n; \ + A |= (z & 0x4) << n; \ + K |= ((z & 0x08) >> 3) << n; \ + D |= (z & 0x1) << n; \ + } \ + S += 1; \ + } while (0) + +#define MP_BC_PRELUDE_SIG_DECODE(ip) \ + size_t n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; \ + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); \ + (void)n_state; (void)n_exc_stack; (void)scope_flags; \ + (void)n_pos_args; (void)n_kwonly_args; (void)n_def_pos_args + +#define MP_BC_PRELUDE_SIZE_ENCODE(I, C, out_byte, out_env) \ + do { \ + /* Encode bit-wise as: xIIIIIIC */ \ + uint8_t z = 0; \ + do { \ + z = (I & 0x3f) << 1 | (C & 1); \ + C >>= 1; \ + I >>= 6; \ + if (C | I) { \ + z |= 0x80; \ + } \ + out_byte(out_env, z); \ + } while (C | I); \ + } while (0) + +#define MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, I, C) \ + do { \ + uint8_t z; \ + C = 0; \ + I = 0; \ + for (unsigned n = 0;; ++n) { \ + z = *(ip)++; \ + /* xIIIIIIC */ \ + C |= (z & 1) << n; \ + I |= ((z & 0x7e) >> 1) << (6 * n); \ + if (!(z & 0x80)) { \ + break; \ + } \ + } \ + } while (0) + +#define MP_BC_PRELUDE_SIZE_DECODE(ip) \ + size_t n_info, n_cell; \ + MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, n_info, n_cell); \ + (void)n_info; (void)n_cell + +// Sentinel value for mp_code_state_t.exc_sp_idx +#define MP_CODE_STATE_EXC_SP_IDX_SENTINEL ((uint16_t)-1) + +// To convert mp_code_state_t.exc_sp_idx to/from a pointer to mp_exc_stack_t +#define MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp) ((exc_sp) + 1 - (exc_stack)) +#define MP_CODE_STATE_EXC_SP_IDX_TO_PTR(exc_stack, exc_sp_idx) ((exc_stack) + (exc_sp_idx) - 1) + +typedef struct _mp_bytecode_prelude_t { + uint n_state; + uint n_exc_stack; + uint scope_flags; + uint n_pos_args; + uint n_kwonly_args; + uint n_def_pos_args; + qstr qstr_block_name_idx; + const byte *line_info; + const byte *line_info_top; + const byte *opcodes; +} mp_bytecode_prelude_t; + +// Exception stack entry +typedef struct _mp_exc_stack_t { + const byte *handler; + // bit 0 is currently unused + // bit 1 is whether the opcode was SETUP_WITH or SETUP_FINALLY + mp_obj_t *val_sp; + // Saved exception + mp_obj_base_t *prev_exc; +} mp_exc_stack_t; + +// Constants associated with a module, to interface bytecode with runtime. +typedef struct _mp_module_constants_t { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + qstr_short_t *qstr_table; + #else + qstr source_file; + #endif + mp_obj_t *obj_table; +} mp_module_constants_t; + +// State associated with a module. +typedef struct _mp_module_context_t { + mp_obj_module_t module; + mp_module_constants_t constants; +} mp_module_context_t; + +// Outer level struct defining a compiled module. +typedef struct _mp_compiled_module_t { + mp_module_context_t *context; + const struct _mp_raw_code_t *rc; + #if MICROPY_PERSISTENT_CODE_SAVE + bool has_native; + size_t n_qstr; + size_t n_obj; + #endif +} mp_compiled_module_t; + +// Outer level struct defining a frozen module. +typedef struct _mp_frozen_module_t { + const mp_module_constants_t constants; + const void *proto_fun; +} mp_frozen_module_t; + +// State for an executing function. +typedef struct _mp_code_state_t { + // The fun_bc entry points to the underlying function object that is being executed. + // It is needed to access the start of bytecode and the const_table. + // It is also needed to prevent the GC from reclaiming the bytecode during execution, + // because the ip pointer below will always point to the interior of the bytecode. + struct _mp_obj_fun_bc_t *fun_bc; + const byte *ip; + mp_obj_t *sp; + uint16_t n_state; + uint16_t exc_sp_idx; + mp_obj_dict_t *old_globals; + #if MICROPY_STACKLESS + struct _mp_code_state_t *prev; + #endif + #if MICROPY_PY_SYS_SETTRACE + struct _mp_code_state_t *prev_state; + struct _mp_obj_frame_t *frame; + #endif + // Variable-length + mp_obj_t state[0]; + // Variable-length, never accessed by name, only as (void*)(state + n_state) + // mp_exc_stack_t exc_state[0]; +} mp_code_state_t; + +// State for an executing native function (based on mp_code_state_t). +typedef struct _mp_code_state_native_t { + struct _mp_obj_fun_bc_t *fun_bc; + const byte *ip; + mp_obj_t *sp; + uint16_t n_state; + uint16_t exc_sp_idx; + mp_obj_dict_t *old_globals; + mp_obj_t state[0]; +} mp_code_state_native_t; + +// Allocator may return NULL, in which case data is not stored (can be used to compute size). +typedef uint8_t *(*mp_encode_uint_allocator_t)(void *env, size_t nbytes); + +void mp_encode_uint(void *env, mp_encode_uint_allocator_t allocator, mp_uint_t val); +mp_uint_t mp_decode_uint(const byte **ptr); +mp_uint_t mp_decode_uint_value(const byte *ptr); +const byte *mp_decode_uint_skip(const byte *ptr); + +mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, +#ifndef __cplusplus + volatile +#endif + mp_obj_t inject_exc); +mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); +void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); +void mp_setup_code_state_native(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); +void mp_bytecode_print(const mp_print_t *print, const struct _mp_raw_code_t *rc, size_t fun_data_len, const mp_module_constants_t *cm); +void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm); +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, const byte *ip, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm); +#define mp_bytecode_print_inst(print, code, x_table) mp_bytecode_print2(print, code, 1, x_table) + +// Helper macros to access pointer with least significant bits holding flags +#define MP_TAGPTR_PTR(x) ((void *)((uintptr_t)(x) & ~((uintptr_t)3))) +#define MP_TAGPTR_TAG0(x) ((uintptr_t)(x) & 1) +#define MP_TAGPTR_TAG1(x) ((uintptr_t)(x) & 2) +#define MP_TAGPTR_MAKE(ptr, tag) ((void *)((uintptr_t)(ptr) | (tag))) + +static inline void mp_module_context_alloc_tables(mp_module_context_t *context, size_t n_qstr, size_t n_obj) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + size_t nq = (n_qstr * sizeof(qstr_short_t) + sizeof(mp_uint_t) - 1) / sizeof(mp_uint_t); + size_t no = n_obj; + mp_uint_t *mem = m_new(mp_uint_t, nq + no); + context->constants.qstr_table = (qstr_short_t *)mem; + context->constants.obj_table = (mp_obj_t *)(mem + nq); + #else + if (n_obj == 0) { + context->constants.obj_table = NULL; + } else { + context->constants.obj_table = m_new(mp_obj_t, n_obj); + } + #endif +} + +static inline size_t mp_bytecode_get_source_line(const byte *line_info, const byte *line_info_top, size_t bc_offset) { + size_t source_line = 1; + while (line_info < line_info_top) { + size_t c = *line_info; + size_t b, l; + if ((c & 0x80) == 0) { + // 0b0LLBBBBB encoding + b = c & 0x1f; + l = c >> 5; + line_info += 1; + } else { + // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + b = c & 0xf; + l = ((c << 4) & 0x700) | line_info[1]; + line_info += 2; + } + if (bc_offset >= b) { + bc_offset -= b; + source_line += l; + } else { + // found source line corresponding to bytecode offset + break; + } + } + return source_line; +} + +#endif // MICROPY_INCLUDED_PY_BC_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/bc0.h b/non_catalog_apps/mp_flipper/lib/micropython/py/bc0.h new file mode 100644 index 00000000..a4a0acf9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/bc0.h @@ -0,0 +1,162 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_BC0_H +#define MICROPY_INCLUDED_PY_BC0_H + +// MicroPython bytecode opcodes, grouped based on the format of the opcode + +// All opcodes are encoded as a byte with an optional argument. Arguments are +// variable-length encoded so they can be as small as possible. The possible +// encodings for arguments are (ip[0] is the opcode): +// +// - unsigned relative bytecode offset: +// - if ip[1] high bit is clear then: arg = ip[1] +// - if ip[1] high bit is set then: arg = ip[1] & 0x7f | ip[2] << 7 +// +// - signed relative bytecode offset: +// - if ip[1] high bit is clear then: arg = ip[1] - 0x40 +// - if ip[1] high bit is set then: arg = (ip[1] & 0x7f | ip[2] << 7) - 0x4000 + +#define MP_BC_MASK_FORMAT (0xf0) +#define MP_BC_MASK_EXTRA_BYTE (0x9e) + +#define MP_BC_FORMAT_BYTE (0) +#define MP_BC_FORMAT_QSTR (1) +#define MP_BC_FORMAT_VAR_UINT (2) +#define MP_BC_FORMAT_OFFSET (3) + +// Nibbles in magic number are: BB BB BB BB BB BO VV QU +#define MP_BC_FORMAT(op) ((0x000003a4 >> (2 * ((op) >> 4))) & 3) + +// Load, Store, Delete, Import, Make, Build, Unpack, Call, Jump, Exception, For, sTack, Return, Yield, Op +#define MP_BC_BASE_RESERVED (0x00) // ---------------- +#define MP_BC_BASE_QSTR_O (0x10) // LLLLLLSSSDDII--- +#define MP_BC_BASE_VINT_E (0x20) // MMLLLLSSDDBBBBBB +#define MP_BC_BASE_VINT_O (0x30) // UUMMCCCC-------- +#define MP_BC_BASE_JUMP_E (0x40) // J-JJJJJEEEEF---- +#define MP_BC_BASE_BYTE_O (0x50) // LLLLSSDTTTTTEEFF +#define MP_BC_BASE_BYTE_E (0x60) // --BREEEYYI------ +#define MP_BC_LOAD_CONST_SMALL_INT_MULTI (0x70) // LLLLLLLLLLLLLLLL +// (0x80) // LLLLLLLLLLLLLLLL +// (0x90) // LLLLLLLLLLLLLLLL +// (0xa0) // LLLLLLLLLLLLLLLL +#define MP_BC_LOAD_FAST_MULTI (0xb0) // LLLLLLLLLLLLLLLL +#define MP_BC_STORE_FAST_MULTI (0xc0) // SSSSSSSSSSSSSSSS +#define MP_BC_UNARY_OP_MULTI (0xd0) // OOOOOOO +#define MP_BC_BINARY_OP_MULTI (0xd7) // OOOOOOOOO +// (0xe0) // OOOOOOOOOOOOOOOO +// (0xf0) // OOOOOOOOOO------ + +#define MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM (64) +#define MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS (16) +#define MP_BC_LOAD_FAST_MULTI_NUM (16) +#define MP_BC_STORE_FAST_MULTI_NUM (16) +#define MP_BC_UNARY_OP_MULTI_NUM (MP_UNARY_OP_NUM_BYTECODE) +#define MP_BC_BINARY_OP_MULTI_NUM (MP_BINARY_OP_NUM_BYTECODE) + +#define MP_BC_LOAD_CONST_FALSE (MP_BC_BASE_BYTE_O + 0x00) +#define MP_BC_LOAD_CONST_NONE (MP_BC_BASE_BYTE_O + 0x01) +#define MP_BC_LOAD_CONST_TRUE (MP_BC_BASE_BYTE_O + 0x02) +#define MP_BC_LOAD_CONST_SMALL_INT (MP_BC_BASE_VINT_E + 0x02) // signed var-int +#define MP_BC_LOAD_CONST_STRING (MP_BC_BASE_QSTR_O + 0x00) // qstr +#define MP_BC_LOAD_CONST_OBJ (MP_BC_BASE_VINT_E + 0x03) // ptr +#define MP_BC_LOAD_NULL (MP_BC_BASE_BYTE_O + 0x03) + +#define MP_BC_LOAD_FAST_N (MP_BC_BASE_VINT_E + 0x04) // uint +#define MP_BC_LOAD_DEREF (MP_BC_BASE_VINT_E + 0x05) // uint +#define MP_BC_LOAD_NAME (MP_BC_BASE_QSTR_O + 0x01) // qstr +#define MP_BC_LOAD_GLOBAL (MP_BC_BASE_QSTR_O + 0x02) // qstr +#define MP_BC_LOAD_ATTR (MP_BC_BASE_QSTR_O + 0x03) // qstr +#define MP_BC_LOAD_METHOD (MP_BC_BASE_QSTR_O + 0x04) // qstr +#define MP_BC_LOAD_SUPER_METHOD (MP_BC_BASE_QSTR_O + 0x05) // qstr +#define MP_BC_LOAD_BUILD_CLASS (MP_BC_BASE_BYTE_O + 0x04) +#define MP_BC_LOAD_SUBSCR (MP_BC_BASE_BYTE_O + 0x05) + +#define MP_BC_STORE_FAST_N (MP_BC_BASE_VINT_E + 0x06) // uint +#define MP_BC_STORE_DEREF (MP_BC_BASE_VINT_E + 0x07) // uint +#define MP_BC_STORE_NAME (MP_BC_BASE_QSTR_O + 0x06) // qstr +#define MP_BC_STORE_GLOBAL (MP_BC_BASE_QSTR_O + 0x07) // qstr +#define MP_BC_STORE_ATTR (MP_BC_BASE_QSTR_O + 0x08) // qstr +#define MP_BC_STORE_SUBSCR (MP_BC_BASE_BYTE_O + 0x06) + +#define MP_BC_DELETE_FAST (MP_BC_BASE_VINT_E + 0x08) // uint +#define MP_BC_DELETE_DEREF (MP_BC_BASE_VINT_E + 0x09) // uint +#define MP_BC_DELETE_NAME (MP_BC_BASE_QSTR_O + 0x09) // qstr +#define MP_BC_DELETE_GLOBAL (MP_BC_BASE_QSTR_O + 0x0a) // qstr + +#define MP_BC_DUP_TOP (MP_BC_BASE_BYTE_O + 0x07) +#define MP_BC_DUP_TOP_TWO (MP_BC_BASE_BYTE_O + 0x08) +#define MP_BC_POP_TOP (MP_BC_BASE_BYTE_O + 0x09) +#define MP_BC_ROT_TWO (MP_BC_BASE_BYTE_O + 0x0a) +#define MP_BC_ROT_THREE (MP_BC_BASE_BYTE_O + 0x0b) + +#define MP_BC_UNWIND_JUMP (MP_BC_BASE_JUMP_E + 0x00) // signed relative bytecode offset; then a byte +#define MP_BC_JUMP (MP_BC_BASE_JUMP_E + 0x02) // signed relative bytecode offset +#define MP_BC_POP_JUMP_IF_TRUE (MP_BC_BASE_JUMP_E + 0x03) // signed relative bytecode offset +#define MP_BC_POP_JUMP_IF_FALSE (MP_BC_BASE_JUMP_E + 0x04) // signed relative bytecode offset +#define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // unsigned relative bytecode offset +#define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // unsigned relative bytecode offset +#define MP_BC_SETUP_WITH (MP_BC_BASE_JUMP_E + 0x07) // unsigned relative bytecode offset +#define MP_BC_SETUP_EXCEPT (MP_BC_BASE_JUMP_E + 0x08) // unsigned relative bytecode offset +#define MP_BC_SETUP_FINALLY (MP_BC_BASE_JUMP_E + 0x09) // unsigned relative bytecode offset +#define MP_BC_POP_EXCEPT_JUMP (MP_BC_BASE_JUMP_E + 0x0a) // unsigned relative bytecode offset +#define MP_BC_FOR_ITER (MP_BC_BASE_JUMP_E + 0x0b) // unsigned relative bytecode offset +#define MP_BC_WITH_CLEANUP (MP_BC_BASE_BYTE_O + 0x0c) +#define MP_BC_END_FINALLY (MP_BC_BASE_BYTE_O + 0x0d) +#define MP_BC_GET_ITER (MP_BC_BASE_BYTE_O + 0x0e) +#define MP_BC_GET_ITER_STACK (MP_BC_BASE_BYTE_O + 0x0f) + +#define MP_BC_BUILD_TUPLE (MP_BC_BASE_VINT_E + 0x0a) // uint +#define MP_BC_BUILD_LIST (MP_BC_BASE_VINT_E + 0x0b) // uint +#define MP_BC_BUILD_MAP (MP_BC_BASE_VINT_E + 0x0c) // uint +#define MP_BC_STORE_MAP (MP_BC_BASE_BYTE_E + 0x02) +#define MP_BC_BUILD_SET (MP_BC_BASE_VINT_E + 0x0d) // uint +#define MP_BC_BUILD_SLICE (MP_BC_BASE_VINT_E + 0x0e) // uint +#define MP_BC_STORE_COMP (MP_BC_BASE_VINT_E + 0x0f) // uint +#define MP_BC_UNPACK_SEQUENCE (MP_BC_BASE_VINT_O + 0x00) // uint +#define MP_BC_UNPACK_EX (MP_BC_BASE_VINT_O + 0x01) // uint + +#define MP_BC_RETURN_VALUE (MP_BC_BASE_BYTE_E + 0x03) +#define MP_BC_RAISE_LAST (MP_BC_BASE_BYTE_E + 0x04) +#define MP_BC_RAISE_OBJ (MP_BC_BASE_BYTE_E + 0x05) +#define MP_BC_RAISE_FROM (MP_BC_BASE_BYTE_E + 0x06) +#define MP_BC_YIELD_VALUE (MP_BC_BASE_BYTE_E + 0x07) +#define MP_BC_YIELD_FROM (MP_BC_BASE_BYTE_E + 0x08) + +#define MP_BC_MAKE_FUNCTION (MP_BC_BASE_VINT_O + 0x02) // uint +#define MP_BC_MAKE_FUNCTION_DEFARGS (MP_BC_BASE_VINT_O + 0x03) // uint +#define MP_BC_MAKE_CLOSURE (MP_BC_BASE_VINT_E + 0x00) // uint; extra byte +#define MP_BC_MAKE_CLOSURE_DEFARGS (MP_BC_BASE_VINT_E + 0x01) // uint; extra byte +#define MP_BC_CALL_FUNCTION (MP_BC_BASE_VINT_O + 0x04) // uint +#define MP_BC_CALL_FUNCTION_VAR_KW (MP_BC_BASE_VINT_O + 0x05) // uint +#define MP_BC_CALL_METHOD (MP_BC_BASE_VINT_O + 0x06) // uint +#define MP_BC_CALL_METHOD_VAR_KW (MP_BC_BASE_VINT_O + 0x07) // uint + +#define MP_BC_IMPORT_NAME (MP_BC_BASE_QSTR_O + 0x0b) // qstr +#define MP_BC_IMPORT_FROM (MP_BC_BASE_QSTR_O + 0x0c) // qstr +#define MP_BC_IMPORT_STAR (MP_BC_BASE_BYTE_E + 0x09) + +#endif // MICROPY_INCLUDED_PY_BC0_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/binary.c b/non_catalog_apps/mp_flipper/lib/micropython/py/binary.c new file mode 100644 index 00000000..7c01cfa1 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/binary.c @@ -0,0 +1,542 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Paul Sokolovsky + * Copyright (c) 2014-2019 Damien P. George + * + * 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 "py/binary.h" +#include "py/smallint.h" +#include "py/objint.h" +#include "py/runtime.h" + +// Helpers to work with binary-encoded data + +#ifndef alignof +#define alignof(type) offsetof(struct { char c; type t; }, t) +#endif + +size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { + size_t size = 0; + int align = 1; + switch (struct_type) { + case '<': + case '>': + switch (val_type) { + case 'b': + case 'B': + size = 1; + break; + case 'h': + case 'H': + size = 2; + break; + case 'i': + case 'I': + size = 4; + break; + case 'l': + case 'L': + size = 4; + break; + case 'q': + case 'Q': + size = 8; + break; + case 'P': + case 'O': + case 'S': + size = sizeof(void *); + break; + case 'e': + size = 2; + break; + case 'f': + size = 4; + break; + case 'd': + size = 8; + break; + } + break; + case '@': { + // TODO: + // The simplest heuristic for alignment is to align by value + // size, but that doesn't work for "bigger than int" types, + // for example, long long may very well have long alignment + // So, we introduce separate alignment handling, but having + // formal support for that is different from actually supporting + // particular (or any) ABI. + switch (val_type) { + case BYTEARRAY_TYPECODE: + case 'b': + case 'B': + align = size = 1; + break; + case 'h': + case 'H': + align = alignof(short); + size = sizeof(short); + break; + case 'i': + case 'I': + align = alignof(int); + size = sizeof(int); + break; + case 'l': + case 'L': + align = alignof(long); + size = sizeof(long); + break; + case 'q': + case 'Q': + align = alignof(long long); + size = sizeof(long long); + break; + case 'P': + case 'O': + case 'S': + align = alignof(void *); + size = sizeof(void *); + break; + case 'e': + align = 2; + size = 2; + break; + case 'f': + align = alignof(float); + size = sizeof(float); + break; + case 'd': + align = alignof(double); + size = sizeof(double); + break; + } + } + } + + if (size == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("bad typecode")); + } + + if (palign != NULL) { + *palign = align; + } + return size; +} + +#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_USE_NATIVE_FLT16 + +static inline float mp_decode_half_float(uint16_t hf) { + union { + uint16_t i; + _Float16 f; + } fpu = { .i = hf }; + return fpu.f; +} + +static inline uint16_t mp_encode_half_float(float x) { + union { + uint16_t i; + _Float16 f; + } fp_sp = { .f = (_Float16)x }; + return fp_sp.i; +} + +#elif MICROPY_PY_BUILTINS_FLOAT + +static float mp_decode_half_float(uint16_t hf) { + union { + uint32_t i; + float f; + } fpu; + + uint16_t m = hf & 0x3ff; + int e = (hf >> 10) & 0x1f; + if (e == 0x1f) { + // Half-float is infinity. + e = 0xff; + } else if (e) { + // Half-float is normal. + e += 127 - 15; + } else if (m) { + // Half-float is subnormal, make it normal. + e = 127 - 15; + while (!(m & 0x400)) { + m <<= 1; + --e; + } + m -= 0x400; + ++e; + } + + fpu.i = ((hf & 0x8000) << 16) | (e << 23) | (m << 13); + return fpu.f; +} + +static uint16_t mp_encode_half_float(float x) { + union { + uint32_t i; + float f; + } fpu = { .f = x }; + + uint16_t m = (fpu.i >> 13) & 0x3ff; + if (fpu.i & (1 << 12)) { + // Round up. + ++m; + } + int e = (fpu.i >> 23) & 0xff; + + if (e == 0xff) { + // Infinity. + e = 0x1f; + } else if (e != 0) { + e -= 127 - 15; + if (e < 0) { + // Underflow: denormalized, or zero. + if (e >= -11) { + m = (m | 0x400) >> -e; + if (m & 1) { + m = (m >> 1) + 1; + } else { + m >>= 1; + } + } else { + m = 0; + } + e = 0; + } else if (e > 0x3f) { + // Overflow: infinity. + e = 0x1f; + m = 0; + } + } + + uint16_t bits = ((fpu.i >> 16) & 0x8000) | (e << 10) | m; + return bits; +} + +#endif + +mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { + mp_int_t val = 0; + switch (typecode) { + case 'b': + val = ((signed char *)p)[index]; + break; + case BYTEARRAY_TYPECODE: + case 'B': + val = ((unsigned char *)p)[index]; + break; + case 'h': + val = ((short *)p)[index]; + break; + case 'H': + val = ((unsigned short *)p)[index]; + break; + case 'i': + return mp_obj_new_int(((int *)p)[index]); + case 'I': + return mp_obj_new_int_from_uint(((unsigned int *)p)[index]); + case 'l': + return mp_obj_new_int(((long *)p)[index]); + case 'L': + return mp_obj_new_int_from_uint(((unsigned long *)p)[index]); + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + case 'q': + return mp_obj_new_int_from_ll(((long long *)p)[index]); + case 'Q': + return mp_obj_new_int_from_ull(((unsigned long long *)p)[index]); + #endif + #if MICROPY_PY_BUILTINS_FLOAT + case 'f': + return mp_obj_new_float_from_f(((float *)p)[index]); + case 'd': + return mp_obj_new_float_from_d(((double *)p)[index]); + #endif + // Extension to CPython: array of objects + case 'O': + return ((mp_obj_t *)p)[index]; + // Extension to CPython: array of pointers + case 'P': + return mp_obj_new_int((mp_int_t)(uintptr_t)((void **)p)[index]); + } + return MP_OBJ_NEW_SMALL_INT(val); +} + +// The long long type is guaranteed to hold at least 64 bits, and size is at +// most 8 (for q and Q), so we will always be able to parse the given data +// and fit it into a long long. +long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src) { + int delta; + if (!big_endian) { + delta = -1; + src += size - 1; + } else { + delta = 1; + } + + unsigned long long val = 0; + if (is_signed && *src & 0x80) { + val = -1; + } + for (uint i = 0; i < size; i++) { + val <<= 8; + val |= *src; + src += delta; + } + + return val; +} + +#define is_signed(typecode) (typecode > 'Z') +mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr) { + byte *p = *ptr; + size_t align; + + size_t size = mp_binary_get_size(struct_type, val_type, &align); + if (struct_type == '@') { + // Align p relative to p_base + p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align); + #if MP_ENDIANNESS_LITTLE + struct_type = '<'; + #else + struct_type = '>'; + #endif + } + *ptr = p + size; + + long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p); + + if (val_type == 'O') { + return (mp_obj_t)(mp_uint_t)val; + } else if (val_type == 'S') { + const char *s_val = (const char *)(uintptr_t)(mp_uint_t)val; + return mp_obj_new_str(s_val, strlen(s_val)); + #if MICROPY_PY_BUILTINS_FLOAT + } else if (val_type == 'e') { + return mp_obj_new_float_from_f(mp_decode_half_float(val)); + } else if (val_type == 'f') { + union { + uint32_t i; + float f; + } fpu = {val}; + return mp_obj_new_float_from_f(fpu.f); + } else if (val_type == 'd') { + union { + uint64_t i; + double f; + } fpu = {val}; + return mp_obj_new_float_from_d(fpu.f); + #endif + } else if (is_signed(val_type)) { + if ((long long)MP_SMALL_INT_MIN <= val && val <= (long long)MP_SMALL_INT_MAX) { + return mp_obj_new_int((mp_int_t)val); + } else { + return mp_obj_new_int_from_ll(val); + } + } else { + if ((unsigned long long)val <= (unsigned long long)MP_SMALL_INT_MAX) { + return mp_obj_new_int_from_uint((mp_uint_t)val); + } else { + return mp_obj_new_int_from_ull(val); + } + } +} + +void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val) { + if (MP_ENDIANNESS_LITTLE && !big_endian) { + memcpy(dest, &val, val_sz); + } else if (MP_ENDIANNESS_BIG && big_endian) { + // only copy the least-significant val_sz bytes + memcpy(dest, (byte *)&val + sizeof(mp_uint_t) - val_sz, val_sz); + } else { + const byte *src; + if (MP_ENDIANNESS_LITTLE) { + src = (const byte *)&val + val_sz; + } else { + src = (const byte *)&val + sizeof(mp_uint_t); + } + while (val_sz--) { + *dest++ = *--src; + } + } +} + +void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr) { + byte *p = *ptr; + size_t align; + + size_t size = mp_binary_get_size(struct_type, val_type, &align); + if (struct_type == '@') { + // Align p relative to p_base + p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align); + if (MP_ENDIANNESS_LITTLE) { + struct_type = '<'; + } else { + struct_type = '>'; + } + } + *ptr = p + size; + + mp_uint_t val; + switch (val_type) { + case 'O': + val = (mp_uint_t)val_in; + break; + #if MICROPY_PY_BUILTINS_FLOAT + case 'e': + val = mp_encode_half_float(mp_obj_get_float_to_f(val_in)); + break; + case 'f': { + union { + uint32_t i; + float f; + } fp_sp; + fp_sp.f = mp_obj_get_float_to_f(val_in); + val = fp_sp.i; + break; + } + case 'd': { + union { + uint64_t i64; + uint32_t i32[2]; + double f; + } fp_dp; + fp_dp.f = mp_obj_get_float_to_d(val_in); + if (MP_BYTES_PER_OBJ_WORD == 8) { + val = fp_dp.i64; + } else { + int be = struct_type == '>'; + mp_binary_set_int(sizeof(uint32_t), be, p, fp_dp.i32[MP_ENDIANNESS_BIG ^ be]); + p += sizeof(uint32_t); + val = fp_dp.i32[MP_ENDIANNESS_LITTLE ^ be]; + } + break; + } + #endif + default: + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + if (mp_obj_is_exact_type(val_in, &mp_type_int)) { + mp_obj_int_to_bytes_impl(val_in, struct_type == '>', size, p); + return; + } + #endif + + val = mp_obj_get_int(val_in); + // zero/sign extend if needed + if (MP_BYTES_PER_OBJ_WORD < 8 && size > sizeof(val)) { + int c = (mp_int_t)val < 0 ? 0xff : 0x00; + memset(p, c, size); + if (struct_type == '>') { + p += size - sizeof(val); + } + } + break; + } + + mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val); +} + +void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in) { + switch (typecode) { + #if MICROPY_PY_BUILTINS_FLOAT + case 'f': + ((float *)p)[index] = mp_obj_get_float_to_f(val_in); + break; + case 'd': + ((double *)p)[index] = mp_obj_get_float_to_d(val_in); + break; + #endif + // Extension to CPython: array of objects + case 'O': + ((mp_obj_t *)p)[index] = val_in; + break; + default: + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + if (mp_obj_is_exact_type(val_in, &mp_type_int)) { + size_t size = mp_binary_get_size('@', typecode, NULL); + mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG, + size, (uint8_t *)p + index * size); + return; + } + #endif + mp_binary_set_val_array_from_int(typecode, p, index, mp_obj_get_int(val_in)); + } +} + +void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val) { + switch (typecode) { + case 'b': + ((signed char *)p)[index] = val; + break; + case BYTEARRAY_TYPECODE: + case 'B': + ((unsigned char *)p)[index] = val; + break; + case 'h': + ((short *)p)[index] = val; + break; + case 'H': + ((unsigned short *)p)[index] = val; + break; + case 'i': + ((int *)p)[index] = val; + break; + case 'I': + ((unsigned int *)p)[index] = val; + break; + case 'l': + ((long *)p)[index] = val; + break; + case 'L': + ((unsigned long *)p)[index] = val; + break; + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + case 'q': + ((long long *)p)[index] = val; + break; + case 'Q': + ((unsigned long long *)p)[index] = val; + break; + #endif + #if MICROPY_PY_BUILTINS_FLOAT + case 'f': + ((float *)p)[index] = (float)val; + break; + case 'd': + ((double *)p)[index] = (double)val; + break; + #endif + // Extension to CPython: array of pointers + case 'P': + ((void **)p)[index] = (void *)(uintptr_t)val; + break; + } +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/binary.h b/non_catalog_apps/mp_flipper/lib/micropython/py/binary.h new file mode 100644 index 00000000..5c645bca --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/binary.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2014-2017 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_BINARY_H +#define MICROPY_INCLUDED_PY_BINARY_H + +#include "py/obj.h" + +// Use special typecode to differentiate repr() of bytearray vs array.array('B') +// (underlyingly they're same). Can't use 0 here because that's used to detect +// type-specification errors due to end-of-string. +#define BYTEARRAY_TYPECODE 1 + +size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign); +mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index); +void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in); +void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val); +mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr); +void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr); +long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src); +void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val); + +#endif // MICROPY_INCLUDED_PY_BINARY_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/builtin.h b/non_catalog_apps/mp_flipper/lib/micropython/py/builtin.h new file mode 100644 index 00000000..81d07898 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/builtin.h @@ -0,0 +1,142 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_BUILTIN_H +#define MICROPY_INCLUDED_PY_BUILTIN_H + +#include "py/obj.h" + +typedef enum { + MP_IMPORT_STAT_NO_EXIST, + MP_IMPORT_STAT_DIR, + MP_IMPORT_STAT_FILE, +} mp_import_stat_t; + +#if MICROPY_VFS + +// Delegate to the VFS for import stat and builtin open. + +#define mp_builtin_open_obj mp_vfs_open_obj + +mp_import_stat_t mp_vfs_import_stat(const char *path); +mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); + +MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj); + +static inline mp_import_stat_t mp_import_stat(const char *path) { + return mp_vfs_import_stat(path); +} + +static inline mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_vfs_open(n_args, args, kwargs); +} + +#else + +// A port can provide implementations of these functions. +mp_import_stat_t mp_import_stat(const char *path); +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); + +// A port can provide this object. +MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj); + +#endif + +// A port can provide its own import handler by defining mp_builtin___import__. +#ifndef mp_builtin___import__ +#define mp_builtin___import__ mp_builtin___import___default +#endif +mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args); +mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args); + +mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args); + +MP_DECLARE_CONST_FUN_OBJ_VAR(mp_builtin___build_class___obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin___import___obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin___repl_print___obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_abs_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_all_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_any_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_bin_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_callable_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_chr_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_delattr_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_eval_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_exec_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_execfile_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj); +MP_DECLARE_CONST_FUN_OBJ_3(mp_builtin_setattr_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_builtin_globals_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_hasattr_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_hash_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_hex_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_id_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_issubclass_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_iter_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_len_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_builtin_locals_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_max_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_min_obj); +#if MICROPY_PY_BUILTINS_NEXT2 +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_next_obj); +#else +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_next_obj); +#endif +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_oct_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_ord_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_print_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_repr_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_round_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_sorted_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_sum_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj); + +MP_DECLARE_CONST_FUN_OBJ_2(mp_namedtuple_obj); + +MP_DECLARE_CONST_FUN_OBJ_2(mp_op_contains_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_op_getitem_obj); +MP_DECLARE_CONST_FUN_OBJ_3(mp_op_setitem_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_op_delitem_obj); + +// Modules needed by the runtime. +extern const mp_obj_dict_t mp_module_builtins_globals; +extern const mp_obj_module_t mp_module___main__; +extern const mp_obj_module_t mp_module_builtins; +extern const mp_obj_module_t mp_module_sys; + +// Modules needed by the parser when MICROPY_COMP_MODULE_CONST is enabled. +extern const mp_obj_module_t mp_module_errno; +extern const mp_obj_module_t mp_module_uctypes; +extern const mp_obj_module_t mp_module_machine; + +extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; + +#endif // MICROPY_INCLUDED_PY_BUILTIN_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/builtinevex.c b/non_catalog_apps/mp_flipper/lib/micropython/py/builtinevex.c new file mode 100644 index 00000000..e25cbd4d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/builtinevex.c @@ -0,0 +1,179 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/objfun.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/builtin.h" + +#if MICROPY_PY_BUILTINS_COMPILE + +typedef struct _mp_obj_code_t { + mp_obj_base_t base; + mp_obj_t module_fun; +} mp_obj_code_t; + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_code, + MP_QSTR_code, + MP_TYPE_FLAG_NONE + ); + +static mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { + // save context + nlr_jump_callback_node_globals_locals_t ctx; + ctx.globals = mp_globals_get(); + ctx.locals = mp_locals_get(); + + // set new context + mp_globals_set(globals); + mp_locals_set(locals); + + // set exception handler to restore context if an exception is raised + nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback); + + // The call to mp_parse_compile_execute() in mp_builtin_compile() below passes + // NULL for the globals, so repopulate that entry now with the correct globals. + if (mp_obj_is_type(self->module_fun, &mp_type_fun_bc) + #if MICROPY_EMIT_NATIVE + || mp_obj_is_type(self->module_fun, &mp_type_fun_native) + #endif + ) { + mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun); + ((mp_module_context_t *)fun_bc->context)->module.globals = globals; + } + + // execute code + mp_obj_t ret = mp_call_function_0(self->module_fun); + + // deregister exception handler and restore context + nlr_pop_jump_callback(true); + + // return value + return ret; +} + +static mp_obj_t mp_builtin_compile(size_t n_args, const mp_obj_t *args) { + (void)n_args; + + // get the source + size_t str_len; + const char *str = mp_obj_str_get_data(args[0], &str_len); + + // get the filename + qstr filename = mp_obj_str_get_qstr(args[1]); + + // create the lexer + mp_lexer_t *lex = mp_lexer_new_from_str_len(filename, str, str_len, 0); + + // get the compile mode + qstr mode = mp_obj_str_get_qstr(args[2]); + mp_parse_input_kind_t parse_input_kind; + switch (mode) { + case MP_QSTR_single: + parse_input_kind = MP_PARSE_SINGLE_INPUT; + break; + case MP_QSTR_exec: + parse_input_kind = MP_PARSE_FILE_INPUT; + break; + case MP_QSTR_eval: + parse_input_kind = MP_PARSE_EVAL_INPUT; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("bad compile mode")); + } + + mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code); + code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL); + return MP_OBJ_FROM_PTR(code); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile); + +#endif // MICROPY_PY_BUILTINS_COMPILE + +#if MICROPY_PY_BUILTINS_EVAL_EXEC + +static mp_obj_t eval_exec_helper(size_t n_args, const mp_obj_t *args, mp_parse_input_kind_t parse_input_kind) { + // work out the context + mp_obj_dict_t *globals = mp_globals_get(); + mp_obj_dict_t *locals = mp_locals_get(); + for (size_t i = 1; i < 3 && i < n_args; ++i) { + if (args[i] != mp_const_none) { + if (!mp_obj_is_type(args[i], &mp_type_dict)) { + mp_raise_TypeError(NULL); + } + locals = MP_OBJ_TO_PTR(args[i]); + if (i == 1) { + globals = locals; + } + } + } + + #if MICROPY_PY_BUILTINS_COMPILE + if (mp_obj_is_type(args[0], &mp_type_code)) { + return code_execute(MP_OBJ_TO_PTR(args[0]), globals, locals); + } + #endif + + + // create the lexer + // MP_PARSE_SINGLE_INPUT is used to indicate a file input + mp_lexer_t *lex; + if (MICROPY_PY_BUILTINS_EXECFILE && parse_input_kind == MP_PARSE_SINGLE_INPUT) { + lex = mp_lexer_new_from_file(mp_obj_str_get_qstr(args[0])); + parse_input_kind = MP_PARSE_FILE_INPUT; + } else { + // Extract the source code. + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, bufinfo.buf, bufinfo.len, 0); + } + + return mp_parse_compile_execute(lex, parse_input_kind, globals, locals); +} + +static mp_obj_t mp_builtin_eval(size_t n_args, const mp_obj_t *args) { + return eval_exec_helper(n_args, args, MP_PARSE_EVAL_INPUT); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_eval_obj, 1, 3, mp_builtin_eval); + +static mp_obj_t mp_builtin_exec(size_t n_args, const mp_obj_t *args) { + return eval_exec_helper(n_args, args, MP_PARSE_FILE_INPUT); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_exec_obj, 1, 3, mp_builtin_exec); + +#endif // MICROPY_PY_BUILTINS_EVAL_EXEC + +#if MICROPY_PY_BUILTINS_EXECFILE +static mp_obj_t mp_builtin_execfile(size_t n_args, const mp_obj_t *args) { + // MP_PARSE_SINGLE_INPUT is used to indicate a file input + return eval_exec_helper(n_args, args, MP_PARSE_SINGLE_INPUT); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_execfile_obj, 1, 3, mp_builtin_execfile); +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/builtinhelp.c b/non_catalog_apps/mp_flipper/lib/micropython/py/builtinhelp.c new file mode 100644 index 00000000..a3fcc4df --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/builtinhelp.c @@ -0,0 +1,174 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * + * 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 "py/builtin.h" +#include "py/objmodule.h" + +#if MICROPY_PY_BUILTINS_HELP + +const char mp_help_default_text[] = + "Welcome to MicroPython!\n" + "\n" + "For online docs please visit http://docs.micropython.org/\n" + "\n" + "Control commands:\n" + " CTRL-A -- on a blank line, enter raw REPL mode\n" + " CTRL-B -- on a blank line, enter normal REPL mode\n" + " CTRL-C -- interrupt a running program\n" + " CTRL-D -- on a blank line, exit or do a soft reset\n" + " CTRL-E -- on a blank line, enter paste mode\n" + "\n" + "For further help on a specific object, type help(obj)\n" +; + +static void mp_help_print_info_about_object(mp_obj_t name_o, mp_obj_t value) { + mp_print_str(MP_PYTHON_PRINTER, " "); + mp_obj_print(name_o, PRINT_STR); + mp_print_str(MP_PYTHON_PRINTER, " -- "); + mp_obj_print(value, PRINT_STR); + mp_print_str(MP_PYTHON_PRINTER, "\n"); +} + +#if MICROPY_PY_BUILTINS_HELP_MODULES +static void mp_help_add_from_map(mp_obj_t list, const mp_map_t *map) { + for (size_t i = 0; i < map->alloc; i++) { + if (mp_map_slot_is_filled(map, i)) { + mp_obj_list_append(list, map->table[i].key); + } + } +} + +#if MICROPY_MODULE_FROZEN +static void mp_help_add_from_names(mp_obj_t list, const char *name) { + while (*name) { + size_t len = strlen(name); + // name should end in '.py' and we strip it off + mp_obj_list_append(list, mp_obj_new_str(name, len - 3)); + name += len + 1; + } +} +#endif + +static void mp_help_print_modules(void) { + mp_obj_t list = mp_obj_new_list(0, NULL); + + mp_help_add_from_map(list, &mp_builtin_module_map); + mp_help_add_from_map(list, &mp_builtin_extensible_module_map); + + #if MICROPY_MODULE_FROZEN + extern const char mp_frozen_names[]; + mp_help_add_from_names(list, mp_frozen_names); + #endif + + // sort the list so it's printed in alphabetical order + mp_obj_list_sort(1, &list, (mp_map_t *)&mp_const_empty_map); + + // print the list of modules in a column-first order + #define NUM_COLUMNS (4) + #define COLUMN_WIDTH (18) + size_t len; + mp_obj_t *items; + mp_obj_list_get(list, &len, &items); + unsigned int num_rows = (len + NUM_COLUMNS - 1) / NUM_COLUMNS; + for (unsigned int i = 0; i < num_rows; ++i) { + unsigned int j = i; + for (;;) { + int l = mp_print_str(MP_PYTHON_PRINTER, mp_obj_str_get_str(items[j])); + j += num_rows; + if (j >= len) { + break; + } + int gap = COLUMN_WIDTH - l; + while (gap < 1) { + gap += COLUMN_WIDTH; + } + while (gap--) { + mp_print_str(MP_PYTHON_PRINTER, " "); + } + } + mp_print_str(MP_PYTHON_PRINTER, "\n"); + } + + #if MICROPY_ENABLE_EXTERNAL_IMPORT + // let the user know there may be other modules available from the filesystem + mp_print_str(MP_PYTHON_PRINTER, "Plus any modules on the filesystem\n"); + #endif +} +#endif + +static void mp_help_print_obj(const mp_obj_t obj) { + #if MICROPY_PY_BUILTINS_HELP_MODULES + if (obj == MP_OBJ_NEW_QSTR(MP_QSTR_modules)) { + mp_help_print_modules(); + return; + } + #endif + + const mp_obj_type_t *type = mp_obj_get_type(obj); + + // try to print something sensible about the given object + mp_print_str(MP_PYTHON_PRINTER, "object "); + mp_obj_print(obj, PRINT_STR); + mp_printf(MP_PYTHON_PRINTER, " is of type %q\n", type->name); + + mp_map_t *map = NULL; + if (type == &mp_type_module) { + map = &mp_obj_module_get_globals(obj)->map; + } else { + if (type == &mp_type_type) { + type = MP_OBJ_TO_PTR(obj); + } + if (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { + map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; + } + } + if (map != NULL) { + for (uint i = 0; i < map->alloc; i++) { + mp_obj_t key = map->table[i].key; + if (key != MP_OBJ_NULL) { + mp_help_print_info_about_object(key, map->table[i].value); + } + } + } +} + +static mp_obj_t mp_builtin_help(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + // print a general help message + mp_print_str(MP_PYTHON_PRINTER, MICROPY_PY_BUILTINS_HELP_TEXT); + } else { + // try to print something sensible about the given object + mp_help_print_obj(args[0]); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj, 0, 1, mp_builtin_help); + +#endif // MICROPY_PY_BUILTINS_HELP diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/builtinimport.c b/non_catalog_apps/mp_flipper/lib/micropython/py/builtinimport.c new file mode 100644 index 00000000..0611926f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/builtinimport.c @@ -0,0 +1,665 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2021 Jim Mussared + * + * 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 "py/compile.h" +#include "py/objmodule.h" +#include "py/persistentcode.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/frozenmod.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + +#if MICROPY_ENABLE_EXTERNAL_IMPORT + +// Must be a string of one byte. +#define PATH_SEP_CHAR "/" + +// Virtual sys.path entry that maps to the frozen modules. +#define MP_FROZEN_PATH_PREFIX ".frozen/" + +// Wrapper for mp_import_stat (which is provided by the port, and typically +// uses mp_vfs_import_stat) to also search frozen modules. Given an exact +// path to a file or directory (e.g. "foo/bar", foo/bar.py" or "foo/bar.mpy"), +// will return whether the path is a file, directory, or doesn't exist. +static mp_import_stat_t stat_path(vstr_t *path) { + const char *str = vstr_null_terminated_str(path); + #if MICROPY_MODULE_FROZEN + // Only try and load as a frozen module if it starts with .frozen/. + const int frozen_path_prefix_len = strlen(MP_FROZEN_PATH_PREFIX); + if (strncmp(str, MP_FROZEN_PATH_PREFIX, frozen_path_prefix_len) == 0) { + // Just stat (which is the return value), don't get the data. + return mp_find_frozen_module(str + frozen_path_prefix_len, NULL, NULL); + } + #endif + return mp_import_stat(str); +} + +// Stat a given filesystem path to a .py file. If the file does not exist, +// then attempt to stat the corresponding .mpy file, and update the path +// argument. This is the logic that makes .py files take precedent over .mpy +// files. This uses stat_path above, rather than mp_import_stat directly, so +// that the .frozen path prefix is handled. +static mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) { + mp_import_stat_t stat = stat_path(path); + if (stat == MP_IMPORT_STAT_FILE) { + return stat; + } + + #if MICROPY_PERSISTENT_CODE_LOAD + // Didn't find .py -- try the .mpy instead by inserting an 'm' into the '.py'. + // Note: There's no point doing this if it's a frozen path, but adding the check + // would be extra code, and no harm letting mp_find_frozen_module fail instead. + vstr_ins_byte(path, path->len - 2, 'm'); + stat = stat_path(path); + if (stat == MP_IMPORT_STAT_FILE) { + return stat; + } + #endif + + return MP_IMPORT_STAT_NO_EXIST; +} + +// Given an import path (e.g. "foo/bar"), try and find "foo/bar" (a directory) +// or "foo/bar.(m)py" in either the filesystem or frozen modules. If the +// result is a file, the path argument will be updated to include the file +// extension. +static mp_import_stat_t stat_module(vstr_t *path) { + mp_import_stat_t stat = stat_path(path); + DEBUG_printf("stat %s: %d\n", vstr_str(path), stat); + if (stat == MP_IMPORT_STAT_DIR) { + return stat; + } + + // Not a directory, add .py and try as a file. + vstr_add_str(path, ".py"); + return stat_file_py_or_mpy(path); +} + +// Given a top-level module name, try and find it in each of the sys.path +// entries. Note: On success, the dest argument will be updated to the matching +// path (i.e. "/mod_name(.py)"). +static mp_import_stat_t stat_top_level(qstr mod_name, vstr_t *dest) { + DEBUG_printf("stat_top_level: '%s'\n", qstr_str(mod_name)); + #if MICROPY_PY_SYS + size_t path_num; + mp_obj_t *path_items; + mp_obj_get_array(mp_sys_path, &path_num, &path_items); + + // go through each sys.path entry, trying to import "/". + for (size_t i = 0; i < path_num; i++) { + vstr_reset(dest); + size_t p_len; + const char *p = mp_obj_str_get_data(path_items[i], &p_len); + if (p_len > 0) { + // Add the path separator (unless the entry is "", i.e. cwd). + vstr_add_strn(dest, p, p_len); + vstr_add_char(dest, PATH_SEP_CHAR[0]); + } + vstr_add_str(dest, qstr_str(mod_name)); + mp_import_stat_t stat = stat_module(dest); + if (stat != MP_IMPORT_STAT_NO_EXIST) { + return stat; + } + } + + // sys.path was empty or no matches, do not search the filesystem or + // frozen code. + return MP_IMPORT_STAT_NO_EXIST; + + #else + + // mp_sys_path is not enabled, so just stat the given path directly. + vstr_add_str(dest, qstr_str(mod_name)); + return stat_module(dest); + + #endif +} + +#if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER +static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { + #if MICROPY_PY___FILE__ + qstr source_name = lex->source_name; + mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + #endif + + // parse, compile and execute the module in its context + mp_obj_dict_t *mod_globals = context->module.globals; + mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals); +} +#endif + +#if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY +static void do_execute_proto_fun(const mp_module_context_t *context, mp_proto_fun_t proto_fun, qstr source_name) { + #if MICROPY_PY___FILE__ + mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + #else + (void)source_name; + #endif + + // execute the module in its context + mp_obj_dict_t *mod_globals = context->module.globals; + + // save context + nlr_jump_callback_node_globals_locals_t ctx; + ctx.globals = mp_globals_get(); + ctx.locals = mp_locals_get(); + + // set new context + mp_globals_set(mod_globals); + mp_locals_set(mod_globals); + + // set exception handler to restore context if an exception is raised + nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback); + + // make and execute the function + mp_obj_t module_fun = mp_make_function_from_proto_fun(proto_fun, context, NULL); + mp_call_function_0(module_fun); + + // deregister exception handler and restore context + nlr_pop_jump_callback(true); +} +#endif + +static void do_load(mp_module_context_t *module_obj, vstr_t *file) { + #if MICROPY_MODULE_FROZEN || MICROPY_ENABLE_COMPILER || (MICROPY_PERSISTENT_CODE_LOAD && MICROPY_HAS_FILE_READER) + const char *file_str = vstr_null_terminated_str(file); + #endif + + // If we support frozen modules (either as str or mpy) then try to find the + // requested filename in the list of frozen module filenames. + #if MICROPY_MODULE_FROZEN + void *modref; + int frozen_type; + const int frozen_path_prefix_len = strlen(MP_FROZEN_PATH_PREFIX); + if (strncmp(file_str, MP_FROZEN_PATH_PREFIX, frozen_path_prefix_len) == 0) { + mp_find_frozen_module(file_str + frozen_path_prefix_len, &frozen_type, &modref); + + // If we support frozen str modules and the compiler is enabled, and we + // found the filename in the list of frozen files, then load and execute it. + #if MICROPY_MODULE_FROZEN_STR + if (frozen_type == MP_FROZEN_STR) { + do_load_from_lexer(module_obj, modref); + return; + } + #endif + + // If we support frozen mpy modules and we found a corresponding file (and + // its data) in the list of frozen files, execute it. + #if MICROPY_MODULE_FROZEN_MPY + if (frozen_type == MP_FROZEN_MPY) { + const mp_frozen_module_t *frozen = modref; + module_obj->constants = frozen->constants; + #if MICROPY_PY___FILE__ + qstr frozen_file_qstr = qstr_from_str(file_str + frozen_path_prefix_len); + #else + qstr frozen_file_qstr = MP_QSTRnull; + #endif + do_execute_proto_fun(module_obj, frozen->proto_fun, frozen_file_qstr); + return; + } + #endif + } + + #endif // MICROPY_MODULE_FROZEN + + qstr file_qstr = qstr_from_str(file_str); + + // If we support loading .mpy files then check if the file extension is of + // the correct format and, if so, load and execute the file. + #if MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD + if (file_str[file->len - 3] == 'm') { + mp_compiled_module_t cm; + cm.context = module_obj; + mp_raw_code_load_file(file_qstr, &cm); + do_execute_proto_fun(cm.context, cm.rc, file_qstr); + return; + } + #endif + + // If we can compile scripts then load the file and compile and execute it. + #if MICROPY_ENABLE_COMPILER + { + mp_lexer_t *lex = mp_lexer_new_from_file(file_qstr); + do_load_from_lexer(module_obj, lex); + return; + } + #else + // If we get here then the file was not frozen and we can't compile scripts. + mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("script compilation not supported")); + #endif +} + +// Convert a relative (to the current module) import, going up "level" levels, +// into an absolute import. +static void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len) { + // What we want to do here is to take the name of the current module, + // remove trailing components, and concatenate the passed-in + // module name. + // For example, level=3, module_name="foo.bar", __name__="a.b.c.d" --> "a.foo.bar" + // "Relative imports use a module's __name__ attribute to determine that + // module's position in the package hierarchy." + // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name + + mp_obj_t current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); + assert(current_module_name_obj != MP_OBJ_NULL); + + #if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT && MICROPY_CPYTHON_COMPAT + if (MP_OBJ_QSTR_VALUE(current_module_name_obj) == MP_QSTR___main__) { + // This is a module loaded by -m command-line switch (e.g. unix port), + // and so its __name__ has been set to "__main__". Get its real name + // that we stored during import in the __main__ attribute. + current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); + } + #endif + + // If we have a __path__ in the globals dict, then we're a package. + bool is_pkg = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); + + #if DEBUG_PRINT + DEBUG_printf("Current module/package: "); + mp_obj_print_helper(MICROPY_DEBUG_PRINTER, current_module_name_obj, PRINT_REPR); + DEBUG_printf(", is_package: %d", is_pkg); + DEBUG_printf("\n"); + #endif + + size_t current_module_name_len; + const char *current_module_name = mp_obj_str_get_data(current_module_name_obj, ¤t_module_name_len); + + const char *p = current_module_name + current_module_name_len; + if (is_pkg) { + // If we're evaluating relative to a package, then take off one fewer + // level (i.e. the relative search starts inside the package, rather + // than as a sibling of the package). + --level; + } + + // Walk back 'level' dots (or run out of path). + while (level && p > current_module_name) { + if (*--p == '.') { + --level; + } + } + + // We must have some component left over to import from. + if (p == current_module_name) { + mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("can't perform relative import")); + } + + // New length is len("."). Note: might be one byte + // more than we need if module_name is empty (for the extra . we will + // append). + uint new_module_name_len = (size_t)(p - current_module_name) + 1 + *module_name_len; + char *new_mod = mp_local_alloc(new_module_name_len); + memcpy(new_mod, current_module_name, p - current_module_name); + + // Only append "." if there was one). + if (*module_name_len != 0) { + new_mod[p - current_module_name] = '.'; + memcpy(new_mod + (p - current_module_name) + 1, *module_name, *module_name_len); + } else { + --new_module_name_len; + } + + // Copy into a QSTR. + qstr new_mod_q = qstr_from_strn(new_mod, new_module_name_len); + mp_local_free(new_mod); + + DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q)); + *module_name = qstr_str(new_mod_q); + *module_name_len = new_module_name_len; +} + +typedef struct _nlr_jump_callback_node_unregister_module_t { + nlr_jump_callback_node_t callback; + qstr name; +} nlr_jump_callback_node_unregister_module_t; + +static void unregister_module_from_nlr_jump_callback(void *ctx_in) { + nlr_jump_callback_node_unregister_module_t *ctx = ctx_in; + mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; + mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(ctx->name), MP_MAP_LOOKUP_REMOVE_IF_FOUND); +} + +// Load a module at the specified absolute path, possibly as a submodule of the given outer module. +// full_mod_name: The full absolute path up to this level (e.g. "foo.bar.baz"). +// level_mod_name: The final component of the path (e.g. "baz"). +// outer_module_obj: The parent module (we need to store this module as an +// attribute on it) (or MP_OBJ_NULL for top-level). +// override_main: Whether to set the __name__ to "__main__" (and use __main__ +// for the actual path). +static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, mp_obj_t outer_module_obj, bool override_main) { + // Immediately return if the module at this level is already loaded. + mp_map_elem_t *elem; + + #if MICROPY_PY_SYS + // If sys.path is empty, the intention is to force using a built-in. This + // means we should also ignore any loaded modules with the same name + // which may have come from the filesystem. + size_t path_num; + mp_obj_t *path_items; + mp_obj_get_array(mp_sys_path, &path_num, &path_items); + if (path_num) + #endif + { + elem = mp_map_lookup(&MP_STATE_VM(mp_loaded_modules_dict).map, MP_OBJ_NEW_QSTR(full_mod_name), MP_MAP_LOOKUP); + if (elem) { + return elem->value; + } + } + + VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX); + mp_import_stat_t stat = MP_IMPORT_STAT_NO_EXIST; + mp_obj_t module_obj; + + if (outer_module_obj == MP_OBJ_NULL) { + // First module in the dotted-name path. + DEBUG_printf("Searching for top-level module\n"); + + // An import of a non-extensible built-in will always bypass the + // filesystem. e.g. `import micropython` or `import pyb`. So try and + // match a non-extensible built-ins first. + module_obj = mp_module_get_builtin(level_mod_name, false); + if (module_obj != MP_OBJ_NULL) { + return module_obj; + } + + // Next try the filesystem. Search for a directory or file relative to + // all the locations in sys.path. + stat = stat_top_level(level_mod_name, &path); + + // If filesystem failed, now try and see if it matches an extensible + // built-in module. + if (stat == MP_IMPORT_STAT_NO_EXIST) { + module_obj = mp_module_get_builtin(level_mod_name, true); + if (module_obj != MP_OBJ_NULL) { + return module_obj; + } + } + } else { + DEBUG_printf("Searching for sub-module\n"); + + #if MICROPY_MODULE_BUILTIN_SUBPACKAGES + // If the outer module is a built-in (because its map is in ROM), then + // treat it like a package if it contains this submodule in its + // globals dict. + mp_obj_module_t *mod = MP_OBJ_TO_PTR(outer_module_obj); + if (mod->globals->map.is_fixed) { + elem = mp_map_lookup(&mod->globals->map, MP_OBJ_NEW_QSTR(level_mod_name), MP_MAP_LOOKUP); + // Also verify that the entry in the globals dict is in fact a module. + if (elem && mp_obj_is_type(elem->value, &mp_type_module)) { + return elem->value; + } + } + #endif + + // If the outer module is a package, it will have __path__ set. + // We can use that as the path to search inside. + mp_obj_t dest[2]; + mp_load_method_maybe(outer_module_obj, MP_QSTR___path__, dest); + if (dest[0] != MP_OBJ_NULL) { + // e.g. __path__ will be "/foo/bar" + vstr_add_str(&path, mp_obj_str_get_str(dest[0])); + + // Add the level module name to the path to get "/foo/bar/baz". + vstr_add_char(&path, PATH_SEP_CHAR[0]); + vstr_add_str(&path, qstr_str(level_mod_name)); + + stat = stat_module(&path); + } + } + + // Not already loaded, and not a built-in, so look at the stat result from the filesystem/frozen. + + if (stat == MP_IMPORT_STAT_NO_EXIST) { + // Not found -- fail. + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found")); + #else + mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), full_mod_name); + #endif + } + + // Module was found on the filesystem/frozen, try and load it. + DEBUG_printf("Found path to load: %.*s\n", (int)vstr_len(&path), vstr_str(&path)); + + // Prepare for loading from the filesystem. Create a new shell module + // and register it in sys.modules. Also make sure we remove it if + // there is any problem below. + module_obj = mp_obj_new_module(full_mod_name); + nlr_jump_callback_node_unregister_module_t ctx; + ctx.name = full_mod_name; + nlr_push_jump_callback(&ctx.callback, unregister_module_from_nlr_jump_callback); + + #if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT + // If this module is being loaded via -m on unix, then + // override __name__ to "__main__". Do this only for *modules* + // however - packages never have their names replaced, instead + // they're -m'ed using a special __main__ submodule in them. (This all + // apparently is done to not touch the package name itself, which is + // important for future imports). + if (override_main && stat != MP_IMPORT_STAT_DIR) { + mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj); + mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); + #if MICROPY_CPYTHON_COMPAT + // Store module as "__main__" in the dictionary of loaded modules (returned by sys.modules). + mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)), MP_OBJ_NEW_QSTR(MP_QSTR___main__), module_obj); + // Store real name in "__main__" attribute. Need this for + // resolving relative imports later. "__main__ was chosen + // semi-randonly, to reuse existing qstr's. + mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(full_mod_name)); + #endif + } + #endif // MICROPY_MODULE_OVERRIDE_MAIN_IMPORT + + if (stat == MP_IMPORT_STAT_DIR) { + // Directory (i.e. a package). + DEBUG_printf("%.*s is dir\n", (int)vstr_len(&path), vstr_str(&path)); + + // Store the __path__ attribute onto this module. + // https://docs.python.org/3/reference/import.html + // "Specifically, any module that contains a __path__ attribute is considered a package." + // This gets used later to locate any subpackages of this module. + mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path))); + size_t orig_path_len = path.len; + vstr_add_str(&path, PATH_SEP_CHAR "__init__.py"); + + // execute "path/__init__.py" (if available). + if (stat_file_py_or_mpy(&path) == MP_IMPORT_STAT_FILE) { + do_load(MP_OBJ_TO_PTR(module_obj), &path); + } else { + // No-op. Nothing to load. + // mp_warning("%s is imported as namespace package", vstr_str(&path)); + } + // Remove /__init__.py suffix from path. + path.len = orig_path_len; + } else { // MP_IMPORT_STAT_FILE + // File -- execute "path.(m)py". + do_load(MP_OBJ_TO_PTR(module_obj), &path); + // Note: This should be the last component in the import path. If + // there are remaining components then in the next call to + // process_import_at_level will detect that it doesn't have + // a __path__ attribute, and not attempt to stat it. + } + + if (outer_module_obj != MP_OBJ_NULL) { + // If it's a sub-module then make it available on the parent module. + mp_store_attr(outer_module_obj, level_mod_name, module_obj); + } + + nlr_pop_jump_callback(false); + + return module_obj; +} + +mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { + #if DEBUG_PRINT + DEBUG_printf("__import__:\n"); + for (size_t i = 0; i < n_args; i++) { + DEBUG_printf(" "); + mp_obj_print_helper(MICROPY_DEBUG_PRINTER, args[i], PRINT_REPR); + DEBUG_printf("\n"); + } + #endif + + // This is the import path, with any leading dots stripped. + // "import foo.bar" --> module_name="foo.bar" + // "from foo.bar import baz" --> module_name="foo.bar" + // "from . import foo" --> module_name="" + // "from ...foo.bar import baz" --> module_name="foo.bar" + mp_obj_t module_name_obj = args[0]; + + // These are the imported names. + // i.e. "from foo.bar import baz, zap" --> fromtuple=("baz", "zap",) + // Note: There's a special case on the Unix port, where this is set to mp_const_false which means that it's __main__. + mp_obj_t fromtuple = mp_const_none; + + // Level is the number of leading dots in a relative import. + // i.e. "from . import foo" --> level=1 + // i.e. "from ...foo.bar import baz" --> level=3 + mp_int_t level = 0; + if (n_args >= 4) { + fromtuple = args[3]; + if (n_args >= 5) { + level = MP_OBJ_SMALL_INT_VALUE(args[4]); + if (level < 0) { + mp_raise_ValueError(NULL); + } + } + } + + size_t module_name_len; + const char *module_name = mp_obj_str_get_data(module_name_obj, &module_name_len); + + if (level != 0) { + // Turn "foo.bar" with level=3 into ".foo.bar". + // Current module name is extracted from globals().__name__. + evaluate_relative_import(level, &module_name, &module_name_len); + // module_name is now an absolute module path. + } + + if (module_name_len == 0) { + mp_raise_ValueError(NULL); + } + + DEBUG_printf("Starting module search for '%s'\n", module_name); + + mp_obj_t top_module_obj = MP_OBJ_NULL; + mp_obj_t outer_module_obj = MP_OBJ_NULL; + + // Iterate the absolute path, finding the end of each component of the path. + // foo.bar.baz + // ^ ^ ^ + size_t current_component_start = 0; + for (size_t i = 1; i <= module_name_len; i++) { + if (i == module_name_len || module_name[i] == '.') { + // The module name up to this depth (e.g. foo.bar.baz). + qstr full_mod_name = qstr_from_strn(module_name, i); + // The current level name (e.g. baz). + qstr level_mod_name = qstr_from_strn(module_name + current_component_start, i - current_component_start); + + DEBUG_printf("Processing module: '%s' at level '%s'\n", qstr_str(full_mod_name), qstr_str(level_mod_name)); + + #if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT + // On unix, if this is being loaded via -m (indicated by sentinel + // fromtuple=mp_const_false), then handle that if it's the final + // component. + bool override_main = (i == module_name_len && fromtuple == mp_const_false); + #else + bool override_main = false; + #endif + + // Import this module. + mp_obj_t module_obj = process_import_at_level(full_mod_name, level_mod_name, outer_module_obj, override_main); + + // Set this as the parent module, and remember the top-level module if it's the first. + outer_module_obj = module_obj; + if (top_module_obj == MP_OBJ_NULL) { + top_module_obj = module_obj; + } + + current_component_start = i + 1; + } + } + + if (fromtuple != mp_const_none) { + // If fromtuple is not empty, return leaf module + return outer_module_obj; + } else { + // Otherwise, we need to return top-level package + return top_module_obj; + } +} + +#else // MICROPY_ENABLE_EXTERNAL_IMPORT + +mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { + // Check that it's not a relative import. + if (n_args >= 5 && MP_OBJ_SMALL_INT_VALUE(args[4]) != 0) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("relative import")); + } + + // Check if the module is already loaded. + mp_map_elem_t *elem = mp_map_lookup(&MP_STATE_VM(mp_loaded_modules_dict).map, args[0], MP_MAP_LOOKUP); + if (elem) { + return elem->value; + } + + // Try the name directly as a non-extensible built-in (e.g. `micropython`). + qstr module_name_qstr = mp_obj_str_get_qstr(args[0]); + mp_obj_t module_obj = mp_module_get_builtin(module_name_qstr, false); + if (module_obj != MP_OBJ_NULL) { + return module_obj; + } + // Now try as an extensible built-in (e.g. `time`). + module_obj = mp_module_get_builtin(module_name_qstr, true); + if (module_obj != MP_OBJ_NULL) { + return module_obj; + } + + // Couldn't find the module, so fail + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found")); + #else + mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), module_name_qstr); + #endif +} + +#endif // MICROPY_ENABLE_EXTERNAL_IMPORT + +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin___import___obj, 1, 5, mp_builtin___import__); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/compile.c b/non_catalog_apps/mp_flipper/lib/micropython/py/compile.c new file mode 100644 index 00000000..62757de3 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/compile.c @@ -0,0 +1,3680 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Damien P. George + * + * 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 "py/scope.h" +#include "py/emit.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/asmbase.h" +#include "py/nativeglue.h" +#include "py/persistentcode.h" +#include "py/smallint.h" + +#if MICROPY_ENABLE_COMPILER + +#define INVALID_LABEL (0xffff) + +typedef enum { +// define rules with a compile function +#define DEF_RULE(rule, comp, kind, ...) PN_##rule, +#define DEF_RULE_NC(rule, kind, ...) + #include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + PN_const_object, // special node for a constant, generic Python object +// define rules without a compile function +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) PN_##rule, + #include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +} pn_kind_t; + +// Whether a mp_parse_node_struct_t that has pns->kind == PN_testlist_comp +// corresponds to a list comprehension or generator. +#define MP_PARSE_NODE_TESTLIST_COMP_HAS_COMP_FOR(pns) \ + (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2 && \ + MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for)) + +#define NEED_METHOD_TABLE MICROPY_EMIT_NATIVE + +#if NEED_METHOD_TABLE + +// we need a method table to do the lookup for the emitter functions +#define EMIT(fun) (comp->emit_method_table->fun(comp->emit)) +#define EMIT_ARG(fun, ...) (comp->emit_method_table->fun(comp->emit, __VA_ARGS__)) +#define EMIT_LOAD_FAST(qst, local_num) (comp->emit_method_table->load_id.local(comp->emit, qst, local_num, MP_EMIT_IDOP_LOCAL_FAST)) +#define EMIT_LOAD_GLOBAL(qst) (comp->emit_method_table->load_id.global(comp->emit, qst, MP_EMIT_IDOP_GLOBAL_GLOBAL)) + +#else + +// if we only have the bytecode emitter enabled then we can do a direct call to the functions +#define EMIT(fun) (mp_emit_bc_##fun(comp->emit)) +#define EMIT_ARG(fun, ...) (mp_emit_bc_##fun(comp->emit, __VA_ARGS__)) +#define EMIT_LOAD_FAST(qst, local_num) (mp_emit_bc_load_local(comp->emit, qst, local_num, MP_EMIT_IDOP_LOCAL_FAST)) +#define EMIT_LOAD_GLOBAL(qst) (mp_emit_bc_load_global(comp->emit, qst, MP_EMIT_IDOP_GLOBAL_GLOBAL)) + +#endif + +#if MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER + +#define NATIVE_EMITTER(f) emit_native_table[mp_dynamic_compiler.native_arch]->emit_##f +#define NATIVE_EMITTER_TABLE (emit_native_table[mp_dynamic_compiler.native_arch]) + +static const emit_method_table_t *emit_native_table[] = { + NULL, + &emit_native_x86_method_table, + &emit_native_x64_method_table, + &emit_native_arm_method_table, + &emit_native_thumb_method_table, + &emit_native_thumb_method_table, + &emit_native_thumb_method_table, + &emit_native_thumb_method_table, + &emit_native_thumb_method_table, + &emit_native_xtensa_method_table, + &emit_native_xtensawin_method_table, +}; + +#elif MICROPY_EMIT_NATIVE +// define a macro to access external native emitter +#if MICROPY_EMIT_X64 +#define NATIVE_EMITTER(f) emit_native_x64_##f +#elif MICROPY_EMIT_X86 +#define NATIVE_EMITTER(f) emit_native_x86_##f +#elif MICROPY_EMIT_THUMB +#define NATIVE_EMITTER(f) emit_native_thumb_##f +#elif MICROPY_EMIT_ARM +#define NATIVE_EMITTER(f) emit_native_arm_##f +#elif MICROPY_EMIT_XTENSA +#define NATIVE_EMITTER(f) emit_native_xtensa_##f +#elif MICROPY_EMIT_XTENSAWIN +#define NATIVE_EMITTER(f) emit_native_xtensawin_##f +#else +#error "unknown native emitter" +#endif +#define NATIVE_EMITTER_TABLE (&NATIVE_EMITTER(method_table)) +#endif + +#if MICROPY_EMIT_INLINE_ASM && MICROPY_DYNAMIC_COMPILER + +#define ASM_EMITTER(f) emit_asm_table[mp_dynamic_compiler.native_arch]->asm_##f +#define ASM_EMITTER_TABLE emit_asm_table[mp_dynamic_compiler.native_arch] + +static const emit_inline_asm_method_table_t *emit_asm_table[] = { + NULL, + NULL, + NULL, + &emit_inline_thumb_method_table, + &emit_inline_thumb_method_table, + &emit_inline_thumb_method_table, + &emit_inline_thumb_method_table, + &emit_inline_thumb_method_table, + &emit_inline_thumb_method_table, + &emit_inline_xtensa_method_table, + NULL, +}; + +#elif MICROPY_EMIT_INLINE_ASM +// define macros for inline assembler +#if MICROPY_EMIT_INLINE_THUMB +#define ASM_DECORATOR_QSTR MP_QSTR_asm_thumb +#define ASM_EMITTER(f) emit_inline_thumb_##f +#elif MICROPY_EMIT_INLINE_XTENSA +#define ASM_DECORATOR_QSTR MP_QSTR_asm_xtensa +#define ASM_EMITTER(f) emit_inline_xtensa_##f +#else +#error "unknown asm emitter" +#endif +#define ASM_EMITTER_TABLE &ASM_EMITTER(method_table) +#endif + +#define EMIT_INLINE_ASM(fun) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm)) +#define EMIT_INLINE_ASM_ARG(fun, ...) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm, __VA_ARGS__)) + +// elements in this struct are ordered to make it compact +typedef struct _compiler_t { + uint8_t is_repl; + uint8_t pass; // holds enum type pass_kind_t + uint8_t have_star; + + // try to keep compiler clean from nlr + mp_obj_t compile_error; // set to an exception object if there's an error + size_t compile_error_line; // set to best guess of line of error + + uint next_label; + + uint16_t num_dict_params; + uint16_t num_default_params; + + uint16_t break_label; // highest bit set indicates we are breaking out of a for loop + uint16_t continue_label; + uint16_t cur_except_level; // increased for SETUP_EXCEPT, SETUP_FINALLY; decreased for POP_BLOCK, POP_EXCEPT + uint16_t break_continue_except_level; + + scope_t *scope_head; + scope_t *scope_cur; + + emit_t *emit; // current emitter + #if NEED_METHOD_TABLE + const emit_method_table_t *emit_method_table; // current emit method table + #endif + + #if MICROPY_EMIT_INLINE_ASM + emit_inline_asm_t *emit_inline_asm; // current emitter for inline asm + const emit_inline_asm_method_table_t *emit_inline_asm_method_table; // current emit method table for inline asm + #endif + + mp_emit_common_t emit_common; +} compiler_t; + +#if MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT +bool mp_compile_allow_top_level_await = false; +#endif + +/******************************************************************************/ +// mp_emit_common_t helper functions +// These are defined here so they can be inlined, to reduce code size. + +static void mp_emit_common_init(mp_emit_common_t *emit, qstr source_file) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + mp_map_init(&emit->qstr_map, 1); + + // add the source file as the first entry in the qstr table + mp_map_elem_t *elem = mp_map_lookup(&emit->qstr_map, MP_OBJ_NEW_QSTR(source_file), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + elem->value = MP_OBJ_NEW_SMALL_INT(0); + #endif + mp_obj_list_init(&emit->const_obj_list, 0); +} + +static void mp_emit_common_start_pass(mp_emit_common_t *emit, pass_kind_t pass) { + emit->pass = pass; + if (pass == MP_PASS_CODE_SIZE) { + if (emit->ct_cur_child == 0) { + emit->children = NULL; + } else { + emit->children = m_new0(mp_raw_code_t *, emit->ct_cur_child); + } + } + emit->ct_cur_child = 0; +} + +static void mp_emit_common_populate_module_context(mp_emit_common_t *emit, qstr source_file, mp_module_context_t *context) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + size_t qstr_map_used = emit->qstr_map.used; + mp_module_context_alloc_tables(context, qstr_map_used, emit->const_obj_list.len); + for (size_t i = 0; i < emit->qstr_map.alloc; ++i) { + if (mp_map_slot_is_filled(&emit->qstr_map, i)) { + size_t idx = MP_OBJ_SMALL_INT_VALUE(emit->qstr_map.table[i].value); + qstr qst = MP_OBJ_QSTR_VALUE(emit->qstr_map.table[i].key); + context->constants.qstr_table[idx] = qst; + } + } + #else + mp_module_context_alloc_tables(context, 0, emit->const_obj_list.len); + context->constants.source_file = source_file; + #endif + + for (size_t i = 0; i < emit->const_obj_list.len; ++i) { + context->constants.obj_table[i] = emit->const_obj_list.items[i]; + } +} + +/******************************************************************************/ + +static void compile_error_set_line(compiler_t *comp, mp_parse_node_t pn) { + // if the line of the error is unknown then try to update it from the pn + if (comp->compile_error_line == 0 && MP_PARSE_NODE_IS_STRUCT(pn)) { + comp->compile_error_line = ((mp_parse_node_struct_t *)pn)->source_line; + } +} + +static void compile_syntax_error(compiler_t *comp, mp_parse_node_t pn, mp_rom_error_text_t msg) { + // only register the error if there has been no other error + if (comp->compile_error == MP_OBJ_NULL) { + comp->compile_error = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); + compile_error_set_line(comp, pn); + } +} + +static void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra); +static void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind); +static void compile_atom_brace_helper(compiler_t *comp, mp_parse_node_struct_t *pns, bool create_map); +static void compile_node(compiler_t *comp, mp_parse_node_t pn); + +static uint comp_next_label(compiler_t *comp) { + return comp->next_label++; +} + +#if MICROPY_EMIT_NATIVE +static void reserve_labels_for_native(compiler_t *comp, int n) { + if (comp->scope_cur->emit_options != MP_EMIT_OPT_BYTECODE) { + comp->next_label += n; + } +} +#else +#define reserve_labels_for_native(comp, n) +#endif + +static void compile_increase_except_level(compiler_t *comp, uint label, int kind) { + EMIT_ARG(setup_block, label, kind); + comp->cur_except_level += 1; + if (comp->cur_except_level > comp->scope_cur->exc_stack_size) { + comp->scope_cur->exc_stack_size = comp->cur_except_level; + } +} + +static void compile_decrease_except_level(compiler_t *comp) { + assert(comp->cur_except_level > 0); + comp->cur_except_level -= 1; + EMIT(end_finally); + reserve_labels_for_native(comp, 1); +} + +static scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, mp_parse_node_t pn, uint emit_options) { + scope_t *scope = scope_new(kind, pn, emit_options); + scope->parent = comp->scope_cur; + scope->next = NULL; + if (comp->scope_head == NULL) { + comp->scope_head = scope; + } else { + scope_t *s = comp->scope_head; + while (s->next != NULL) { + s = s->next; + } + s->next = scope; + } + return scope; +} + +typedef void (*apply_list_fun_t)(compiler_t *comp, mp_parse_node_t pn); + +static void apply_to_single_or_list(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_list_kind, apply_list_fun_t f) { + if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, pn_list_kind)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + for (int i = 0; i < num_nodes; i++) { + f(comp, pns->nodes[i]); + } + } else if (!MP_PARSE_NODE_IS_NULL(pn)) { + f(comp, pn); + } +} + +static void compile_generic_all_nodes(compiler_t *comp, mp_parse_node_struct_t *pns) { + int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + for (int i = 0; i < num_nodes; i++) { + compile_node(comp, pns->nodes[i]); + if (comp->compile_error != MP_OBJ_NULL) { + // add line info for the error in case it didn't have a line number + compile_error_set_line(comp, pns->nodes[i]); + return; + } + } +} + +static void compile_load_id(compiler_t *comp, qstr qst) { + if (comp->pass == MP_PASS_SCOPE) { + mp_emit_common_get_id_for_load(comp->scope_cur, qst); + } else { + #if NEED_METHOD_TABLE + mp_emit_common_id_op(comp->emit, &comp->emit_method_table->load_id, comp->scope_cur, qst); + #else + mp_emit_common_id_op(comp->emit, &mp_emit_bc_method_table_load_id_ops, comp->scope_cur, qst); + #endif + } +} + +static void compile_store_id(compiler_t *comp, qstr qst) { + if (comp->pass == MP_PASS_SCOPE) { + mp_emit_common_get_id_for_modification(comp->scope_cur, qst); + } else { + #if NEED_METHOD_TABLE + mp_emit_common_id_op(comp->emit, &comp->emit_method_table->store_id, comp->scope_cur, qst); + #else + mp_emit_common_id_op(comp->emit, &mp_emit_bc_method_table_store_id_ops, comp->scope_cur, qst); + #endif + } +} + +static void compile_delete_id(compiler_t *comp, qstr qst) { + if (comp->pass == MP_PASS_SCOPE) { + mp_emit_common_get_id_for_modification(comp->scope_cur, qst); + } else { + #if NEED_METHOD_TABLE + mp_emit_common_id_op(comp->emit, &comp->emit_method_table->delete_id, comp->scope_cur, qst); + #else + mp_emit_common_id_op(comp->emit, &mp_emit_bc_method_table_delete_id_ops, comp->scope_cur, qst); + #endif + } +} + +static void compile_generic_tuple(compiler_t *comp, mp_parse_node_struct_t *pns) { + // a simple tuple expression + size_t num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + for (size_t i = 0; i < num_nodes; i++) { + compile_node(comp, pns->nodes[i]); + } + EMIT_ARG(build, num_nodes, MP_EMIT_BUILD_TUPLE); +} + +static void c_if_cond(compiler_t *comp, mp_parse_node_t pn, bool jump_if, int label) { + if (mp_parse_node_is_const_false(pn)) { + if (jump_if == false) { + EMIT_ARG(jump, label); + } + return; + } else if (mp_parse_node_is_const_true(pn)) { + if (jump_if == true) { + EMIT_ARG(jump, label); + } + return; + } else if (MP_PARSE_NODE_IS_STRUCT(pn)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) { + if (jump_if == false) { + and_or_logic1:; + uint label2 = comp_next_label(comp); + for (int i = 0; i < n - 1; i++) { + c_if_cond(comp, pns->nodes[i], !jump_if, label2); + } + c_if_cond(comp, pns->nodes[n - 1], jump_if, label); + EMIT_ARG(label_assign, label2); + } else { + and_or_logic2: + for (int i = 0; i < n; i++) { + c_if_cond(comp, pns->nodes[i], jump_if, label); + } + } + return; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) { + if (jump_if == false) { + goto and_or_logic2; + } else { + goto and_or_logic1; + } + } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) { + c_if_cond(comp, pns->nodes[0], !jump_if, label); + return; + } + } + + // nothing special, fall back to default compiling for node and jump + compile_node(comp, pn); + EMIT_ARG(pop_jump_if, jump_if, label); +} + +typedef enum { ASSIGN_STORE, ASSIGN_AUG_LOAD, ASSIGN_AUG_STORE } assign_kind_t; +static void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t kind); + +static void c_assign_atom_expr(compiler_t *comp, mp_parse_node_struct_t *pns, assign_kind_t assign_kind) { + if (assign_kind != ASSIGN_AUG_STORE) { + compile_node(comp, pns->nodes[0]); + } + + if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) { + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1]; + if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_atom_expr_trailers) { + int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1); + if (assign_kind != ASSIGN_AUG_STORE) { + for (int i = 0; i < n - 1; i++) { + compile_node(comp, pns1->nodes[i]); + } + } + assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1])); + pns1 = (mp_parse_node_struct_t *)pns1->nodes[n - 1]; + } + if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) { + if (assign_kind == ASSIGN_AUG_STORE) { + EMIT(rot_three); + EMIT_ARG(subscr, MP_EMIT_SUBSCR_STORE); + } else { + compile_node(comp, pns1->nodes[0]); + if (assign_kind == ASSIGN_AUG_LOAD) { + EMIT(dup_top_two); + EMIT_ARG(subscr, MP_EMIT_SUBSCR_LOAD); + } else { + EMIT_ARG(subscr, MP_EMIT_SUBSCR_STORE); + } + } + return; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) { + assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); + if (assign_kind == ASSIGN_AUG_LOAD) { + EMIT(dup_top); + EMIT_ARG(attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]), MP_EMIT_ATTR_LOAD); + } else { + if (assign_kind == ASSIGN_AUG_STORE) { + EMIT(rot_two); + } + EMIT_ARG(attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]), MP_EMIT_ATTR_STORE); + } + return; + } + } + + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("can't assign to expression")); +} + +static void c_assign_tuple(compiler_t *comp, uint num_tail, mp_parse_node_t *nodes_tail) { + // look for star expression + uint have_star_index = -1; + for (uint i = 0; i < num_tail; i++) { + if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes_tail[i], PN_star_expr)) { + if (have_star_index == (uint)-1) { + EMIT_ARG(unpack_ex, i, num_tail - i - 1); + have_star_index = i; + } else { + compile_syntax_error(comp, nodes_tail[i], MP_ERROR_TEXT("multiple *x in assignment")); + return; + } + } + } + if (have_star_index == (uint)-1) { + EMIT_ARG(unpack_sequence, num_tail); + } + for (uint i = 0; i < num_tail; i++) { + if (i == have_star_index) { + c_assign(comp, ((mp_parse_node_struct_t *)nodes_tail[i])->nodes[0], ASSIGN_STORE); + } else { + c_assign(comp, nodes_tail[i], ASSIGN_STORE); + } + } +} + +// assigns top of stack to pn +static void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t assign_kind) { + assert(!MP_PARSE_NODE_IS_NULL(pn)); + if (MP_PARSE_NODE_IS_LEAF(pn)) { + if (MP_PARSE_NODE_IS_ID(pn)) { + qstr arg = MP_PARSE_NODE_LEAF_ARG(pn); + switch (assign_kind) { + case ASSIGN_STORE: + case ASSIGN_AUG_STORE: + compile_store_id(comp, arg); + break; + case ASSIGN_AUG_LOAD: + default: + compile_load_id(comp, arg); + break; + } + } else { + goto cannot_assign; + } + } else { + // pn must be a struct + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + switch (MP_PARSE_NODE_STRUCT_KIND(pns)) { + case PN_atom_expr_normal: + // lhs is an index or attribute + c_assign_atom_expr(comp, pns, assign_kind); + break; + + case PN_testlist_star_expr: + case PN_exprlist: + // lhs is a tuple + if (assign_kind != ASSIGN_STORE) { + goto cannot_assign; + } + c_assign_tuple(comp, MP_PARSE_NODE_STRUCT_NUM_NODES(pns), pns->nodes); + break; + + case PN_atom_paren: + // lhs is something in parenthesis + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // empty tuple + goto cannot_assign; + } else { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)); + if (assign_kind != ASSIGN_STORE) { + goto cannot_assign; + } + pns = (mp_parse_node_struct_t *)pns->nodes[0]; + goto testlist_comp; + } + break; + + case PN_atom_bracket: + // lhs is something in brackets + if (assign_kind != ASSIGN_STORE) { + goto cannot_assign; + } + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // empty list, assignment allowed + c_assign_tuple(comp, 0, NULL); + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) { + pns = (mp_parse_node_struct_t *)pns->nodes[0]; + goto testlist_comp; + } else { + // brackets around 1 item + c_assign_tuple(comp, 1, pns->nodes); + } + break; + + default: + goto cannot_assign; + } + return; + + testlist_comp: + // lhs is a sequence + if (MP_PARSE_NODE_TESTLIST_COMP_HAS_COMP_FOR(pns)) { + goto cannot_assign; + } + c_assign_tuple(comp, MP_PARSE_NODE_STRUCT_NUM_NODES(pns), pns->nodes); + return; + } + return; + +cannot_assign: + compile_syntax_error(comp, pn, MP_ERROR_TEXT("can't assign to expression")); +} + +// stuff for lambda and comprehensions and generators: +// if n_pos_defaults > 0 then there is a tuple on the stack with the positional defaults +// if n_kw_defaults > 0 then there is a dictionary on the stack with the keyword defaults +// if both exist, the tuple is above the dictionary (ie the first pop gets the tuple) +static void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_pos_defaults, int n_kw_defaults) { + assert(n_pos_defaults >= 0); + assert(n_kw_defaults >= 0); + + // set flags + if (n_kw_defaults > 0) { + this_scope->scope_flags |= MP_SCOPE_FLAG_DEFKWARGS; + } + this_scope->num_def_pos_args = n_pos_defaults; + + #if MICROPY_EMIT_NATIVE + // When creating a function/closure it will take a reference to the current globals + comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_REFGLOBALS | MP_SCOPE_FLAG_HASCONSTS; + #endif + + // make closed over variables, if any + // ensure they are closed over in the order defined in the outer scope (mainly to agree with CPython) + int nfree = 0; + if (comp->scope_cur->kind != SCOPE_MODULE) { + for (int i = 0; i < comp->scope_cur->id_info_len; i++) { + id_info_t *id = &comp->scope_cur->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) { + for (int j = 0; j < this_scope->id_info_len; j++) { + id_info_t *id2 = &this_scope->id_info[j]; + if (id2->kind == ID_INFO_KIND_FREE && id->qst == id2->qst) { + // in MicroPython we load closures using LOAD_FAST + EMIT_LOAD_FAST(id->qst, id->local_num); + nfree += 1; + } + } + } + } + } + + // make the function/closure + if (nfree == 0) { + EMIT_ARG(make_function, this_scope, n_pos_defaults, n_kw_defaults); + } else { + EMIT_ARG(make_closure, this_scope, nfree, n_pos_defaults, n_kw_defaults); + } +} + +static void compile_funcdef_lambdef_param(compiler_t *comp, mp_parse_node_t pn) { + // For efficiency of the code below we extract the parse-node kind here + int pn_kind; + if (MP_PARSE_NODE_IS_ID(pn)) { + pn_kind = -1; + } else { + assert(MP_PARSE_NODE_IS_STRUCT(pn)); + pn_kind = MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)pn); + } + + if (pn_kind == PN_typedargslist_star || pn_kind == PN_varargslist_star) { + comp->have_star = true; + /* don't need to distinguish bare from named star + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn; + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // bare star + } else { + // named star + } + */ + + } else if (pn_kind == PN_typedargslist_dbl_star || pn_kind == PN_varargslist_dbl_star) { + // named double star + // TODO do we need to do anything with this? + + } else { + mp_parse_node_t pn_id; + mp_parse_node_t pn_equal; + if (pn_kind == -1) { + // this parameter is just an id + + pn_id = pn; + pn_equal = MP_PARSE_NODE_NULL; + + } else if (pn_kind == PN_typedargslist_name) { + // this parameter has a colon and/or equal specifier + + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + pn_id = pns->nodes[0]; + // pn_colon = pns->nodes[1]; // unused + pn_equal = pns->nodes[2]; + + } else { + assert(pn_kind == PN_varargslist_name); // should be + // this parameter has an equal specifier + + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + pn_id = pns->nodes[0]; + pn_equal = pns->nodes[1]; + } + + if (MP_PARSE_NODE_IS_NULL(pn_equal)) { + // this parameter does not have a default value + + // check for non-default parameters given after default parameters (allowed by parser, but not syntactically valid) + if (!comp->have_star && comp->num_default_params != 0) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("non-default argument follows default argument")); + return; + } + + } else { + // this parameter has a default value + // in CPython, None (and True, False?) as default parameters are loaded with LOAD_NAME; don't understandy why + + if (comp->have_star) { + comp->num_dict_params += 1; + // in MicroPython we put the default dict parameters into a dictionary using the bytecode + if (comp->num_dict_params == 1) { + // in MicroPython we put the default positional parameters into a tuple using the bytecode + // we need to do this here before we start building the map for the default keywords + if (comp->num_default_params > 0) { + EMIT_ARG(build, comp->num_default_params, MP_EMIT_BUILD_TUPLE); + } else { + EMIT(load_null); // sentinel indicating empty default positional args + } + // first default dict param, so make the map + EMIT_ARG(build, 0, MP_EMIT_BUILD_MAP); + } + + // compile value then key, then store it to the dict + compile_node(comp, pn_equal); + EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id)); + EMIT(store_map); + } else { + comp->num_default_params += 1; + compile_node(comp, pn_equal); + } + } + } +} + +static void compile_funcdef_lambdef(compiler_t *comp, scope_t *scope, mp_parse_node_t pn_params, pn_kind_t pn_list_kind) { + // When we call compile_funcdef_lambdef_param below it can compile an arbitrary + // expression for default arguments, which may contain a lambda. The lambda will + // call here in a nested way, so we must save and restore the relevant state. + bool orig_have_star = comp->have_star; + uint16_t orig_num_dict_params = comp->num_dict_params; + uint16_t orig_num_default_params = comp->num_default_params; + + // compile default parameters + comp->have_star = false; + comp->num_dict_params = 0; + comp->num_default_params = 0; + apply_to_single_or_list(comp, pn_params, pn_list_kind, compile_funcdef_lambdef_param); + + if (comp->compile_error != MP_OBJ_NULL) { + return; + } + + // in MicroPython we put the default positional parameters into a tuple using the bytecode + // the default keywords args may have already made the tuple; if not, do it now + if (comp->num_default_params > 0 && comp->num_dict_params == 0) { + EMIT_ARG(build, comp->num_default_params, MP_EMIT_BUILD_TUPLE); + EMIT(load_null); // sentinel indicating empty default keyword args + } + + // make the function + close_over_variables_etc(comp, scope, comp->num_default_params, comp->num_dict_params); + + // restore state + comp->have_star = orig_have_star; + comp->num_dict_params = orig_num_dict_params; + comp->num_default_params = orig_num_default_params; +} + +// leaves function object on stack +// returns function name +static qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) { + if (comp->pass == MP_PASS_SCOPE) { + // create a new scope for this function + scope_t *s = scope_new_and_link(comp, SCOPE_FUNCTION, (mp_parse_node_t)pns, emit_options); + // store the function scope so the compiling function can use it at each pass + pns->nodes[4] = (mp_parse_node_t)s; + } + + // get the scope for this function + scope_t *fscope = (scope_t *)pns->nodes[4]; + + // compile the function definition + compile_funcdef_lambdef(comp, fscope, pns->nodes[1], PN_typedargslist); + + // return its name (the 'f' in "def f(...):") + return fscope->simple_name; +} + +// leaves class object on stack +// returns class name +static qstr compile_classdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) { + if (comp->pass == MP_PASS_SCOPE) { + // create a new scope for this class + scope_t *s = scope_new_and_link(comp, SCOPE_CLASS, (mp_parse_node_t)pns, emit_options); + // store the class scope so the compiling function can use it at each pass + pns->nodes[3] = (mp_parse_node_t)s; + } + + EMIT(load_build_class); + + // scope for this class + scope_t *cscope = (scope_t *)pns->nodes[3]; + + // compile the class + close_over_variables_etc(comp, cscope, 0, 0); + + // get its name + EMIT_ARG(load_const_str, cscope->simple_name); + + // nodes[1] has parent classes, if any + // empty parenthesis (eg class C():) gets here as an empty PN_classdef_2 and needs special handling + mp_parse_node_t parents = pns->nodes[1]; + if (MP_PARSE_NODE_IS_STRUCT_KIND(parents, PN_classdef_2)) { + parents = MP_PARSE_NODE_NULL; + } + compile_trailer_paren_helper(comp, parents, false, 2); + + // return its name (the 'C' in class C(...):") + return cscope->simple_name; +} + +// returns true if it was a built-in decorator (even if the built-in had an error) +static bool compile_built_in_decorator(compiler_t *comp, size_t name_len, mp_parse_node_t *name_nodes, uint *emit_options) { + if (MP_PARSE_NODE_LEAF_ARG(name_nodes[0]) != MP_QSTR_micropython) { + return false; + } + + if (name_len != 2) { + compile_syntax_error(comp, name_nodes[0], MP_ERROR_TEXT("invalid micropython decorator")); + return true; + } + + qstr attr = MP_PARSE_NODE_LEAF_ARG(name_nodes[1]); + if (attr == MP_QSTR_bytecode) { + *emit_options = MP_EMIT_OPT_BYTECODE; + #if MICROPY_EMIT_NATIVE + } else if (attr == MP_QSTR_native) { + *emit_options = MP_EMIT_OPT_NATIVE_PYTHON; + } else if (attr == MP_QSTR_viper) { + *emit_options = MP_EMIT_OPT_VIPER; + #endif + #if MICROPY_EMIT_INLINE_ASM + #if MICROPY_DYNAMIC_COMPILER + } else if (attr == MP_QSTR_asm_thumb) { + *emit_options = MP_EMIT_OPT_ASM; + } else if (attr == MP_QSTR_asm_xtensa) { + *emit_options = MP_EMIT_OPT_ASM; + #else + } else if (attr == ASM_DECORATOR_QSTR) { + *emit_options = MP_EMIT_OPT_ASM; + #endif + #endif + } else { + compile_syntax_error(comp, name_nodes[1], MP_ERROR_TEXT("invalid micropython decorator")); + } + + #if MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER + if (*emit_options == MP_EMIT_OPT_NATIVE_PYTHON || *emit_options == MP_EMIT_OPT_VIPER) { + if (emit_native_table[mp_dynamic_compiler.native_arch] == NULL) { + compile_syntax_error(comp, name_nodes[1], MP_ERROR_TEXT("invalid arch")); + } + } else if (*emit_options == MP_EMIT_OPT_ASM) { + if (emit_asm_table[mp_dynamic_compiler.native_arch] == NULL) { + compile_syntax_error(comp, name_nodes[1], MP_ERROR_TEXT("invalid arch")); + } + } + #endif + + return true; +} + +static void compile_decorated(compiler_t *comp, mp_parse_node_struct_t *pns) { + // get the list of decorators + mp_parse_node_t *nodes; + size_t n = mp_parse_node_extract_list(&pns->nodes[0], PN_decorators, &nodes); + + // inherit emit options for this function/class definition + uint emit_options = comp->scope_cur->emit_options; + + // compile each decorator + size_t num_built_in_decorators = 0; + for (size_t i = 0; i < n; i++) { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_decorator)); // should be + mp_parse_node_struct_t *pns_decorator = (mp_parse_node_struct_t *)nodes[i]; + + // nodes[0] contains the decorator function, which is a dotted name + mp_parse_node_t *name_nodes; + size_t name_len = mp_parse_node_extract_list(&pns_decorator->nodes[0], PN_dotted_name, &name_nodes); + + // check for built-in decorators + if (compile_built_in_decorator(comp, name_len, name_nodes, &emit_options)) { + // this was a built-in + num_built_in_decorators += 1; + + } else { + // not a built-in, compile normally + + // compile the decorator function + compile_node(comp, name_nodes[0]); + for (size_t j = 1; j < name_len; j++) { + assert(MP_PARSE_NODE_IS_ID(name_nodes[j])); // should be + EMIT_ARG(attr, MP_PARSE_NODE_LEAF_ARG(name_nodes[j]), MP_EMIT_ATTR_LOAD); + } + + // nodes[1] contains arguments to the decorator function, if any + if (!MP_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) { + // call the decorator function with the arguments in nodes[1] + compile_node(comp, pns_decorator->nodes[1]); + } + } + } + + // compile the body (funcdef, async funcdef or classdef) and get its name + mp_parse_node_struct_t *pns_body = (mp_parse_node_struct_t *)pns->nodes[1]; + qstr body_name = 0; + if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_funcdef) { + body_name = compile_funcdef_helper(comp, pns_body, emit_options); + #if MICROPY_PY_ASYNC_AWAIT + } else if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_async_funcdef) { + assert(MP_PARSE_NODE_IS_STRUCT(pns_body->nodes[0])); + mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns_body->nodes[0]; + body_name = compile_funcdef_helper(comp, pns0, emit_options); + scope_t *fscope = (scope_t *)pns0->nodes[4]; + fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR; + #endif + } else { + assert(MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_classdef); // should be + body_name = compile_classdef_helper(comp, pns_body, emit_options); + } + + // call each decorator + for (size_t i = 0; i < n - num_built_in_decorators; i++) { + EMIT_ARG(call_function, 1, 0, 0); + } + + // store func/class object into name + compile_store_id(comp, body_name); +} + +static void compile_funcdef(compiler_t *comp, mp_parse_node_struct_t *pns) { + qstr fname = compile_funcdef_helper(comp, pns, comp->scope_cur->emit_options); + // store function object into function name + compile_store_id(comp, fname); +} + +static void c_del_stmt(compiler_t *comp, mp_parse_node_t pn) { + if (MP_PARSE_NODE_IS_ID(pn)) { + compile_delete_id(comp, MP_PARSE_NODE_LEAF_ARG(pn)); + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_expr_normal)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + + compile_node(comp, pns->nodes[0]); // base of the atom_expr_normal node + + if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) { + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1]; + if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_atom_expr_trailers) { + int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1); + for (int i = 0; i < n - 1; i++) { + compile_node(comp, pns1->nodes[i]); + } + assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1])); + pns1 = (mp_parse_node_struct_t *)pns1->nodes[n - 1]; + } + if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) { + compile_node(comp, pns1->nodes[0]); + EMIT_ARG(subscr, MP_EMIT_SUBSCR_DELETE); + } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) { + assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); + EMIT_ARG(attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]), MP_EMIT_ATTR_DELETE); + } else { + goto cannot_delete; + } + } else { + goto cannot_delete; + } + + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_paren)) { + pn = ((mp_parse_node_struct_t *)pn)->nodes[0]; + if (MP_PARSE_NODE_IS_NULL(pn)) { + goto cannot_delete; + } else { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_testlist_comp)); + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + if (MP_PARSE_NODE_TESTLIST_COMP_HAS_COMP_FOR(pns)) { + goto cannot_delete; + } + for (size_t i = 0; i < MP_PARSE_NODE_STRUCT_NUM_NODES(pns); ++i) { + c_del_stmt(comp, pns->nodes[i]); + } + } + } else { + // some arbitrary statement that we can't delete (eg del 1) + goto cannot_delete; + } + + return; + +cannot_delete: + compile_syntax_error(comp, (mp_parse_node_t)pn, MP_ERROR_TEXT("can't delete expression")); +} + +static void compile_del_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + apply_to_single_or_list(comp, pns->nodes[0], PN_exprlist, c_del_stmt); +} + +static void compile_break_cont_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + uint16_t label; + if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_break_stmt) { + label = comp->break_label; + } else { + label = comp->continue_label; + } + if (label == INVALID_LABEL) { + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("'break'/'continue' outside loop")); + } + assert(comp->cur_except_level >= comp->break_continue_except_level); + EMIT_ARG(unwind_jump, label, comp->cur_except_level - comp->break_continue_except_level); +} + +static void compile_return_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + #if MICROPY_CPYTHON_COMPAT + if (comp->scope_cur->kind != SCOPE_FUNCTION) { + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("'return' outside function")); + return; + } + #endif + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // no argument to 'return', so return None + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + } else if (MICROPY_COMP_RETURN_IF_EXPR + && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_test_if_expr)) { + // special case when returning an if-expression; to match CPython optimisation + mp_parse_node_struct_t *pns_test_if_expr = (mp_parse_node_struct_t *)pns->nodes[0]; + mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t *)pns_test_if_expr->nodes[1]; + + uint l_fail = comp_next_label(comp); + c_if_cond(comp, pns_test_if_else->nodes[0], false, l_fail); // condition + compile_node(comp, pns_test_if_expr->nodes[0]); // success value + EMIT(return_value); + EMIT_ARG(label_assign, l_fail); + compile_node(comp, pns_test_if_else->nodes[1]); // failure value + } else { + compile_node(comp, pns->nodes[0]); + } + EMIT(return_value); +} + +static void compile_yield_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_node(comp, pns->nodes[0]); + EMIT(pop_top); +} + +static void compile_raise_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // raise + EMIT_ARG(raise_varargs, 0); + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_raise_stmt_arg)) { + // raise x from y + pns = (mp_parse_node_struct_t *)pns->nodes[0]; + compile_node(comp, pns->nodes[0]); + compile_node(comp, pns->nodes[1]); + EMIT_ARG(raise_varargs, 2); + } else { + // raise x + compile_node(comp, pns->nodes[0]); + EMIT_ARG(raise_varargs, 1); + } +} + +// q_base holds the base of the name +// eg a -> q_base=a +// a.b.c -> q_base=a +static void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) { + bool is_as = false; + if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dotted_as_name)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + // a name of the form x as y; unwrap it + *q_base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]); + pn = pns->nodes[0]; + is_as = true; + } + if (MP_PARSE_NODE_IS_NULL(pn)) { + // empty name (eg, from . import x) + *q_base = MP_QSTR_; + EMIT_ARG(import, MP_QSTR_, MP_EMIT_IMPORT_NAME); // import the empty string + } else if (MP_PARSE_NODE_IS_ID(pn)) { + // just a simple name + qstr q_full = MP_PARSE_NODE_LEAF_ARG(pn); + if (!is_as) { + *q_base = q_full; + } + EMIT_ARG(import, q_full, MP_EMIT_IMPORT_NAME); + } else { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dotted_name)); // should be + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + { + // a name of the form a.b.c + if (!is_as) { + *q_base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + } + size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + if (n == 0) { + // There must be at least one node in this PN_dotted_name. + // Let the compiler know this so it doesn't warn, and can generate better code. + MP_UNREACHABLE; + } + size_t len = n - 1; + for (size_t i = 0; i < n; i++) { + len += qstr_len(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])); + } + char *q_ptr = mp_local_alloc(len); + char *str_dest = q_ptr; + for (size_t i = 0; i < n; i++) { + if (i > 0) { + *str_dest++ = '.'; + } + size_t str_src_len; + const byte *str_src = qstr_data(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]), &str_src_len); + memcpy(str_dest, str_src, str_src_len); + str_dest += str_src_len; + } + qstr q_full = qstr_from_strn(q_ptr, len); + mp_local_free(q_ptr); + EMIT_ARG(import, q_full, MP_EMIT_IMPORT_NAME); + if (is_as) { + for (size_t i = 1; i < n; i++) { + EMIT_ARG(attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]), MP_EMIT_ATTR_LOAD); + } + } + } + } +} + +static void compile_dotted_as_name(compiler_t *comp, mp_parse_node_t pn) { + EMIT_ARG(load_const_small_int, 0); // level 0 import + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // not importing from anything + qstr q_base; + do_import_name(comp, pn, &q_base); + compile_store_id(comp, q_base); +} + +static void compile_import_name(compiler_t *comp, mp_parse_node_struct_t *pns) { + apply_to_single_or_list(comp, pns->nodes[0], PN_dotted_as_names, compile_dotted_as_name); +} + +static void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) { + mp_parse_node_t pn_import_source = pns->nodes[0]; + + // extract the preceding .'s (if any) for a relative import, to compute the import level + uint import_level = 0; + do { + mp_parse_node_t pn_rel; + if (MP_PARSE_NODE_IS_TOKEN(pn_import_source) || MP_PARSE_NODE_IS_STRUCT_KIND(pn_import_source, PN_one_or_more_period_or_ellipsis)) { + // This covers relative imports with dots only like "from .. import" + pn_rel = pn_import_source; + pn_import_source = MP_PARSE_NODE_NULL; + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_import_source, PN_import_from_2b)) { + // This covers relative imports starting with dot(s) like "from .foo import" + mp_parse_node_struct_t *pns_2b = (mp_parse_node_struct_t *)pn_import_source; + pn_rel = pns_2b->nodes[0]; + pn_import_source = pns_2b->nodes[1]; + assert(!MP_PARSE_NODE_IS_NULL(pn_import_source)); // should not be + } else { + // Not a relative import + break; + } + + // get the list of . and/or ...'s + mp_parse_node_t *nodes; + size_t n = mp_parse_node_extract_list(&pn_rel, PN_one_or_more_period_or_ellipsis, &nodes); + + // count the total number of .'s + for (size_t i = 0; i < n; i++) { + if (MP_PARSE_NODE_IS_TOKEN_KIND(nodes[i], MP_TOKEN_DEL_PERIOD)) { + import_level++; + } else { + // should be an MP_TOKEN_ELLIPSIS + import_level += 3; + } + } + } while (0); + + if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) { + #if MICROPY_CPYTHON_COMPAT + if (comp->scope_cur->kind != SCOPE_MODULE) { + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("import * not at module level")); + return; + } + #endif + + EMIT_ARG(load_const_small_int, import_level); + + // build the "fromlist" tuple + EMIT_ARG(load_const_str, MP_QSTR__star_); + EMIT_ARG(build, 1, MP_EMIT_BUILD_TUPLE); + + // do the import + qstr dummy_q; + do_import_name(comp, pn_import_source, &dummy_q); + EMIT_ARG(import, MP_QSTRnull, MP_EMIT_IMPORT_STAR); + + } else { + EMIT_ARG(load_const_small_int, import_level); + + // build the "fromlist" tuple + mp_parse_node_t *pn_nodes; + size_t n = mp_parse_node_extract_list(&pns->nodes[1], PN_import_as_names, &pn_nodes); + for (size_t i = 0; i < n; i++) { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name)); + mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t *)pn_nodes[i]; + qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id + EMIT_ARG(load_const_str, id2); + } + EMIT_ARG(build, n, MP_EMIT_BUILD_TUPLE); + + // do the import + qstr dummy_q; + do_import_name(comp, pn_import_source, &dummy_q); + for (size_t i = 0; i < n; i++) { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name)); + mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t *)pn_nodes[i]; + qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id + EMIT_ARG(import, id2, MP_EMIT_IMPORT_FROM); + if (MP_PARSE_NODE_IS_NULL(pns3->nodes[1])) { + compile_store_id(comp, id2); + } else { + compile_store_id(comp, MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1])); + } + } + EMIT(pop_top); + } +} + +static void compile_declare_global(compiler_t *comp, mp_parse_node_t pn, id_info_t *id_info) { + if (id_info->kind != ID_INFO_KIND_UNDECIDED && id_info->kind != ID_INFO_KIND_GLOBAL_EXPLICIT) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("identifier redefined as global")); + return; + } + id_info->kind = ID_INFO_KIND_GLOBAL_EXPLICIT; + + // if the id exists in the global scope, set its kind to EXPLICIT_GLOBAL + id_info = scope_find_global(comp->scope_cur, id_info->qst); + if (id_info != NULL) { + id_info->kind = ID_INFO_KIND_GLOBAL_EXPLICIT; + } +} + +static void compile_declare_nonlocal(compiler_t *comp, mp_parse_node_t pn, id_info_t *id_info) { + if (id_info->kind == ID_INFO_KIND_UNDECIDED) { + id_info->kind = ID_INFO_KIND_GLOBAL_IMPLICIT; + scope_check_to_close_over(comp->scope_cur, id_info); + if (id_info->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("no binding for nonlocal found")); + } + } else if (id_info->kind != ID_INFO_KIND_FREE) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("identifier redefined as nonlocal")); + } +} + +static void compile_declare_global_or_nonlocal(compiler_t *comp, mp_parse_node_t pn, id_info_t *id_info, bool is_global) { + if (is_global) { + compile_declare_global(comp, pn, id_info); + } else { + compile_declare_nonlocal(comp, pn, id_info); + } +} + +static void compile_global_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (comp->pass == MP_PASS_SCOPE) { + bool is_global = MP_PARSE_NODE_STRUCT_KIND(pns) == PN_global_stmt; + + if (!is_global && comp->scope_cur->kind == SCOPE_MODULE) { + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("can't declare nonlocal in outer code")); + return; + } + + mp_parse_node_t *nodes; + size_t n = mp_parse_node_extract_list(&pns->nodes[0], PN_name_list, &nodes); + for (size_t i = 0; i < n; i++) { + qstr qst = MP_PARSE_NODE_LEAF_ARG(nodes[i]); + id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qst, ID_INFO_KIND_UNDECIDED); + compile_declare_global_or_nonlocal(comp, (mp_parse_node_t)pns, id_info, is_global); + } + } +} + +static void compile_assert_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + // with optimisations enabled we don't compile assertions + if (MP_STATE_VM(mp_optimise_value) != 0) { + return; + } + + uint l_end = comp_next_label(comp); + c_if_cond(comp, pns->nodes[0], true, l_end); + EMIT_LOAD_GLOBAL(MP_QSTR_AssertionError); // we load_global instead of load_id, to be consistent with CPython + if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) { + // assertion message + compile_node(comp, pns->nodes[1]); + EMIT_ARG(call_function, 1, 0, 0); + } + EMIT_ARG(raise_varargs, 1); + EMIT_ARG(label_assign, l_end); +} + +static void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + uint l_end = comp_next_label(comp); + + // optimisation: don't emit anything when "if False" + if (!mp_parse_node_is_const_false(pns->nodes[0])) { + uint l_fail = comp_next_label(comp); + c_if_cond(comp, pns->nodes[0], false, l_fail); // if condition + + compile_node(comp, pns->nodes[1]); // if block + + // optimisation: skip everything else when "if True" + if (mp_parse_node_is_const_true(pns->nodes[0])) { + goto done; + } + + // optimisation: don't jump over non-existent elif/else blocks + if (!(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3]))) { + // jump over elif/else blocks + EMIT_ARG(jump, l_end); + } + + EMIT_ARG(label_assign, l_fail); + } + + // compile elif blocks (if any) + mp_parse_node_t *pn_elif; + size_t n_elif = mp_parse_node_extract_list(&pns->nodes[2], PN_if_stmt_elif_list, &pn_elif); + for (size_t i = 0; i < n_elif; i++) { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_elif[i], PN_if_stmt_elif)); // should be + mp_parse_node_struct_t *pns_elif = (mp_parse_node_struct_t *)pn_elif[i]; + + // optimisation: don't emit anything when "if False" + if (!mp_parse_node_is_const_false(pns_elif->nodes[0])) { + uint l_fail = comp_next_label(comp); + c_if_cond(comp, pns_elif->nodes[0], false, l_fail); // elif condition + + compile_node(comp, pns_elif->nodes[1]); // elif block + + // optimisation: skip everything else when "elif True" + if (mp_parse_node_is_const_true(pns_elif->nodes[0])) { + goto done; + } + + EMIT_ARG(jump, l_end); + EMIT_ARG(label_assign, l_fail); + } + } + + // compile else block + compile_node(comp, pns->nodes[3]); // can be null + +done: + EMIT_ARG(label_assign, l_end); +} + +#define START_BREAK_CONTINUE_BLOCK \ + uint16_t old_break_label = comp->break_label; \ + uint16_t old_continue_label = comp->continue_label; \ + uint16_t old_break_continue_except_level = comp->break_continue_except_level; \ + uint break_label = comp_next_label(comp); \ + uint continue_label = comp_next_label(comp); \ + comp->break_label = break_label; \ + comp->continue_label = continue_label; \ + comp->break_continue_except_level = comp->cur_except_level; + +#define END_BREAK_CONTINUE_BLOCK \ + comp->break_label = old_break_label; \ + comp->continue_label = old_continue_label; \ + comp->break_continue_except_level = old_break_continue_except_level; + +static void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + START_BREAK_CONTINUE_BLOCK + + if (!mp_parse_node_is_const_false(pns->nodes[0])) { // optimisation: don't emit anything for "while False" + uint top_label = comp_next_label(comp); + if (!mp_parse_node_is_const_true(pns->nodes[0])) { // optimisation: don't jump to cond for "while True" + EMIT_ARG(jump, continue_label); + } + EMIT_ARG(label_assign, top_label); + compile_node(comp, pns->nodes[1]); // body + EMIT_ARG(label_assign, continue_label); + c_if_cond(comp, pns->nodes[0], true, top_label); // condition + } + + // break/continue apply to outer loop (if any) in the else block + END_BREAK_CONTINUE_BLOCK + + compile_node(comp, pns->nodes[2]); // else + + EMIT_ARG(label_assign, break_label); +} + +// This function compiles an optimised for-loop of the form: +// for in range(, , ): +// +// else: +// +// must be an identifier and must be a small-int. +// +// Semantics of for-loop require: +// - final failing value should not be stored in the loop variable +// - if the loop never runs, the loop variable should never be assigned +// - assignments to , or in the body do not alter the loop +// ( is a constant for us, so no need to worry about it changing) +// +// If is a small-int, then the stack during the for-loop contains just +// the current value of . Otherwise, the stack contains then the +// current value of . +static void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var, mp_parse_node_t pn_start, mp_parse_node_t pn_end, mp_parse_node_t pn_step, mp_parse_node_t pn_body, mp_parse_node_t pn_else) { + START_BREAK_CONTINUE_BLOCK + + uint top_label = comp_next_label(comp); + uint entry_label = comp_next_label(comp); + + // put the end value on the stack if it's not a small-int constant + bool end_on_stack = !MP_PARSE_NODE_IS_SMALL_INT(pn_end); + if (end_on_stack) { + compile_node(comp, pn_end); + } + + // compile: start + compile_node(comp, pn_start); + + EMIT_ARG(jump, entry_label); + EMIT_ARG(label_assign, top_label); + + // duplicate next value and store it to var + EMIT(dup_top); + c_assign(comp, pn_var, ASSIGN_STORE); + + // compile body + compile_node(comp, pn_body); + + EMIT_ARG(label_assign, continue_label); + + // compile: var + step + compile_node(comp, pn_step); + EMIT_ARG(binary_op, MP_BINARY_OP_INPLACE_ADD); + + EMIT_ARG(label_assign, entry_label); + + // compile: if var end: goto top + if (end_on_stack) { + EMIT(dup_top_two); + EMIT(rot_two); + } else { + EMIT(dup_top); + compile_node(comp, pn_end); + } + assert(MP_PARSE_NODE_IS_SMALL_INT(pn_step)); + if (MP_PARSE_NODE_LEAF_SMALL_INT(pn_step) >= 0) { + EMIT_ARG(binary_op, MP_BINARY_OP_LESS); + } else { + EMIT_ARG(binary_op, MP_BINARY_OP_MORE); + } + EMIT_ARG(pop_jump_if, true, top_label); + + // break/continue apply to outer loop (if any) in the else block + END_BREAK_CONTINUE_BLOCK + + // Compile the else block. We must pop the iterator variables before + // executing the else code because it may contain break/continue statements. + uint end_label = 0; + if (!MP_PARSE_NODE_IS_NULL(pn_else)) { + // discard final value of "var", and possible "end" value + EMIT(pop_top); + if (end_on_stack) { + EMIT(pop_top); + } + compile_node(comp, pn_else); + end_label = comp_next_label(comp); + EMIT_ARG(jump, end_label); + EMIT_ARG(adjust_stack_size, 1 + end_on_stack); + } + + EMIT_ARG(label_assign, break_label); + + // discard final value of var that failed the loop condition + EMIT(pop_top); + + // discard value if it's on the stack + if (end_on_stack) { + EMIT(pop_top); + } + + if (!MP_PARSE_NODE_IS_NULL(pn_else)) { + EMIT_ARG(label_assign, end_label); + } +} + +static void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + // this bit optimises: for in range(...), turning it into an explicitly incremented variable + // this is actually slower, but uses no heap memory + // for viper it will be much, much faster + if (/*comp->scope_cur->emit_options == MP_EMIT_OPT_VIPER &&*/ MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_atom_expr_normal)) { + mp_parse_node_struct_t *pns_it = (mp_parse_node_struct_t *)pns->nodes[1]; + if (MP_PARSE_NODE_IS_ID(pns_it->nodes[0]) + && MP_PARSE_NODE_LEAF_ARG(pns_it->nodes[0]) == MP_QSTR_range + && MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)pns_it->nodes[1]) == PN_trailer_paren) { + mp_parse_node_t pn_range_args = ((mp_parse_node_struct_t *)pns_it->nodes[1])->nodes[0]; + mp_parse_node_t *args; + size_t n_args = mp_parse_node_extract_list(&pn_range_args, PN_arglist, &args); + mp_parse_node_t pn_range_start; + mp_parse_node_t pn_range_end; + mp_parse_node_t pn_range_step; + bool optimize = false; + if (1 <= n_args && n_args <= 3) { + optimize = true; + if (n_args == 1) { + pn_range_start = mp_parse_node_new_small_int(0); + pn_range_end = args[0]; + pn_range_step = mp_parse_node_new_small_int(1); + } else if (n_args == 2) { + pn_range_start = args[0]; + pn_range_end = args[1]; + pn_range_step = mp_parse_node_new_small_int(1); + } else { + pn_range_start = args[0]; + pn_range_end = args[1]; + pn_range_step = args[2]; + // the step must be a non-zero constant integer to do the optimisation + if (!MP_PARSE_NODE_IS_SMALL_INT(pn_range_step) + || MP_PARSE_NODE_LEAF_SMALL_INT(pn_range_step) == 0) { + optimize = false; + } + } + // arguments must be able to be compiled as standard expressions + if (optimize && MP_PARSE_NODE_IS_STRUCT(pn_range_start)) { + int k = MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)pn_range_start); + if (k == PN_arglist_star || k == PN_arglist_dbl_star || k == PN_argument) { + optimize = false; + } + } + if (optimize && MP_PARSE_NODE_IS_STRUCT(pn_range_end)) { + int k = MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)pn_range_end); + if (k == PN_arglist_star || k == PN_arglist_dbl_star || k == PN_argument) { + optimize = false; + } + } + } + if (optimize) { + compile_for_stmt_optimised_range(comp, pns->nodes[0], pn_range_start, pn_range_end, pn_range_step, pns->nodes[2], pns->nodes[3]); + return; + } + } + } + + START_BREAK_CONTINUE_BLOCK + comp->break_label |= MP_EMIT_BREAK_FROM_FOR; + + uint pop_label = comp_next_label(comp); + + compile_node(comp, pns->nodes[1]); // iterator + EMIT_ARG(get_iter, true); + EMIT_ARG(label_assign, continue_label); + EMIT_ARG(for_iter, pop_label); + c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable + compile_node(comp, pns->nodes[2]); // body + EMIT_ARG(jump, continue_label); + EMIT_ARG(label_assign, pop_label); + EMIT(for_iter_end); + + // break/continue apply to outer loop (if any) in the else block + END_BREAK_CONTINUE_BLOCK + + compile_node(comp, pns->nodes[3]); // else (may be empty) + + EMIT_ARG(label_assign, break_label); +} + +static void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_excepts, mp_parse_node_t pn_else) { + // setup code + uint l1 = comp_next_label(comp); + uint success_label = comp_next_label(comp); + + compile_increase_except_level(comp, l1, MP_EMIT_SETUP_BLOCK_EXCEPT); + + compile_node(comp, pn_body); // body + EMIT_ARG(pop_except_jump, success_label, false); // jump over exception handler + + EMIT_ARG(label_assign, l1); // start of exception handler + EMIT(start_except_handler); + + // at this point the top of the stack contains the exception instance that was raised + + uint l2 = comp_next_label(comp); + + for (int i = 0; i < n_except; i++) { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_excepts[i], PN_try_stmt_except)); // should be + mp_parse_node_struct_t *pns_except = (mp_parse_node_struct_t *)pn_excepts[i]; + + qstr qstr_exception_local = 0; + uint end_finally_label = comp_next_label(comp); + #if MICROPY_PY_SYS_SETTRACE + EMIT_ARG(set_source_line, pns_except->source_line); + #endif + + if (MP_PARSE_NODE_IS_NULL(pns_except->nodes[0])) { + // this is a catch all exception handler + if (i + 1 != n_except) { + compile_syntax_error(comp, pn_excepts[i], MP_ERROR_TEXT("default 'except' must be last")); + compile_decrease_except_level(comp); + return; + } + } else { + // this exception handler requires a match to a certain type of exception + mp_parse_node_t pns_exception_expr = pns_except->nodes[0]; + if (MP_PARSE_NODE_IS_STRUCT(pns_exception_expr)) { + mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t *)pns_exception_expr; + if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_try_stmt_as_name) { + // handler binds the exception to a local + pns_exception_expr = pns3->nodes[0]; + qstr_exception_local = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1]); + } + } + EMIT(dup_top); + compile_node(comp, pns_exception_expr); + EMIT_ARG(binary_op, MP_BINARY_OP_EXCEPTION_MATCH); + EMIT_ARG(pop_jump_if, false, end_finally_label); + } + + // either discard or store the exception instance + if (qstr_exception_local == 0) { + EMIT(pop_top); + } else { + compile_store_id(comp, qstr_exception_local); + } + + // If the exception is bound to a variable then the of the + // exception handler is wrapped in a try-finally so that the name can + // be deleted (per Python semantics) even if the has an exception. + // In such a case the generated code for the exception handler is: + // try: + // + // finally: + // = None + // del + uint l3 = 0; + if (qstr_exception_local != 0) { + l3 = comp_next_label(comp); + compile_increase_except_level(comp, l3, MP_EMIT_SETUP_BLOCK_FINALLY); + } + compile_node(comp, pns_except->nodes[1]); // the + if (qstr_exception_local != 0) { + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT_ARG(label_assign, l3); + EMIT_ARG(adjust_stack_size, 1); // stack adjust for possible return value + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + compile_store_id(comp, qstr_exception_local); + compile_delete_id(comp, qstr_exception_local); + EMIT_ARG(adjust_stack_size, -1); + compile_decrease_except_level(comp); + } + + EMIT_ARG(pop_except_jump, l2, true); + EMIT_ARG(label_assign, end_finally_label); + EMIT_ARG(adjust_stack_size, 1); // stack adjust for the exception instance + } + + compile_decrease_except_level(comp); + EMIT(end_except_handler); + + EMIT_ARG(label_assign, success_label); + compile_node(comp, pn_else); // else block, can be null + EMIT_ARG(label_assign, l2); +} + +static void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_except, mp_parse_node_t pn_else, mp_parse_node_t pn_finally) { + uint l_finally_block = comp_next_label(comp); + + compile_increase_except_level(comp, l_finally_block, MP_EMIT_SETUP_BLOCK_FINALLY); + + if (n_except == 0) { + assert(MP_PARSE_NODE_IS_NULL(pn_else)); + EMIT_ARG(adjust_stack_size, 3); // stack adjust for possible UNWIND_JUMP state + compile_node(comp, pn_body); + EMIT_ARG(adjust_stack_size, -3); + } else { + compile_try_except(comp, pn_body, n_except, pn_except, pn_else); + } + + // If the code reaches this point then the try part of the try-finally exited normally. + // This is indicated to the runtime by None sitting on the stack. + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + + // Compile the finally block. + // The stack needs to be adjusted by 1 to account for the possibility that the finally is + // being executed as part of a return, and the return value is on the top of the stack. + EMIT_ARG(label_assign, l_finally_block); + EMIT_ARG(adjust_stack_size, 1); + compile_node(comp, pn_finally); + EMIT_ARG(adjust_stack_size, -1); + + compile_decrease_except_level(comp); +} + +static void compile_try_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should be + { + mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t *)pns->nodes[1]; + if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_finally) { + // just try-finally + compile_try_finally(comp, pns->nodes[0], 0, NULL, MP_PARSE_NODE_NULL, pns2->nodes[0]); + } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_except_and_more) { + // try-except and possibly else and/or finally + mp_parse_node_t *pn_excepts; + size_t n_except = mp_parse_node_extract_list(&pns2->nodes[0], PN_try_stmt_except_list, &pn_excepts); + if (MP_PARSE_NODE_IS_NULL(pns2->nodes[2])) { + // no finally + compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1]); + } else { + // have finally + compile_try_finally(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1], ((mp_parse_node_struct_t *)pns2->nodes[2])->nodes[0]); + } + } else { + // just try-except + mp_parse_node_t *pn_excepts; + size_t n_except = mp_parse_node_extract_list(&pns->nodes[1], PN_try_stmt_except_list, &pn_excepts); + compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, MP_PARSE_NODE_NULL); + } + } +} + +static void compile_with_stmt_helper(compiler_t *comp, size_t n, mp_parse_node_t *nodes, mp_parse_node_t body) { + if (n == 0) { + // no more pre-bits, compile the body of the with + compile_node(comp, body); + } else { + uint l_end = comp_next_label(comp); + if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) { + // this pre-bit is of the form "a as b" + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)nodes[0]; + compile_node(comp, pns->nodes[0]); + compile_increase_except_level(comp, l_end, MP_EMIT_SETUP_BLOCK_WITH); + c_assign(comp, pns->nodes[1], ASSIGN_STORE); + } else { + // this pre-bit is just an expression + compile_node(comp, nodes[0]); + compile_increase_except_level(comp, l_end, MP_EMIT_SETUP_BLOCK_WITH); + EMIT(pop_top); + } + // compile additional pre-bits and the body + compile_with_stmt_helper(comp, n - 1, nodes + 1, body); + // finish this with block + EMIT_ARG(with_cleanup, l_end); + reserve_labels_for_native(comp, 3); // used by native's with_cleanup + compile_decrease_except_level(comp); + } +} + +static void compile_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + // get the nodes for the pre-bit of the with (the a as b, c as d, ... bit) + mp_parse_node_t *nodes; + size_t n = mp_parse_node_extract_list(&pns->nodes[0], PN_with_stmt_list, &nodes); + assert(n > 0); + + // compile in a nested fashion + compile_with_stmt_helper(comp, n, nodes, pns->nodes[1]); +} + +static void compile_yield_from(compiler_t *comp) { + EMIT_ARG(get_iter, false); + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT_ARG(yield, MP_EMIT_YIELD_FROM); + reserve_labels_for_native(comp, 3); +} + +#if MICROPY_PY_ASYNC_AWAIT +static void compile_await_object_method(compiler_t *comp, qstr method) { + EMIT_ARG(load_method, method, false); + EMIT_ARG(call_method, 0, 0, 0); + compile_yield_from(comp); +} + +static void compile_async_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + // Allocate labels. + uint while_else_label = comp_next_label(comp); + uint try_exception_label = comp_next_label(comp); + uint try_else_label = comp_next_label(comp); + uint try_finally_label = comp_next_label(comp); + + // Stack: (...) + + // Compile the iterator expression and load and call its __aiter__ method. + compile_node(comp, pns->nodes[1]); // iterator + // Stack: (..., iterator) + EMIT_ARG(load_method, MP_QSTR___aiter__, false); + // Stack: (..., iterator, __aiter__) + EMIT_ARG(call_method, 0, 0, 0); + // Stack: (..., iterable) + + START_BREAK_CONTINUE_BLOCK + + EMIT_ARG(label_assign, continue_label); + + compile_increase_except_level(comp, try_exception_label, MP_EMIT_SETUP_BLOCK_EXCEPT); + + EMIT(dup_top); + // Stack: (..., iterable, iterable) + + // Compile: yield from iterable.__anext__() + compile_await_object_method(comp, MP_QSTR___anext__); + // Stack: (..., iterable, yielded_value) + + c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable + // Stack: (..., iterable) + EMIT_ARG(pop_except_jump, try_else_label, false); + + EMIT_ARG(label_assign, try_exception_label); + EMIT(start_except_handler); + EMIT(dup_top); + EMIT_LOAD_GLOBAL(MP_QSTR_StopAsyncIteration); + EMIT_ARG(binary_op, MP_BINARY_OP_EXCEPTION_MATCH); + EMIT_ARG(pop_jump_if, false, try_finally_label); + EMIT(pop_top); // pop exception instance + EMIT_ARG(pop_except_jump, while_else_label, true); + + EMIT_ARG(label_assign, try_finally_label); + EMIT_ARG(adjust_stack_size, 1); // if we jump here, the exc is on the stack + compile_decrease_except_level(comp); + EMIT(end_except_handler); + + // Stack: (..., iterable) + + EMIT_ARG(label_assign, try_else_label); + compile_node(comp, pns->nodes[2]); // body + + EMIT_ARG(jump, continue_label); + // break/continue apply to outer loop (if any) in the else block + END_BREAK_CONTINUE_BLOCK + + EMIT_ARG(label_assign, while_else_label); + compile_node(comp, pns->nodes[3]); // else + + EMIT_ARG(label_assign, break_label); + // Stack: (..., iterable) + + EMIT(pop_top); + // Stack: (...) +} + +static void compile_async_with_stmt_helper(compiler_t *comp, size_t n, mp_parse_node_t *nodes, mp_parse_node_t body) { + if (n == 0) { + // no more pre-bits, compile the body of the with + compile_node(comp, body); + } else { + uint l_finally_block = comp_next_label(comp); + uint l_aexit_no_exc = comp_next_label(comp); + uint l_ret_unwind_jump = comp_next_label(comp); + uint l_end = comp_next_label(comp); + + if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) { + // this pre-bit is of the form "a as b" + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)nodes[0]; + compile_node(comp, pns->nodes[0]); + EMIT(dup_top); + compile_await_object_method(comp, MP_QSTR___aenter__); + c_assign(comp, pns->nodes[1], ASSIGN_STORE); + } else { + // this pre-bit is just an expression + compile_node(comp, nodes[0]); + EMIT(dup_top); + compile_await_object_method(comp, MP_QSTR___aenter__); + EMIT(pop_top); + } + + // To keep the Python stack size down, and because we can't access values on + // this stack further down than 3 elements (via rot_three), we don't preload + // __aexit__ (as per normal with) but rather wait until we need it below. + + // Start the try-finally statement + compile_increase_except_level(comp, l_finally_block, MP_EMIT_SETUP_BLOCK_FINALLY); + + // Compile any additional pre-bits of the "async with", and also the body + EMIT_ARG(adjust_stack_size, 3); // stack adjust for possible UNWIND_JUMP state + compile_async_with_stmt_helper(comp, n - 1, nodes + 1, body); + EMIT_ARG(adjust_stack_size, -3); + + // We have now finished the "try" block and fall through to the "finally" + + // At this point, after the with body has executed, we have 3 cases: + // 1. no exception, we just fall through to this point; stack: (..., ctx_mgr) + // 2. exception propagating out, we get to the finally block; stack: (..., ctx_mgr, exc) + // 3. return or unwind jump, we get to the finally block; stack: (..., ctx_mgr, X, INT) + + // Handle case 1: call __aexit__ + // Stack: (..., ctx_mgr) + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // to tell end_finally there's no exception + EMIT(rot_two); + EMIT_ARG(jump, l_aexit_no_exc); // jump to code below to call __aexit__ + + // Start of "finally" block + // At this point we have case 2 or 3, we detect which one by the TOS being an exception or not + EMIT_ARG(label_assign, l_finally_block); + + // Detect if TOS an exception or not + EMIT(dup_top); + EMIT_LOAD_GLOBAL(MP_QSTR_BaseException); + EMIT_ARG(binary_op, MP_BINARY_OP_EXCEPTION_MATCH); + EMIT_ARG(pop_jump_if, false, l_ret_unwind_jump); // if not an exception then we have case 3 + + // Handle case 2: call __aexit__ and either swallow or re-raise the exception + // Stack: (..., ctx_mgr, exc) + EMIT(dup_top); + EMIT(rot_three); + EMIT(rot_two); + EMIT_ARG(load_method, MP_QSTR___aexit__, false); + EMIT(rot_three); + EMIT(rot_three); + EMIT(dup_top); + #if MICROPY_CPYTHON_COMPAT + EMIT_ARG(attr, MP_QSTR___class__, MP_EMIT_ATTR_LOAD); // get type(exc) + #else + compile_load_id(comp, MP_QSTR_type); + EMIT(rot_two); + EMIT_ARG(call_function, 1, 0, 0); // get type(exc) + #endif + EMIT(rot_two); + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // dummy traceback value + // Stack: (..., exc, __aexit__, ctx_mgr, type(exc), exc, None) + EMIT_ARG(call_method, 3, 0, 0); + compile_yield_from(comp); + EMIT_ARG(pop_jump_if, false, l_end); + EMIT(pop_top); // pop exception + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); // replace with None to swallow exception + EMIT_ARG(jump, l_end); + EMIT_ARG(adjust_stack_size, 2); + + // Handle case 3: call __aexit__ + // Stack: (..., ctx_mgr, X, INT) + EMIT_ARG(label_assign, l_ret_unwind_jump); + EMIT(rot_three); + EMIT(rot_three); + EMIT_ARG(label_assign, l_aexit_no_exc); + EMIT_ARG(load_method, MP_QSTR___aexit__, false); + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT(dup_top); + EMIT(dup_top); + EMIT_ARG(call_method, 3, 0, 0); + compile_yield_from(comp); + EMIT(pop_top); + EMIT_ARG(adjust_stack_size, -1); + + // End of "finally" block + // Stack can have one of three configurations: + // a. (..., None) - from either case 1, or case 2 with swallowed exception + // b. (..., exc) - from case 2 with re-raised exception + // c. (..., X, INT) - from case 3 + EMIT_ARG(label_assign, l_end); + compile_decrease_except_level(comp); + } +} + +static void compile_async_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + // get the nodes for the pre-bit of the with (the a as b, c as d, ... bit) + mp_parse_node_t *nodes; + size_t n = mp_parse_node_extract_list(&pns->nodes[0], PN_with_stmt_list, &nodes); + assert(n > 0); + + // compile in a nested fashion + compile_async_with_stmt_helper(comp, n, nodes, pns->nodes[1]); +} + +static void compile_async_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[0])); + mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns->nodes[0]; + if (MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_funcdef) { + // async def + compile_funcdef(comp, pns0); + scope_t *fscope = (scope_t *)pns0->nodes[4]; + fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR; + } else { + // async for/with; first verify the scope is a generator + int scope_flags = comp->scope_cur->scope_flags; + if (!(scope_flags & MP_SCOPE_FLAG_GENERATOR)) { + compile_syntax_error(comp, (mp_parse_node_t)pns0, + MP_ERROR_TEXT("async for/with outside async function")); + return; + } + + if (MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_for_stmt) { + // async for + compile_async_for_stmt(comp, pns0); + } else { + // async with + assert(MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_with_stmt); + compile_async_with_stmt(comp, pns0); + } + } +} +#endif + +static void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + mp_parse_node_t pn_rhs = pns->nodes[1]; + if (MP_PARSE_NODE_IS_NULL(pn_rhs)) { + if (comp->is_repl && comp->scope_cur->kind == SCOPE_MODULE) { + // for REPL, evaluate then print the expression + compile_load_id(comp, MP_QSTR___repl_print__); + compile_node(comp, pns->nodes[0]); + EMIT_ARG(call_function, 1, 0, 0); + EMIT(pop_top); + + } else { + // for non-REPL, evaluate then discard the expression + if ((MP_PARSE_NODE_IS_LEAF(pns->nodes[0]) && !MP_PARSE_NODE_IS_ID(pns->nodes[0])) + || MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_const_object)) { + // do nothing with a lonely constant + } else { + compile_node(comp, pns->nodes[0]); // just an expression + EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack + } + } + } else if (MP_PARSE_NODE_IS_STRUCT(pn_rhs)) { + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn_rhs; + int kind = MP_PARSE_NODE_STRUCT_KIND(pns1); + if (kind == PN_annassign) { + // the annotation is in pns1->nodes[0] and is ignored + if (MP_PARSE_NODE_IS_NULL(pns1->nodes[1])) { + // an annotation of the form "x: y" + // inside a function this declares "x" as a local + if (comp->scope_cur->kind == SCOPE_FUNCTION) { + if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) { + qstr lhs = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + scope_find_or_add_id(comp->scope_cur, lhs, ID_INFO_KIND_LOCAL); + } + } + } else { + // an assigned annotation of the form "x: y = z" + pn_rhs = pns1->nodes[1]; + goto plain_assign; + } + } else if (kind == PN_expr_stmt_augassign) { + c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign + compile_node(comp, pns1->nodes[1]); // rhs + assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0])); + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]); + mp_binary_op_t op = MP_BINARY_OP_INPLACE_OR + (tok - MP_TOKEN_DEL_PIPE_EQUAL); + EMIT_ARG(binary_op, op); + c_assign(comp, pns->nodes[0], ASSIGN_AUG_STORE); // lhs store for aug assign + } else if (kind == PN_expr_stmt_assign_list) { + int rhs = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) - 1; + compile_node(comp, pns1->nodes[rhs]); // rhs + // following CPython, we store left-most first + if (rhs > 0) { + EMIT(dup_top); + } + c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store + for (int i = 0; i < rhs; i++) { + if (i + 1 < rhs) { + EMIT(dup_top); + } + c_assign(comp, pns1->nodes[i], ASSIGN_STORE); // middle store + } + } else { + plain_assign: + #if MICROPY_COMP_DOUBLE_TUPLE_ASSIGN + if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_rhs, PN_testlist_star_expr) + && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)) { + mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns->nodes[0]; + pns1 = (mp_parse_node_struct_t *)pn_rhs; + uint32_t n_pns0 = MP_PARSE_NODE_STRUCT_NUM_NODES(pns0); + // Can only optimise a tuple-to-tuple assignment when all of the following hold: + // - equal number of items in LHS and RHS tuples + // - 2 or 3 items in the tuples + // - there are no star expressions in the LHS tuple + if (n_pns0 == MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) + && (n_pns0 == 2 + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + || n_pns0 == 3 + #endif + ) + && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr) + && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr) + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + && (n_pns0 == 2 || !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[2], PN_star_expr)) + #endif + ) { + // Optimisation for a, b = c, d or a, b, c = d, e, f + compile_node(comp, pns1->nodes[0]); // rhs + compile_node(comp, pns1->nodes[1]); // rhs + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + if (n_pns0 == 3) { + compile_node(comp, pns1->nodes[2]); // rhs + EMIT(rot_three); + } + #endif + EMIT(rot_two); + c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store + c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + if (n_pns0 == 3) { + c_assign(comp, pns0->nodes[2], ASSIGN_STORE); // lhs store + } + #endif + return; + } + } + #endif + + compile_node(comp, pn_rhs); // rhs + c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store + } + } else { + goto plain_assign; + } +} + +static void compile_test_if_expr(compiler_t *comp, mp_parse_node_struct_t *pns) { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_test_if_else)); + mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t *)pns->nodes[1]; + + uint l_fail = comp_next_label(comp); + uint l_end = comp_next_label(comp); + c_if_cond(comp, pns_test_if_else->nodes[0], false, l_fail); // condition + compile_node(comp, pns->nodes[0]); // success value + EMIT_ARG(jump, l_end); + EMIT_ARG(label_assign, l_fail); + EMIT_ARG(adjust_stack_size, -1); // adjust stack size + compile_node(comp, pns_test_if_else->nodes[1]); // failure value + EMIT_ARG(label_assign, l_end); +} + +static void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (comp->pass == MP_PASS_SCOPE) { + // create a new scope for this lambda + scope_t *s = scope_new_and_link(comp, SCOPE_LAMBDA, (mp_parse_node_t)pns, comp->scope_cur->emit_options); + // store the lambda scope so the compiling function (this one) can use it at each pass + pns->nodes[2] = (mp_parse_node_t)s; + } + + // get the scope for this lambda + scope_t *this_scope = (scope_t *)pns->nodes[2]; + + // compile the lambda definition + compile_funcdef_lambdef(comp, this_scope, pns->nodes[0], PN_varargslist); +} + +#if MICROPY_PY_ASSIGN_EXPR +static void compile_namedexpr_helper(compiler_t *comp, mp_parse_node_t pn_name, mp_parse_node_t pn_expr) { + if (!MP_PARSE_NODE_IS_ID(pn_name)) { + compile_syntax_error(comp, (mp_parse_node_t)pn_name, MP_ERROR_TEXT("can't assign to expression")); + } + compile_node(comp, pn_expr); + EMIT(dup_top); + + qstr target = MP_PARSE_NODE_LEAF_ARG(pn_name); + + // When a variable is assigned via := in a comprehension then that variable is bound to + // the parent scope. Any global or nonlocal declarations in the parent scope are honoured. + // For details see: https://peps.python.org/pep-0572/#scope-of-the-target + if (comp->pass == MP_PASS_SCOPE && SCOPE_IS_COMP_LIKE(comp->scope_cur->kind)) { + id_info_t *id_info_parent = mp_emit_common_get_id_for_modification(comp->scope_cur->parent, target); + if (id_info_parent->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) { + scope_find_or_add_id(comp->scope_cur, target, ID_INFO_KIND_GLOBAL_EXPLICIT); + } else { + id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, target, ID_INFO_KIND_UNDECIDED); + bool is_global = comp->scope_cur->parent->parent == NULL; // comprehension is defined in outer scope + if (!is_global && id_info->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { + // Variable was already referenced but now needs to be closed over, so reset the kind + // such that scope_check_to_close_over() is called in compile_declare_nonlocal(). + id_info->kind = ID_INFO_KIND_UNDECIDED; + } + compile_declare_global_or_nonlocal(comp, pn_name, id_info, is_global); + } + } + + // Do the store to the target variable. + compile_store_id(comp, target); +} + +static void compile_namedexpr(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_namedexpr_helper(comp, pns->nodes[0], pns->nodes[1]); +} +#endif + +static void compile_or_and_test(compiler_t *comp, mp_parse_node_struct_t *pns) { + bool cond = MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test; + uint l_end = comp_next_label(comp); + int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + for (int i = 0; i < n; i += 1) { + compile_node(comp, pns->nodes[i]); + if (i + 1 < n) { + EMIT_ARG(jump_if_or_pop, cond, l_end); + } + } + EMIT_ARG(label_assign, l_end); +} + +static void compile_not_test_2(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_node(comp, pns->nodes[0]); + EMIT_ARG(unary_op, MP_UNARY_OP_NOT); +} + +static void compile_comparison(compiler_t *comp, mp_parse_node_struct_t *pns) { + int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + compile_node(comp, pns->nodes[0]); + bool multi = (num_nodes > 3); + uint l_fail = 0; + if (multi) { + l_fail = comp_next_label(comp); + } + for (int i = 1; i + 1 < num_nodes; i += 2) { + compile_node(comp, pns->nodes[i + 1]); + if (i + 2 < num_nodes) { + EMIT(dup_top); + EMIT(rot_three); + } + if (MP_PARSE_NODE_IS_TOKEN(pns->nodes[i])) { + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]); + mp_binary_op_t op; + if (tok == MP_TOKEN_KW_IN) { + op = MP_BINARY_OP_IN; + } else { + op = MP_BINARY_OP_LESS + (tok - MP_TOKEN_OP_LESS); + } + EMIT_ARG(binary_op, op); + } else { + assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[i])); // should be + mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t *)pns->nodes[i]; + int kind = MP_PARSE_NODE_STRUCT_KIND(pns2); + if (kind == PN_comp_op_not_in) { + EMIT_ARG(binary_op, MP_BINARY_OP_NOT_IN); + } else { + assert(kind == PN_comp_op_is); // should be + if (MP_PARSE_NODE_IS_NULL(pns2->nodes[0])) { + EMIT_ARG(binary_op, MP_BINARY_OP_IS); + } else { + EMIT_ARG(binary_op, MP_BINARY_OP_IS_NOT); + } + } + } + if (i + 2 < num_nodes) { + EMIT_ARG(jump_if_or_pop, false, l_fail); + } + } + if (multi) { + uint l_end = comp_next_label(comp); + EMIT_ARG(jump, l_end); + EMIT_ARG(label_assign, l_fail); + EMIT_ARG(adjust_stack_size, 1); + EMIT(rot_two); + EMIT(pop_top); + EMIT_ARG(label_assign, l_end); + } +} + +static void compile_star_expr(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("*x must be assignment target")); +} + +static void compile_binary_op(compiler_t *comp, mp_parse_node_struct_t *pns) { + MP_STATIC_ASSERT(MP_BINARY_OP_OR + PN_xor_expr - PN_expr == MP_BINARY_OP_XOR); + MP_STATIC_ASSERT(MP_BINARY_OP_OR + PN_and_expr - PN_expr == MP_BINARY_OP_AND); + mp_binary_op_t binary_op = MP_BINARY_OP_OR + MP_PARSE_NODE_STRUCT_KIND(pns) - PN_expr; + int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + compile_node(comp, pns->nodes[0]); + for (int i = 1; i < num_nodes; ++i) { + compile_node(comp, pns->nodes[i]); + EMIT_ARG(binary_op, binary_op); + } +} + +static void compile_term(compiler_t *comp, mp_parse_node_struct_t *pns) { + int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + compile_node(comp, pns->nodes[0]); + for (int i = 1; i + 1 < num_nodes; i += 2) { + compile_node(comp, pns->nodes[i + 1]); + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]); + mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); + EMIT_ARG(binary_op, op); + } +} + +static void compile_factor_2(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_node(comp, pns->nodes[1]); + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + mp_unary_op_t op; + if (tok == MP_TOKEN_OP_TILDE) { + op = MP_UNARY_OP_INVERT; + } else { + assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); + op = MP_UNARY_OP_POSITIVE + (tok - MP_TOKEN_OP_PLUS); + } + EMIT_ARG(unary_op, op); +} + +static void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *pns) { + // compile the subject of the expression + compile_node(comp, pns->nodes[0]); + + // compile_atom_expr_await may call us with a NULL node + if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) { + return; + } + + // get the array of trailers (known to be an array of PARSE_NODE_STRUCT) + size_t num_trail = 1; + mp_parse_node_struct_t **pns_trail = (mp_parse_node_struct_t **)&pns->nodes[1]; + if (MP_PARSE_NODE_STRUCT_KIND(pns_trail[0]) == PN_atom_expr_trailers) { + num_trail = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_trail[0]); + pns_trail = (mp_parse_node_struct_t **)&pns_trail[0]->nodes[0]; + } + + // the current index into the array of trailers + size_t i = 0; + + // handle special super() call + if (comp->scope_cur->kind == SCOPE_FUNCTION + && MP_PARSE_NODE_IS_ID(pns->nodes[0]) + && MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]) == MP_QSTR_super + && MP_PARSE_NODE_STRUCT_KIND(pns_trail[0]) == PN_trailer_paren + && MP_PARSE_NODE_IS_NULL(pns_trail[0]->nodes[0])) { + // at this point we have matched "super()" within a function + + // load the class for super to search for a parent + compile_load_id(comp, MP_QSTR___class__); + + // look for first argument to function (assumes it's "self") + bool found = false; + id_info_t *id = &comp->scope_cur->id_info[0]; + for (size_t n = comp->scope_cur->id_info_len; n > 0; --n, ++id) { + if (id->flags & ID_FLAG_IS_PARAM) { + // first argument found; load it + compile_load_id(comp, id->qst); + found = true; + break; + } + } + if (!found) { + compile_syntax_error(comp, (mp_parse_node_t)pns_trail[0], + MP_ERROR_TEXT("super() can't find self")); // really a TypeError + return; + } + + if (num_trail >= 3 + && MP_PARSE_NODE_STRUCT_KIND(pns_trail[1]) == PN_trailer_period + && MP_PARSE_NODE_STRUCT_KIND(pns_trail[2]) == PN_trailer_paren) { + // optimisation for method calls super().f(...), to eliminate heap allocation + mp_parse_node_struct_t *pns_period = pns_trail[1]; + mp_parse_node_struct_t *pns_paren = pns_trail[2]; + EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), true); + compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0); + i = 3; + } else { + // a super() call + EMIT_ARG(call_function, 2, 0, 0); + i = 1; + } + + #if MICROPY_COMP_CONST_LITERAL && MICROPY_PY_COLLECTIONS_ORDEREDDICT + // handle special OrderedDict constructor + } else if (MP_PARSE_NODE_IS_ID(pns->nodes[0]) + && MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]) == MP_QSTR_OrderedDict + && MP_PARSE_NODE_STRUCT_KIND(pns_trail[0]) == PN_trailer_paren + && MP_PARSE_NODE_IS_STRUCT_KIND(pns_trail[0]->nodes[0], PN_atom_brace)) { + // at this point we have matched "OrderedDict({...})" + + EMIT_ARG(call_function, 0, 0, 0); + mp_parse_node_struct_t *pns_dict = (mp_parse_node_struct_t *)pns_trail[0]->nodes[0]; + compile_atom_brace_helper(comp, pns_dict, false); + i = 1; + #endif + } + + // compile the remaining trailers + for (; i < num_trail; i++) { + if (i + 1 < num_trail + && MP_PARSE_NODE_STRUCT_KIND(pns_trail[i]) == PN_trailer_period + && MP_PARSE_NODE_STRUCT_KIND(pns_trail[i + 1]) == PN_trailer_paren) { + // optimisation for method calls a.f(...), following PyPy + mp_parse_node_struct_t *pns_period = pns_trail[i]; + mp_parse_node_struct_t *pns_paren = pns_trail[i + 1]; + EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), false); + compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0); + i += 1; + } else { + // node is one of: trailer_paren, trailer_bracket, trailer_period + compile_node(comp, (mp_parse_node_t)pns_trail[i]); + } + } +} + +static void compile_power(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_generic_all_nodes(comp, pns); // 2 nodes, arguments of power + EMIT_ARG(binary_op, MP_BINARY_OP_POWER); +} + +static void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra) { + // function to call is on top of stack + + // get the list of arguments + mp_parse_node_t *args; + size_t n_args = mp_parse_node_extract_list(&pn_arglist, PN_arglist, &args); + + // compile the arguments + // Rather than calling compile_node on the list, we go through the list of args + // explicitly here so that we can count the number of arguments and give sensible + // error messages. + int n_positional = n_positional_extra; + uint n_keyword = 0; + uint star_flags = 0; + mp_uint_t star_args = 0; + for (size_t i = 0; i < n_args; i++) { + if (MP_PARSE_NODE_IS_STRUCT(args[i])) { + mp_parse_node_struct_t *pns_arg = (mp_parse_node_struct_t *)args[i]; + if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_star) { + if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { + compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("* arg after **")); + return; + } + #if MICROPY_DYNAMIC_COMPILER + if (i >= (size_t)mp_dynamic_compiler.small_int_bits - 1) + #else + if (i >= MP_SMALL_INT_BITS - 1) + #endif + { + // If there are not enough bits in a small int to fit the flag, then we consider + // it a syntax error. It should be unlikely to have this many args in practice. + compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("too many args")); + return; + } + star_flags |= MP_EMIT_STAR_FLAG_SINGLE; + star_args |= (mp_uint_t)1 << i; + compile_node(comp, pns_arg->nodes[0]); + n_positional++; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { + star_flags |= MP_EMIT_STAR_FLAG_DOUBLE; + // double-star args are stored as kw arg with key of None + EMIT(load_null); + compile_node(comp, pns_arg->nodes[0]); + n_keyword++; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_argument) { + #if MICROPY_PY_ASSIGN_EXPR + if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_3)) { + compile_namedexpr_helper(comp, pns_arg->nodes[0], ((mp_parse_node_struct_t *)pns_arg->nodes[1])->nodes[0]); + n_positional++; + } else + #endif + if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_comp_for)) { + if (!MP_PARSE_NODE_IS_ID(pns_arg->nodes[0])) { + compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("LHS of keyword arg must be an id")); + return; + } + EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns_arg->nodes[0])); + compile_node(comp, pns_arg->nodes[1]); + n_keyword++; + } else { + compile_comprehension(comp, pns_arg, SCOPE_GEN_EXPR); + n_positional++; + } + } else { + goto normal_argument; + } + } else { + normal_argument: + if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { + compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after **")); + return; + } + if (n_keyword > 0) { + compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after keyword arg")); + return; + } + compile_node(comp, args[i]); + n_positional++; + } + } + + if (star_flags != 0) { + // one extra object that contains the star_args map + EMIT_ARG(load_const_small_int, star_args); + } + + // emit the function/method call + if (is_method_call) { + EMIT_ARG(call_method, n_positional, n_keyword, star_flags); + } else { + EMIT_ARG(call_function, n_positional, n_keyword, star_flags); + } +} + +// pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node +static void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind) { + assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2); + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for)); + mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t *)pns->nodes[1]; + + if (comp->pass == MP_PASS_SCOPE) { + // create a new scope for this comprehension + scope_t *s = scope_new_and_link(comp, kind, (mp_parse_node_t)pns, comp->scope_cur->emit_options); + // store the comprehension scope so the compiling function (this one) can use it at each pass + pns_comp_for->nodes[3] = (mp_parse_node_t)s; + } + + // get the scope for this comprehension + scope_t *this_scope = (scope_t *)pns_comp_for->nodes[3]; + + // compile the comprehension + close_over_variables_etc(comp, this_scope, 0, 0); + + compile_node(comp, pns_comp_for->nodes[1]); // source of the iterator + if (kind == SCOPE_GEN_EXPR) { + EMIT_ARG(get_iter, false); + } + EMIT_ARG(call_function, 1, 0, 0); +} + +static void compile_atom_paren(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // an empty tuple + EMIT_ARG(build, 0, MP_EMIT_BUILD_TUPLE); + } else { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)); + pns = (mp_parse_node_struct_t *)pns->nodes[0]; + if (MP_PARSE_NODE_TESTLIST_COMP_HAS_COMP_FOR(pns)) { + // generator expression + compile_comprehension(comp, pns, SCOPE_GEN_EXPR); + } else { + // tuple with N items + compile_generic_tuple(comp, pns); + } + } +} + +static void compile_atom_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // empty list + EMIT_ARG(build, 0, MP_EMIT_BUILD_LIST); + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) { + mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t *)pns->nodes[0]; + if (MP_PARSE_NODE_TESTLIST_COMP_HAS_COMP_FOR(pns2)) { + // list comprehension + compile_comprehension(comp, pns2, SCOPE_LIST_COMP); + } else { + // list with N items + compile_generic_all_nodes(comp, pns2); + EMIT_ARG(build, MP_PARSE_NODE_STRUCT_NUM_NODES(pns2), MP_EMIT_BUILD_LIST); + } + } else { + // list with 1 item + compile_node(comp, pns->nodes[0]); + EMIT_ARG(build, 1, MP_EMIT_BUILD_LIST); + } +} + +static void compile_atom_brace_helper(compiler_t *comp, mp_parse_node_struct_t *pns, bool create_map) { + mp_parse_node_t pn = pns->nodes[0]; + if (MP_PARSE_NODE_IS_NULL(pn)) { + // empty dict + if (create_map) { + EMIT_ARG(build, 0, MP_EMIT_BUILD_MAP); + } + } else if (MP_PARSE_NODE_IS_STRUCT(pn)) { + pns = (mp_parse_node_struct_t *)pn; + if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker_item) { + // dict with one element + if (create_map) { + EMIT_ARG(build, 1, MP_EMIT_BUILD_MAP); + } + compile_node(comp, pn); + EMIT(store_map); + } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) { + assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1]; + if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) { + // dict/set with multiple elements + + // get tail elements (2nd, 3rd, ...) + mp_parse_node_t *nodes; + size_t n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes); + + // first element sets whether it's a dict or set + bool is_dict; + if (!MICROPY_PY_BUILTINS_SET || MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) { + // a dictionary + if (create_map) { + EMIT_ARG(build, 1 + n, MP_EMIT_BUILD_MAP); + } + compile_node(comp, pns->nodes[0]); + EMIT(store_map); + is_dict = true; + } else { + // a set + compile_node(comp, pns->nodes[0]); // 1st value of set + is_dict = false; + } + + // process rest of elements + for (size_t i = 0; i < n; i++) { + mp_parse_node_t pn_i = nodes[i]; + bool is_key_value = MP_PARSE_NODE_IS_STRUCT_KIND(pn_i, PN_dictorsetmaker_item); + compile_node(comp, pn_i); + if (is_dict) { + if (!is_key_value) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("invalid syntax")); + #else + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("expecting key:value for dict")); + #endif + return; + } + EMIT(store_map); + } else { + if (is_key_value) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("invalid syntax")); + #else + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("expecting just a value for set")); + #endif + return; + } + } + } + + #if MICROPY_PY_BUILTINS_SET + // if it's a set, build it + if (!is_dict) { + EMIT_ARG(build, 1 + n, MP_EMIT_BUILD_SET); + } + #endif + } else { + assert(MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_comp_for); // should be + // dict/set comprehension + if (!MICROPY_PY_BUILTINS_SET || MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) { + // a dictionary comprehension + compile_comprehension(comp, pns, SCOPE_DICT_COMP); + } else { + // a set comprehension + compile_comprehension(comp, pns, SCOPE_SET_COMP); + } + } + } else { + // set with one element + goto set_with_one_element; + } + } else { + // set with one element + set_with_one_element: + #if MICROPY_PY_BUILTINS_SET + compile_node(comp, pn); + EMIT_ARG(build, 1, MP_EMIT_BUILD_SET); + #else + assert(0); + #endif + } +} + +static void compile_atom_brace(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_atom_brace_helper(comp, pns, true); +} + +static void compile_trailer_paren(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_trailer_paren_helper(comp, pns->nodes[0], false, 0); +} + +static void compile_trailer_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) { + // object who's index we want is on top of stack + compile_node(comp, pns->nodes[0]); // the index + EMIT_ARG(subscr, MP_EMIT_SUBSCR_LOAD); +} + +static void compile_trailer_period(compiler_t *comp, mp_parse_node_struct_t *pns) { + // object who's attribute we want is on top of stack + EMIT_ARG(attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]), MP_EMIT_ATTR_LOAD); // attribute to get +} + +#if MICROPY_PY_BUILTINS_SLICE +static void compile_subscript(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_2) { + compile_node(comp, pns->nodes[0]); // start of slice + assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be + pns = (mp_parse_node_struct_t *)pns->nodes[1]; + } else { + // pns is a PN_subscript_3, load None for start of slice + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + } + + assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3); // should always be + mp_parse_node_t pn = pns->nodes[0]; + if (MP_PARSE_NODE_IS_NULL(pn)) { + // [?:] + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT_ARG(build, 2, MP_EMIT_BUILD_SLICE); + } else if (MP_PARSE_NODE_IS_STRUCT(pn)) { + pns = (mp_parse_node_struct_t *)pn; + if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3c) { + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + pn = pns->nodes[0]; + if (MP_PARSE_NODE_IS_NULL(pn)) { + // [?::] + EMIT_ARG(build, 2, MP_EMIT_BUILD_SLICE); + } else { + // [?::x] + compile_node(comp, pn); + EMIT_ARG(build, 3, MP_EMIT_BUILD_SLICE); + } + } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3d) { + compile_node(comp, pns->nodes[0]); + assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be + pns = (mp_parse_node_struct_t *)pns->nodes[1]; + assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_sliceop); // should always be + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // [?:x:] + EMIT_ARG(build, 2, MP_EMIT_BUILD_SLICE); + } else { + // [?:x:x] + compile_node(comp, pns->nodes[0]); + EMIT_ARG(build, 3, MP_EMIT_BUILD_SLICE); + } + } else { + // [?:x] + compile_node(comp, pn); + EMIT_ARG(build, 2, MP_EMIT_BUILD_SLICE); + } + } else { + // [?:x] + compile_node(comp, pn); + EMIT_ARG(build, 2, MP_EMIT_BUILD_SLICE); + } +} +#endif // MICROPY_PY_BUILTINS_SLICE + +static void compile_dictorsetmaker_item(compiler_t *comp, mp_parse_node_struct_t *pns) { + // if this is called then we are compiling a dict key:value pair + compile_node(comp, pns->nodes[1]); // value + compile_node(comp, pns->nodes[0]); // key +} + +static void compile_classdef(compiler_t *comp, mp_parse_node_struct_t *pns) { + qstr cname = compile_classdef_helper(comp, pns, comp->scope_cur->emit_options); + // store class object into class name + compile_store_id(comp, cname); +} + +static void compile_yield_expr(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (comp->scope_cur->kind != SCOPE_FUNCTION && comp->scope_cur->kind != SCOPE_LAMBDA) { + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("'yield' outside function")); + return; + } + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT_ARG(yield, MP_EMIT_YIELD_VALUE); + reserve_labels_for_native(comp, 1); + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_yield_arg_from)) { + pns = (mp_parse_node_struct_t *)pns->nodes[0]; + compile_node(comp, pns->nodes[0]); + compile_yield_from(comp); + } else { + compile_node(comp, pns->nodes[0]); + EMIT_ARG(yield, MP_EMIT_YIELD_VALUE); + reserve_labels_for_native(comp, 1); + } +} + +#if MICROPY_PY_ASYNC_AWAIT +static void compile_atom_expr_await(compiler_t *comp, mp_parse_node_struct_t *pns) { + if (comp->scope_cur->kind != SCOPE_FUNCTION && comp->scope_cur->kind != SCOPE_LAMBDA) { + #if MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT + if (!mp_compile_allow_top_level_await) + #endif + { + compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("'await' outside function")); + return; + } + } + compile_atom_expr_normal(comp, pns); + compile_yield_from(comp); +} +#endif + +static mp_obj_t get_const_object(mp_parse_node_struct_t *pns) { + return mp_parse_node_extract_const_object(pns); +} + +static void compile_const_object(compiler_t *comp, mp_parse_node_struct_t *pns) { + EMIT_ARG(load_const_obj, get_const_object(pns)); +} + +typedef void (*compile_function_t)(compiler_t *, mp_parse_node_struct_t *); +static const compile_function_t compile_function[] = { +// only define rules with a compile function +#define c(f) compile_##f +#define DEF_RULE(rule, comp, kind, ...) comp, +#define DEF_RULE_NC(rule, kind, ...) + #include "py/grammar.h" +#undef c +#undef DEF_RULE +#undef DEF_RULE_NC + compile_const_object, +}; + +static void compile_node(compiler_t *comp, mp_parse_node_t pn) { + if (MP_PARSE_NODE_IS_NULL(pn)) { + // pass + } else if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { + mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + EMIT_ARG(load_const_small_int, arg); + } else if (MP_PARSE_NODE_IS_LEAF(pn)) { + uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); + switch (MP_PARSE_NODE_LEAF_KIND(pn)) { + case MP_PARSE_NODE_ID: + compile_load_id(comp, arg); + break; + case MP_PARSE_NODE_STRING: + EMIT_ARG(load_const_str, arg); + break; + case MP_PARSE_NODE_TOKEN: + default: + if (arg == MP_TOKEN_NEWLINE) { + // this can occur when file_input lets through a NEWLINE (eg if file starts with a newline) + // or when single_input lets through a NEWLINE (user enters a blank line) + // do nothing + } else { + EMIT_ARG(load_const_tok, arg); + } + break; + } + } else { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + EMIT_ARG(set_source_line, pns->source_line); + assert(MP_PARSE_NODE_STRUCT_KIND(pns) <= PN_const_object); + compile_function_t f = compile_function[MP_PARSE_NODE_STRUCT_KIND(pns)]; + f(comp, pns); + } +} + +#if MICROPY_EMIT_NATIVE +static int compile_viper_type_annotation(compiler_t *comp, mp_parse_node_t pn_annotation) { + int native_type = MP_NATIVE_TYPE_OBJ; + if (MP_PARSE_NODE_IS_NULL(pn_annotation)) { + // No annotation, type defaults to object + } else if (MP_PARSE_NODE_IS_ID(pn_annotation)) { + qstr type_name = MP_PARSE_NODE_LEAF_ARG(pn_annotation); + native_type = mp_native_type_from_qstr(type_name); + if (native_type < 0) { + comp->compile_error = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, MP_ERROR_TEXT("unknown type '%q'"), type_name); + native_type = 0; + } + } else { + compile_syntax_error(comp, pn_annotation, MP_ERROR_TEXT("annotation must be an identifier")); + } + return native_type; +} +#endif + +static void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star) { + (void)pn_dbl_star; + + // check that **kw is last + if ((comp->scope_cur->scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("invalid syntax")); + return; + } + + qstr param_name = MP_QSTRnull; + uint param_flag = ID_FLAG_IS_PARAM; + mp_parse_node_struct_t *pns = NULL; + if (MP_PARSE_NODE_IS_ID(pn)) { + param_name = MP_PARSE_NODE_LEAF_ARG(pn); + if (comp->have_star) { + // comes after a star, so counts as a keyword-only parameter + comp->scope_cur->num_kwonly_args += 1; + } else { + // comes before a star, so counts as a positional parameter + comp->scope_cur->num_pos_args += 1; + } + } else { + assert(MP_PARSE_NODE_IS_STRUCT(pn)); + pns = (mp_parse_node_struct_t *)pn; + if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_name) { + // named parameter with possible annotation + param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + if (comp->have_star) { + // comes after a star, so counts as a keyword-only parameter + comp->scope_cur->num_kwonly_args += 1; + } else { + // comes before a star, so counts as a positional parameter + comp->scope_cur->num_pos_args += 1; + } + } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) { + if (comp->have_star) { + // more than one star + compile_syntax_error(comp, pn, MP_ERROR_TEXT("invalid syntax")); + return; + } + comp->have_star = true; + param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_STAR_PARAM; + if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { + // bare star + // TODO see http://www.python.org/dev/peps/pep-3102/ + // assert(comp->scope_cur->num_dict_params == 0); + pns = NULL; + } else if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) { + // named star + comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARARGS; + param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + pns = NULL; + } else { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)); // should be + // named star with possible annotation + comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARARGS; + pns = (mp_parse_node_struct_t *)pns->nodes[0]; + param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + } + } else { + // double star with possible annotation + assert(MP_PARSE_NODE_STRUCT_KIND(pns) == pn_dbl_star); // should be + param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_DBL_STAR_PARAM; + comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARKEYWORDS; + } + } + + if (param_name != MP_QSTRnull) { + id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, param_name, ID_INFO_KIND_UNDECIDED); + if (id_info->kind != ID_INFO_KIND_UNDECIDED) { + compile_syntax_error(comp, pn, MP_ERROR_TEXT("argument name reused")); + return; + } + id_info->kind = ID_INFO_KIND_LOCAL; + id_info->flags = param_flag; + + #if MICROPY_EMIT_NATIVE + if (comp->scope_cur->emit_options == MP_EMIT_OPT_VIPER && pn_name == PN_typedargslist_name && pns != NULL) { + id_info->flags |= compile_viper_type_annotation(comp, pns->nodes[1]) << ID_FLAG_VIPER_TYPE_POS; + } + #else + (void)pns; + #endif + } +} + +static void compile_scope_func_param(compiler_t *comp, mp_parse_node_t pn) { + compile_scope_func_lambda_param(comp, pn, PN_typedargslist_name, PN_typedargslist_star, PN_typedargslist_dbl_star); +} + +static void compile_scope_lambda_param(compiler_t *comp, mp_parse_node_t pn) { + compile_scope_func_lambda_param(comp, pn, PN_varargslist_name, PN_varargslist_star, PN_varargslist_dbl_star); +} + +static void compile_scope_comp_iter(compiler_t *comp, mp_parse_node_struct_t *pns_comp_for, mp_parse_node_t pn_inner_expr, int for_depth) { + uint l_top = comp_next_label(comp); + uint l_end = comp_next_label(comp); + EMIT_ARG(label_assign, l_top); + EMIT_ARG(for_iter, l_end); + c_assign(comp, pns_comp_for->nodes[0], ASSIGN_STORE); + mp_parse_node_t pn_iter = pns_comp_for->nodes[2]; + +tail_recursion: + if (MP_PARSE_NODE_IS_NULL(pn_iter)) { + // no more nested if/for; compile inner expression + compile_node(comp, pn_inner_expr); + if (comp->scope_cur->kind == SCOPE_GEN_EXPR) { + EMIT_ARG(yield, MP_EMIT_YIELD_VALUE); + reserve_labels_for_native(comp, 1); + EMIT(pop_top); + } else { + EMIT_ARG(store_comp, comp->scope_cur->kind, 4 * for_depth + 5); + } + } else if (MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)pn_iter) == PN_comp_if) { + // if condition + mp_parse_node_struct_t *pns_comp_if = (mp_parse_node_struct_t *)pn_iter; + c_if_cond(comp, pns_comp_if->nodes[0], false, l_top); + pn_iter = pns_comp_if->nodes[1]; + goto tail_recursion; + } else { + assert(MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)pn_iter) == PN_comp_for); // should be + // for loop + mp_parse_node_struct_t *pns_comp_for2 = (mp_parse_node_struct_t *)pn_iter; + compile_node(comp, pns_comp_for2->nodes[1]); + EMIT_ARG(get_iter, true); + compile_scope_comp_iter(comp, pns_comp_for2, pn_inner_expr, for_depth + 1); + } + + EMIT_ARG(jump, l_top); + EMIT_ARG(label_assign, l_end); + EMIT(for_iter_end); +} + +static void check_for_doc_string(compiler_t *comp, mp_parse_node_t pn) { + #if MICROPY_ENABLE_DOC_STRING + // see http://www.python.org/dev/peps/pep-0257/ + + // look for the first statement + if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) { + // a statement; fall through + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_file_input_2)) { + // file input; find the first non-newline node + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + for (int i = 0; i < num_nodes; i++) { + pn = pns->nodes[i]; + if (!(MP_PARSE_NODE_IS_LEAF(pn) && MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN && MP_PARSE_NODE_LEAF_ARG(pn) == MP_TOKEN_NEWLINE)) { + // not a newline, so this is the first statement; finish search + break; + } + } + // if we didn't find a non-newline then it's okay to fall through; pn will be a newline and so doc-string test below will fail gracefully + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_suite_block_stmts)) { + // a list of statements; get the first one + pn = ((mp_parse_node_struct_t *)pn)->nodes[0]; + } else { + return; + } + + // check the first statement for a doc string + if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + if ((MP_PARSE_NODE_IS_LEAF(pns->nodes[0]) + && MP_PARSE_NODE_LEAF_KIND(pns->nodes[0]) == MP_PARSE_NODE_STRING) + || (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_const_object) + && mp_obj_is_str(get_const_object((mp_parse_node_struct_t *)pns->nodes[0])))) { + // compile the doc string + compile_node(comp, pns->nodes[0]); + // store the doc string + compile_store_id(comp, MP_QSTR___doc__); + } + } + #else + (void)comp; + (void)pn; + #endif +} + +static bool compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { + comp->pass = pass; + comp->scope_cur = scope; + comp->next_label = 0; + mp_emit_common_start_pass(&comp->emit_common, pass); + EMIT_ARG(start_pass, pass, scope); + reserve_labels_for_native(comp, 6); // used by native's start_pass + + if (comp->pass == MP_PASS_SCOPE) { + // reset maximum stack sizes in scope + // they will be computed in this first pass + scope->stack_size = 0; + scope->exc_stack_size = 0; + } + + // compile + if (MP_PARSE_NODE_IS_STRUCT_KIND(scope->pn, PN_eval_input)) { + assert(scope->kind == SCOPE_MODULE); + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)scope->pn; + compile_node(comp, pns->nodes[0]); // compile the expression + EMIT(return_value); + } else if (scope->kind == SCOPE_MODULE) { + if (!comp->is_repl) { + check_for_doc_string(comp, scope->pn); + } + compile_node(comp, scope->pn); + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT(return_value); + } else if (scope->kind == SCOPE_FUNCTION) { + assert(MP_PARSE_NODE_IS_STRUCT(scope->pn)); + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)scope->pn; + assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef); + + // work out number of parameters, keywords and default parameters, and add them to the id_info array + // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc) + if (comp->pass == MP_PASS_SCOPE) { + comp->have_star = false; + apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_scope_func_param); + + #if MICROPY_EMIT_NATIVE + if (scope->emit_options == MP_EMIT_OPT_VIPER) { + // Compile return type; pns->nodes[2] is return/whole function annotation + scope->scope_flags |= compile_viper_type_annotation(comp, pns->nodes[2]) << MP_SCOPE_FLAG_VIPERRET_POS; + } + #endif // MICROPY_EMIT_NATIVE + } + + compile_node(comp, pns->nodes[3]); // 3 is function body + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT(return_value); + } else if (scope->kind == SCOPE_LAMBDA) { + assert(MP_PARSE_NODE_IS_STRUCT(scope->pn)); + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)scope->pn; + assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 3); + + // Set the source line number for the start of the lambda + EMIT_ARG(set_source_line, pns->source_line); + + // work out number of parameters, keywords and default parameters, and add them to the id_info array + // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc) + if (comp->pass == MP_PASS_SCOPE) { + comp->have_star = false; + apply_to_single_or_list(comp, pns->nodes[0], PN_varargslist, compile_scope_lambda_param); + } + + compile_node(comp, pns->nodes[1]); // 1 is lambda body + + // if the lambda is a generator, then we return None, not the result of the expression of the lambda + if (scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + EMIT(pop_top); + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + } + EMIT(return_value); + } else if (SCOPE_IS_COMP_LIKE(scope->kind)) { + // a bit of a hack at the moment + + assert(MP_PARSE_NODE_IS_STRUCT(scope->pn)); + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)scope->pn; + assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2); + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for)); + mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t *)pns->nodes[1]; + + // We need a unique name for the comprehension argument (the iterator). + // CPython uses .0, but we should be able to use anything that won't + // clash with a user defined variable. Best to use an existing qstr, + // so we use the blank qstr. + qstr qstr_arg = MP_QSTR_; + if (comp->pass == MP_PASS_SCOPE) { + scope_find_or_add_id(comp->scope_cur, qstr_arg, ID_INFO_KIND_LOCAL); + scope->num_pos_args = 1; + } + + // Set the source line number for the start of the comprehension + EMIT_ARG(set_source_line, pns->source_line); + + if (scope->kind == SCOPE_LIST_COMP) { + EMIT_ARG(build, 0, MP_EMIT_BUILD_LIST); + } else if (scope->kind == SCOPE_DICT_COMP) { + EMIT_ARG(build, 0, MP_EMIT_BUILD_MAP); + #if MICROPY_PY_BUILTINS_SET + } else if (scope->kind == SCOPE_SET_COMP) { + EMIT_ARG(build, 0, MP_EMIT_BUILD_SET); + #endif + } + + // There are 4 slots on the stack for the iterator, and the first one is + // NULL to indicate that the second one points to the iterator object. + if (scope->kind == SCOPE_GEN_EXPR) { + MP_STATIC_ASSERT(MP_OBJ_ITER_BUF_NSLOTS == 4); + EMIT(load_null); + compile_load_id(comp, qstr_arg); + EMIT(load_null); + EMIT(load_null); + } else { + compile_load_id(comp, qstr_arg); + EMIT_ARG(get_iter, true); + } + + compile_scope_comp_iter(comp, pns_comp_for, pns->nodes[0], 0); + + if (scope->kind == SCOPE_GEN_EXPR) { + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + } + EMIT(return_value); + } else { + assert(scope->kind == SCOPE_CLASS); + assert(MP_PARSE_NODE_IS_STRUCT(scope->pn)); + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)scope->pn; + assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_classdef); + + if (comp->pass == MP_PASS_SCOPE) { + scope_find_or_add_id(scope, MP_QSTR___class__, ID_INFO_KIND_LOCAL); + } + + #if MICROPY_PY_SYS_SETTRACE + EMIT_ARG(set_source_line, pns->source_line); + #endif + compile_load_id(comp, MP_QSTR___name__); + compile_store_id(comp, MP_QSTR___module__); + EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name + compile_store_id(comp, MP_QSTR___qualname__); + + check_for_doc_string(comp, pns->nodes[2]); + compile_node(comp, pns->nodes[2]); // 2 is class body + + id_info_t *id = scope_find(scope, MP_QSTR___class__); + assert(id != NULL); + if (id->kind == ID_INFO_KIND_LOCAL) { + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + } else { + EMIT_LOAD_FAST(MP_QSTR___class__, id->local_num); + } + EMIT(return_value); + } + + bool pass_complete = EMIT(end_pass); + + // make sure we match all the exception levels + assert(comp->cur_except_level == 0); + + return pass_complete; +} + +#if MICROPY_EMIT_INLINE_ASM +// requires 3 passes: SCOPE, CODE_SIZE, EMIT +static void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind_t pass) { + comp->pass = pass; + comp->scope_cur = scope; + comp->next_label = 0; + + if (scope->kind != SCOPE_FUNCTION) { + compile_syntax_error(comp, MP_PARSE_NODE_NULL, MP_ERROR_TEXT("inline assembler must be a function")); + return; + } + + if (comp->pass > MP_PASS_SCOPE) { + EMIT_INLINE_ASM_ARG(start_pass, comp->pass, &comp->compile_error); + } + + // get the function definition parse node + assert(MP_PARSE_NODE_IS_STRUCT(scope->pn)); + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)scope->pn; + assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef); + + // qstr f_id = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); // function name + + // parameters are in pns->nodes[1] + if (comp->pass == MP_PASS_CODE_SIZE) { + mp_parse_node_t *pn_params; + size_t n_params = mp_parse_node_extract_list(&pns->nodes[1], PN_typedargslist, &pn_params); + scope->num_pos_args = EMIT_INLINE_ASM_ARG(count_params, n_params, pn_params); + if (comp->compile_error != MP_OBJ_NULL) { + goto inline_asm_error; + } + } + + // pns->nodes[2] is function return annotation + mp_uint_t type_sig = MP_NATIVE_TYPE_INT; + mp_parse_node_t pn_annotation = pns->nodes[2]; + if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) { + // nodes[2] can be null or a test-expr + if (MP_PARSE_NODE_IS_ID(pn_annotation)) { + qstr ret_type = MP_PARSE_NODE_LEAF_ARG(pn_annotation); + switch (ret_type) { + case MP_QSTR_object: + type_sig = MP_NATIVE_TYPE_OBJ; + break; + case MP_QSTR_bool: + type_sig = MP_NATIVE_TYPE_BOOL; + break; + case MP_QSTR_int: + type_sig = MP_NATIVE_TYPE_INT; + break; + case MP_QSTR_uint: + type_sig = MP_NATIVE_TYPE_UINT; + break; + default: + compile_syntax_error(comp, pn_annotation, MP_ERROR_TEXT("unknown type")); + return; + } + } else { + compile_syntax_error(comp, pn_annotation, MP_ERROR_TEXT("return annotation must be an identifier")); + } + } + + mp_parse_node_t pn_body = pns->nodes[3]; // body + mp_parse_node_t *nodes; + size_t num = mp_parse_node_extract_list(&pn_body, PN_suite_block_stmts, &nodes); + + for (size_t i = 0; i < num; i++) { + assert(MP_PARSE_NODE_IS_STRUCT(nodes[i])); + mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t *)nodes[i]; + if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_pass_stmt) { + // no instructions + continue; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) != PN_expr_stmt) { + // not an instruction; error + not_an_instruction: + compile_syntax_error(comp, nodes[i], MP_ERROR_TEXT("expecting an assembler instruction")); + return; + } + + // check structure of parse node + assert(MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0])); + if (!MP_PARSE_NODE_IS_NULL(pns2->nodes[1])) { + goto not_an_instruction; + } + pns2 = (mp_parse_node_struct_t *)pns2->nodes[0]; + if (MP_PARSE_NODE_STRUCT_KIND(pns2) != PN_atom_expr_normal) { + goto not_an_instruction; + } + if (!MP_PARSE_NODE_IS_ID(pns2->nodes[0])) { + goto not_an_instruction; + } + if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns2->nodes[1], PN_trailer_paren)) { + goto not_an_instruction; + } + + // parse node looks like an instruction + // get instruction name and args + qstr op = MP_PARSE_NODE_LEAF_ARG(pns2->nodes[0]); + pns2 = (mp_parse_node_struct_t *)pns2->nodes[1]; // PN_trailer_paren + mp_parse_node_t *pn_arg; + size_t n_args = mp_parse_node_extract_list(&pns2->nodes[0], PN_arglist, &pn_arg); + + // emit instructions + if (op == MP_QSTR_label) { + if (!(n_args == 1 && MP_PARSE_NODE_IS_ID(pn_arg[0]))) { + compile_syntax_error(comp, nodes[i], MP_ERROR_TEXT("'label' requires 1 argument")); + return; + } + uint lab = comp_next_label(comp); + if (pass > MP_PASS_SCOPE) { + if (!EMIT_INLINE_ASM_ARG(label, lab, MP_PARSE_NODE_LEAF_ARG(pn_arg[0]))) { + compile_syntax_error(comp, nodes[i], MP_ERROR_TEXT("label redefined")); + return; + } + } + } else if (op == MP_QSTR_align) { + if (!(n_args == 1 && MP_PARSE_NODE_IS_SMALL_INT(pn_arg[0]))) { + compile_syntax_error(comp, nodes[i], MP_ERROR_TEXT("'align' requires 1 argument")); + return; + } + if (pass > MP_PASS_SCOPE) { + mp_asm_base_align((mp_asm_base_t *)comp->emit_inline_asm, + MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[0])); + } + } else if (op == MP_QSTR_data) { + if (!(n_args >= 2 && MP_PARSE_NODE_IS_SMALL_INT(pn_arg[0]))) { + compile_syntax_error(comp, nodes[i], MP_ERROR_TEXT("'data' requires at least 2 arguments")); + return; + } + if (pass > MP_PASS_SCOPE) { + mp_int_t bytesize = MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[0]); + for (uint j = 1; j < n_args; j++) { + mp_obj_t int_obj; + if (!mp_parse_node_get_int_maybe(pn_arg[j], &int_obj)) { + compile_syntax_error(comp, nodes[i], MP_ERROR_TEXT("'data' requires integer arguments")); + return; + } + mp_asm_base_data((mp_asm_base_t *)comp->emit_inline_asm, + bytesize, mp_obj_int_get_truncated(int_obj)); + } + } + } else { + if (pass > MP_PASS_SCOPE) { + EMIT_INLINE_ASM_ARG(op, op, n_args, pn_arg); + } + } + + if (comp->compile_error != MP_OBJ_NULL) { + pns = pns2; // this is the parse node that had the error + goto inline_asm_error; + } + } + + if (comp->pass > MP_PASS_SCOPE) { + EMIT_INLINE_ASM_ARG(end_pass, type_sig); + + if (comp->pass == MP_PASS_EMIT) { + void *f = mp_asm_base_get_code((mp_asm_base_t *)comp->emit_inline_asm); + mp_emit_glue_assign_native(comp->scope_cur->raw_code, MP_CODE_NATIVE_ASM, + f, mp_asm_base_get_code_size((mp_asm_base_t *)comp->emit_inline_asm), + NULL, + #if MICROPY_PERSISTENT_CODE_SAVE + 0, + 0, + #endif + 0, comp->scope_cur->num_pos_args, type_sig); + } + } + + if (comp->compile_error != MP_OBJ_NULL) { + // inline assembler had an error; set line for its exception + inline_asm_error: + comp->compile_error_line = pns->source_line; + } +} +#endif + +static void scope_compute_things(scope_t *scope) { + // in MicroPython we put the *x parameter after all other parameters (except **y) + if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) { + id_info_t *id_param = NULL; + for (int i = scope->id_info_len - 1; i >= 0; i--) { + id_info_t *id = &scope->id_info[i]; + if (id->flags & ID_FLAG_IS_STAR_PARAM) { + if (id_param != NULL) { + // swap star param with last param + id_info_t temp = *id_param; + *id_param = *id; + *id = temp; + } + break; + } else if (id_param == NULL && id->flags == ID_FLAG_IS_PARAM) { + id_param = id; + } + } + } + + // in functions, turn implicit globals into explicit globals + // compute the index of each local + scope->num_locals = 0; + for (int i = 0; i < scope->id_info_len; i++) { + id_info_t *id = &scope->id_info[i]; + if (scope->kind == SCOPE_CLASS && id->qst == MP_QSTR___class__) { + // __class__ is not counted as a local; if it's used then it becomes a ID_INFO_KIND_CELL + continue; + } + if (SCOPE_IS_FUNC_LIKE(scope->kind) && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { + id->kind = ID_INFO_KIND_GLOBAL_EXPLICIT; + } + #if MICROPY_EMIT_NATIVE + if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) { + // This function makes a reference to a global variable + if (scope->emit_options == MP_EMIT_OPT_VIPER + && mp_native_type_from_qstr(id->qst) >= MP_NATIVE_TYPE_INT) { + // A casting operator in viper mode, not a real global reference + } else { + scope->scope_flags |= MP_SCOPE_FLAG_REFGLOBALS; + } + } + #endif + // params always count for 1 local, even if they are a cell + if (id->kind == ID_INFO_KIND_LOCAL || (id->flags & ID_FLAG_IS_PARAM)) { + id->local_num = scope->num_locals++; + } + } + + // compute the index of cell vars + for (int i = 0; i < scope->id_info_len; i++) { + id_info_t *id = &scope->id_info[i]; + // in MicroPython the cells come right after the fast locals + // parameters are not counted here, since they remain at the start + // of the locals, even if they are cell vars + if (id->kind == ID_INFO_KIND_CELL && !(id->flags & ID_FLAG_IS_PARAM)) { + id->local_num = scope->num_locals; + scope->num_locals += 1; + } + } + + // compute the index of free vars + // make sure they are in the order of the parent scope + if (scope->parent != NULL) { + int num_free = 0; + for (int i = 0; i < scope->parent->id_info_len; i++) { + id_info_t *id = &scope->parent->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) { + for (int j = 0; j < scope->id_info_len; j++) { + id_info_t *id2 = &scope->id_info[j]; + if (id2->kind == ID_INFO_KIND_FREE && id->qst == id2->qst) { + assert(!(id2->flags & ID_FLAG_IS_PARAM)); // free vars should not be params + // in MicroPython the frees come first, before the params + id2->local_num = num_free; + num_free += 1; + } + } + } + } + // in MicroPython shift all other locals after the free locals + if (num_free > 0) { + for (int i = 0; i < scope->id_info_len; i++) { + id_info_t *id = &scope->id_info[i]; + if (id->kind != ID_INFO_KIND_FREE || (id->flags & ID_FLAG_IS_PARAM)) { + id->local_num += num_free; + } + } + scope->num_pos_args += num_free; // free vars are counted as params for passing them into the function + scope->num_locals += num_free; + } + } +} + +#if !MICROPY_PERSISTENT_CODE_SAVE +static +#endif +void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm) { + // put compiler state on the stack, it's relatively small + compiler_t comp_state = {0}; + compiler_t *comp = &comp_state; + + comp->is_repl = is_repl; + comp->break_label = INVALID_LABEL; + comp->continue_label = INVALID_LABEL; + mp_emit_common_init(&comp->emit_common, source_file); + + // create the module scope + #if MICROPY_EMIT_NATIVE + const uint emit_opt = MP_STATE_VM(default_emit_opt); + #else + const uint emit_opt = MP_EMIT_OPT_NONE; + #endif + scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt); + + // create standard emitter; it's used at least for MP_PASS_SCOPE + emit_t *emit_bc = emit_bc_new(&comp->emit_common); + + // compile MP_PASS_SCOPE + comp->emit = emit_bc; + #if MICROPY_EMIT_NATIVE + comp->emit_method_table = &emit_bc_method_table; + #endif + uint max_num_labels = 0; + for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { + #if MICROPY_EMIT_INLINE_ASM + if (s->emit_options == MP_EMIT_OPT_ASM) { + compile_scope_inline_asm(comp, s, MP_PASS_SCOPE); + } else + #endif + { + compile_scope(comp, s, MP_PASS_SCOPE); + + // Check if any implicitly declared variables should be closed over + for (size_t i = 0; i < s->id_info_len; ++i) { + id_info_t *id = &s->id_info[i]; + if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { + scope_check_to_close_over(s, id); + } + } + } + + // update maximum number of labels needed + if (comp->next_label > max_num_labels) { + max_num_labels = comp->next_label; + } + } + + // compute some things related to scope and identifiers + for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { + scope_compute_things(s); + } + + // set max number of labels now that it's calculated + emit_bc_set_max_num_labels(emit_bc, max_num_labels); + + // compile MP_PASS_STACK_SIZE, MP_PASS_CODE_SIZE, MP_PASS_EMIT + #if MICROPY_EMIT_NATIVE + emit_t *emit_native = NULL; + #endif + for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { + #if MICROPY_EMIT_INLINE_ASM + if (s->emit_options == MP_EMIT_OPT_ASM) { + // inline assembly + if (comp->emit_inline_asm == NULL) { + comp->emit_inline_asm = ASM_EMITTER(new)(max_num_labels); + } + comp->emit = NULL; + comp->emit_inline_asm_method_table = ASM_EMITTER_TABLE; + compile_scope_inline_asm(comp, s, MP_PASS_CODE_SIZE); + #if MICROPY_EMIT_INLINE_XTENSA + // Xtensa requires an extra pass to compute size of l32r const table + // TODO this can be improved by calculating it during SCOPE pass + // but that requires some other structural changes to the asm emitters + #if MICROPY_DYNAMIC_COMPILER + if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_XTENSA) + #endif + { + compile_scope_inline_asm(comp, s, MP_PASS_CODE_SIZE); + } + #endif + if (comp->compile_error == MP_OBJ_NULL) { + compile_scope_inline_asm(comp, s, MP_PASS_EMIT); + } + } else + #endif + { + + // choose the emit type + + switch (s->emit_options) { + + #if MICROPY_EMIT_NATIVE + case MP_EMIT_OPT_NATIVE_PYTHON: + case MP_EMIT_OPT_VIPER: + if (emit_native == NULL) { + emit_native = NATIVE_EMITTER(new)(&comp->emit_common, &comp->compile_error, &comp->next_label, max_num_labels); + } + comp->emit_method_table = NATIVE_EMITTER_TABLE; + comp->emit = emit_native; + break; + #endif // MICROPY_EMIT_NATIVE + + default: + comp->emit = emit_bc; + #if MICROPY_EMIT_NATIVE + comp->emit_method_table = &emit_bc_method_table; + #endif + break; + } + + // need a pass to compute stack size + compile_scope(comp, s, MP_PASS_STACK_SIZE); + + // second last pass: compute code size + if (comp->compile_error == MP_OBJ_NULL) { + compile_scope(comp, s, MP_PASS_CODE_SIZE); + } + + // final pass: emit code + // the emitter can request multiple of these passes + if (comp->compile_error == MP_OBJ_NULL) { + while (!compile_scope(comp, s, MP_PASS_EMIT)) { + } + } + } + } + + if (comp->compile_error != MP_OBJ_NULL) { + // if there is no line number for the error then use the line + // number for the start of this scope + compile_error_set_line(comp, comp->scope_cur->pn); + // add a traceback to the exception using relevant source info + mp_obj_exception_add_traceback(comp->compile_error, source_file, + comp->compile_error_line, comp->scope_cur->simple_name); + } + + // construct the global qstr/const table for this module + cm->rc = module_scope->raw_code; + #if MICROPY_PERSISTENT_CODE_SAVE + cm->has_native = false; + #if MICROPY_EMIT_NATIVE + if (emit_native != NULL) { + cm->has_native = true; + } + #endif + #if MICROPY_EMIT_INLINE_ASM + if (comp->emit_inline_asm != NULL) { + cm->has_native = true; + } + #endif + cm->n_qstr = comp->emit_common.qstr_map.used; + cm->n_obj = comp->emit_common.const_obj_list.len; + #endif + if (comp->compile_error == MP_OBJ_NULL) { + mp_emit_common_populate_module_context(&comp->emit_common, source_file, cm->context); + + #if MICROPY_DEBUG_PRINTERS + // now that the module context is valid, the raw codes can be printed + if (mp_verbose_flag >= 2) { + for (scope_t *s = comp->scope_head; s != NULL; s = s->next) { + mp_raw_code_t *rc = s->raw_code; + if (rc->kind == MP_CODE_BYTECODE) { + mp_bytecode_print(&mp_plat_print, rc, s->raw_code_data_len, &cm->context->constants); + } + } + } + #endif + } + + // free the emitters + + emit_bc_free(emit_bc); + #if MICROPY_EMIT_NATIVE + if (emit_native != NULL) { + NATIVE_EMITTER(free)(emit_native); + } + #endif + #if MICROPY_EMIT_INLINE_ASM + if (comp->emit_inline_asm != NULL) { + ASM_EMITTER(free)(comp->emit_inline_asm); + } + #endif + + // free the parse tree + mp_parse_tree_clear(parse_tree); + + // free the scopes + for (scope_t *s = module_scope; s;) { + scope_t *next = s->next; + scope_free(s); + s = next; + } + + if (comp->compile_error != MP_OBJ_NULL) { + nlr_raise(comp->compile_error); + } +} + +mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { + mp_compiled_module_t cm; + cm.context = m_new_obj(mp_module_context_t); + cm.context->module.globals = mp_globals_get(); + mp_compile_to_raw_code(parse_tree, source_file, is_repl, &cm); + // return function that executes the outer module + return mp_make_function_from_proto_fun(cm.rc, cm.context, NULL); +} + +#endif // MICROPY_ENABLE_COMPILER diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/compile.h b/non_catalog_apps/mp_flipper/lib/micropython/py/compile.h new file mode 100644 index 00000000..f9970a52 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/compile.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_COMPILE_H +#define MICROPY_INCLUDED_PY_COMPILE_H + +#include "py/lexer.h" +#include "py/parse.h" +#include "py/emitglue.h" + +#if MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT +// set to `true` to allow top-level await expressions +extern bool mp_compile_allow_top_level_await; +#endif + +// the compiler will raise an exception if an error occurred +// the compiler will clear the parse tree before it returns +// mp_globals_get() will be used for the context +mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); + +#if MICROPY_PERSISTENT_CODE_SAVE +// this has the same semantics as mp_compile +void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm); +#endif + +// this is implemented in runtime.c +mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals); + +#endif // MICROPY_INCLUDED_PY_COMPILE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/dynruntime.h b/non_catalog_apps/mp_flipper/lib/micropython/py/dynruntime.h new file mode 100644 index 00000000..d07b1dce --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/dynruntime.h @@ -0,0 +1,301 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_DYNRUNTIME_H +#define MICROPY_INCLUDED_PY_DYNRUNTIME_H + +// This header file contains definitions to dynamically implement the static +// MicroPython runtime API defined in py/obj.h and py/runtime.h. + +#include "py/binary.h" +#include "py/nativeglue.h" +#include "py/objfun.h" +#include "py/objstr.h" +#include "py/objtype.h" + +#if !MICROPY_ENABLE_DYNRUNTIME +#error "dynruntime.h included in non-dynamic-module build." +#endif + +#undef MP_ROM_QSTR +#undef MP_OBJ_QSTR_VALUE +#undef MP_OBJ_NEW_QSTR +#undef mp_const_none +#undef mp_const_false +#undef mp_const_true +#undef mp_const_empty_bytes +#undef mp_const_empty_tuple +#undef nlr_raise + +/******************************************************************************/ +// Memory allocation + +#define m_malloc(n) (m_malloc_dyn((n))) +#define m_free(ptr) (m_free_dyn((ptr))) +#define m_realloc(ptr, new_num_bytes) (m_realloc_dyn((ptr), (new_num_bytes))) + +static inline void *m_malloc_dyn(size_t n) { + // TODO won't raise on OOM + return mp_fun_table.realloc_(NULL, n, false); +} + +static inline void m_free_dyn(void *ptr) { + mp_fun_table.realloc_(ptr, 0, false); +} + +static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { + // TODO won't raise on OOM + return mp_fun_table.realloc_(ptr, new_num_bytes, true); +} + +/******************************************************************************/ +// Printing + +#define mp_plat_print (*mp_fun_table.plat_print) +#define mp_printf(p, ...) (mp_fun_table.printf_((p), __VA_ARGS__)) +#define mp_vprintf(p, fmt, args) (mp_fun_table.vprintf_((p), (fmt), (args))) + +/******************************************************************************/ +// Types and objects + +#define MP_OBJ_NEW_QSTR(x) (mp_fun_table.native_to_obj(x, MP_NATIVE_TYPE_QSTR)) + +#define mp_type_type (*mp_fun_table.type_type) +#define mp_type_NoneType (*mp_obj_get_type(mp_const_none)) +#define mp_type_bool (*mp_obj_get_type(mp_const_false)) +#define mp_type_int (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_int))) +#define mp_type_str (*mp_fun_table.type_str) +#define mp_type_bytes (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_bytes))) +#define mp_type_tuple (*((mp_obj_base_t *)mp_const_empty_tuple)->type) +#define mp_type_list (*mp_fun_table.type_list) +#define mp_type_EOFError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_EOFError))) +#define mp_type_IndexError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_IndexError))) +#define mp_type_KeyError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_KeyError))) +#define mp_type_NotImplementedError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_NotImplementedError))) +#define mp_type_RuntimeError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_RuntimeError))) +#define mp_type_TypeError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_TypeError))) +#define mp_type_ValueError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_ValueError))) + +#define mp_stream_read_obj (*mp_fun_table.stream_read_obj) +#define mp_stream_readinto_obj (*mp_fun_table.stream_readinto_obj) +#define mp_stream_unbuffered_readline_obj (*mp_fun_table.stream_unbuffered_readline_obj) +#define mp_stream_write_obj (*mp_fun_table.stream_write_obj) + +#define mp_const_none ((mp_obj_t)mp_fun_table.const_none) +#define mp_const_false ((mp_obj_t)mp_fun_table.const_false) +#define mp_const_true ((mp_obj_t)mp_fun_table.const_true) +#define mp_const_empty_bytes (MP_OBJ_TYPE_GET_SLOT(&mp_type_bytes, make_new)(NULL, 0, 0, NULL)) +#define mp_const_empty_tuple (mp_fun_table.new_tuple(0, NULL)) + +#define mp_obj_new_bool(b) ((b) ? (mp_obj_t)mp_fun_table.const_true : (mp_obj_t)mp_fun_table.const_false) +#define mp_obj_new_int(i) (mp_fun_table.native_to_obj(i, MP_NATIVE_TYPE_INT)) +#define mp_obj_new_int_from_uint(i) (mp_fun_table.native_to_obj(i, MP_NATIVE_TYPE_UINT)) +#define mp_obj_new_str(data, len) (mp_fun_table.obj_new_str((data), (len))) +#define mp_obj_new_str_of_type(t, d, l) (mp_obj_new_str_of_type_dyn((t), (d), (l))) +#define mp_obj_new_bytes(data, len) (mp_fun_table.obj_new_bytes((data), (len))) +#define mp_obj_new_bytearray_by_ref(n, i) (mp_fun_table.obj_new_bytearray_by_ref((n), (i))) +#define mp_obj_new_tuple(n, items) (mp_fun_table.new_tuple((n), (items))) +#define mp_obj_new_list(n, items) (mp_fun_table.new_list((n), (items))) +#define mp_obj_new_dict(n) (mp_fun_table.new_dict((n))) + +#define mp_obj_get_type(o) (mp_fun_table.obj_get_type((o))) +#define mp_obj_cast_to_native_base(o, t) (mp_obj_cast_to_native_base_dyn((o), (t))) +#define mp_obj_get_int(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_INT)) +#define mp_obj_get_int_truncated(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_UINT)) +#define mp_obj_str_get_str(s) (mp_obj_str_get_data_dyn((s), NULL)) +#define mp_obj_str_get_data(o, len) (mp_obj_str_get_data_dyn((o), (len))) +#define mp_get_buffer(o, bufinfo, fl) (mp_fun_table.get_buffer((o), (bufinfo), (fl))) +#define mp_get_buffer_raise(o, bufinfo, fl) (mp_fun_table.get_buffer((o), (bufinfo), (fl) | MP_BUFFER_RAISE_IF_UNSUPPORTED)) +#define mp_get_stream_raise(s, flags) (mp_fun_table.get_stream_raise((s), (flags))) +#define mp_obj_is_true(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_BOOL)) + +#define mp_obj_len(o) (mp_obj_len_dyn(o)) +#define mp_obj_subscr(base, index, val) (mp_fun_table.obj_subscr((base), (index), (val))) +#define mp_obj_get_array(o, len, items) (mp_obj_get_array_dyn((o), (len), (items))) +#define mp_obj_list_append(list, item) (mp_fun_table.list_append((list), (item))) +#define mp_obj_dict_store(dict, key, val) (mp_fun_table.dict_store((dict), (key), (val))) + +#define mp_obj_malloc_helper(n, t) (mp_obj_malloc_helper_dyn(n, t)) + +static inline mp_obj_t mp_obj_new_str_of_type_dyn(const mp_obj_type_t *type, const byte *data, size_t len) { + if (type == &mp_type_str) { + return mp_obj_new_str((const char *)data, len); + } else { + return mp_obj_new_bytes(data, len); + } +} + +static inline mp_obj_t mp_obj_cast_to_native_base_dyn(mp_obj_t self_in, mp_const_obj_t native_type) { + const mp_obj_type_t *self_type = mp_obj_get_type(self_in); + + if (MP_OBJ_FROM_PTR(self_type) == native_type) { + return self_in; + } else if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(self_type, parent) != native_type) { + // The self_in object is not a direct descendant of native_type, so fail the cast. + // This is a very simple version of mp_obj_is_subclass_fast that could be improved. + return MP_OBJ_NULL; + } else { + mp_obj_instance_t *self = (mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in); + return self->subobj[0]; + } +} + +static inline void *mp_obj_str_get_data_dyn(mp_obj_t o, size_t *l) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(o, &bufinfo, MP_BUFFER_READ); + if (l != NULL) { + *l = bufinfo.len; + } + return bufinfo.buf; +} + +static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { + // If bytes implemented MP_UNARY_OP_LEN could use: mp_unary_op(MP_UNARY_OP_LEN, o) + return mp_fun_table.call_function_n_kw(mp_fun_table.load_name(MP_QSTR_len), 1, &o); +} + +static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type_t *type) { + mp_obj_base_t *base = (mp_obj_base_t *)m_malloc(num_bytes); + base->type = type; + return base; +} + +/******************************************************************************/ +// General runtime functions + +#define mp_binary_get_size(struct_type, val_type, palign) (mp_fun_table.binary_get_size((struct_type), (val_type), (palign))) +#define mp_binary_get_val_array(typecode, p, index) (mp_fun_table.binary_get_val_array((typecode), (p), (index))) +#define mp_binary_set_val_array(typecode, p, index, val_in) (mp_fun_table.binary_set_val_array((typecode), (p), (index), (val_in))) + +#define mp_load_name(qst) (mp_fun_table.load_name((qst))) +#define mp_load_global(qst) (mp_fun_table.load_global((qst))) +#define mp_load_attr(base, attr) (mp_fun_table.load_attr((base), (attr))) +#define mp_load_method(base, attr, dest) (mp_fun_table.load_method((base), (attr), (dest))) +#define mp_load_super_method(attr, dest) (mp_fun_table.load_super_method((attr), (dest))) +#define mp_store_name(qst, obj) (mp_fun_table.store_name((qst), (obj))) +#define mp_store_global(qst, obj) (mp_fun_table.store_global((qst), (obj))) +#define mp_store_attr(base, attr, val) (mp_fun_table.store_attr((base), (attr), (val))) + +#define mp_unary_op(op, obj) (mp_fun_table.unary_op((op), (obj))) +#define mp_binary_op(op, lhs, rhs) (mp_fun_table.binary_op((op), (lhs), (rhs))) + +#define mp_make_function_from_proto_fun(rc, context, def_args) \ + (mp_fun_table.make_function_from_proto_fun((rc), (context), (def_args))) + +#define mp_call_function_n_kw(fun, n_args, n_kw, args) \ + (mp_fun_table.call_function_n_kw((fun), (n_args) | ((n_kw) << 8), args)) + +#define mp_arg_check_num(n_args, n_kw, n_args_min, n_args_max, takes_kw) \ + (mp_fun_table.arg_check_num_sig((n_args), (n_kw), MP_OBJ_FUN_MAKE_SIG((n_args_min), (n_args_max), (takes_kw)))) + +#define MP_DYNRUNTIME_INIT_ENTRY \ + mp_obj_t old_globals = mp_fun_table.swap_globals(self->context->module.globals); \ + mp_raw_code_truncated_t rc; \ + rc.proto_fun_indicator[0] = MP_PROTO_FUN_INDICATOR_RAW_CODE_0; \ + rc.proto_fun_indicator[1] = MP_PROTO_FUN_INDICATOR_RAW_CODE_1; \ + rc.kind = MP_CODE_NATIVE_VIPER; \ + rc.is_generator = 0; \ + (void)rc; + +#define MP_DYNRUNTIME_INIT_EXIT \ + mp_fun_table.swap_globals(old_globals); \ + return mp_const_none; + +#define MP_DYNRUNTIME_MAKE_FUNCTION(f) \ + (mp_make_function_from_proto_fun((rc.fun_data = (f), (const mp_raw_code_t *)&rc), self->context, NULL)) + +#define mp_import_name(name, fromlist, level) \ + (mp_fun_table.import_name((name), (fromlist), (level))) +#define mp_import_from(module, name) \ + (mp_fun_table.import_from((module), (name))) +#define mp_import_all(module) \ + (mp_fun_table.import_all((module)) + +/******************************************************************************/ +// Exceptions + +#define mp_obj_new_exception(o) ((mp_obj_t)(o)) // Assumes returned object will be raised, will create instance then +#define mp_obj_new_exception_arg1(e_type, arg) (mp_obj_new_exception_arg1_dyn((e_type), (arg))) + +#define nlr_raise(o) (mp_raise_dyn(o)) +#define mp_raise_type_arg(type, arg) (mp_raise_dyn(mp_obj_new_exception_arg1_dyn((type), (arg)))) +#define mp_raise_msg(type, msg) (mp_fun_table.raise_msg((type), (msg))) +#define mp_raise_OSError(er) (mp_raise_OSError_dyn(er)) +#define mp_raise_NotImplementedError(msg) (mp_raise_msg(&mp_type_NotImplementedError, (msg))) +#define mp_raise_TypeError(msg) (mp_raise_msg(&mp_type_TypeError, (msg))) +#define mp_raise_ValueError(msg) (mp_raise_msg(&mp_type_ValueError, (msg))) + +static inline mp_obj_t mp_obj_new_exception_arg1_dyn(const mp_obj_type_t *exc_type, mp_obj_t arg) { + mp_obj_t args[1] = { arg }; + return mp_call_function_n_kw(MP_OBJ_FROM_PTR(exc_type), 1, 0, &args[0]); +} + +static NORETURN inline void mp_raise_dyn(mp_obj_t o) { + mp_fun_table.raise(o); + for (;;) { + } +} + +static inline void mp_raise_OSError_dyn(int er) { + mp_obj_t args[1] = { MP_OBJ_NEW_SMALL_INT(er) }; + nlr_raise(mp_call_function_n_kw(mp_load_global(MP_QSTR_OSError), 1, 0, &args[0])); +} + +/******************************************************************************/ +// Floating point + +#define mp_obj_new_float_from_f(f) (mp_fun_table.obj_new_float_from_f((f))) +#define mp_obj_new_float_from_d(d) (mp_fun_table.obj_new_float_from_d((d))) +#define mp_obj_get_float_to_f(o) (mp_fun_table.obj_get_float_to_f((o))) +#define mp_obj_get_float_to_d(o) (mp_fun_table.obj_get_float_to_d((o))) + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define mp_obj_new_float(f) (mp_obj_new_float_from_f((f))) +#define mp_obj_get_float(o) (mp_obj_get_float_to_f((o))) +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define mp_obj_new_float(f) (mp_obj_new_float_from_d((f))) +#define mp_obj_get_float(o) (mp_obj_get_float_to_d((o))) +#endif + +/******************************************************************************/ +// Inline function definitions. + +// *items may point inside a GC block +static inline void mp_obj_get_array_dyn(mp_obj_t o, size_t *len, mp_obj_t **items) { + const mp_obj_type_t *type = mp_obj_get_type(o); + if (type == &mp_type_tuple) { + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(o); + *len = t->len; + *items = &t->items[0]; + } else if (type == &mp_type_list) { + mp_obj_list_t *l = MP_OBJ_TO_PTR(o); + *len = l->len; + *items = l->items; + } else { + mp_raise_TypeError("expected tuple/list"); + } +} + +#endif // MICROPY_INCLUDED_PY_DYNRUNTIME_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emit.h b/non_catalog_apps/mp_flipper/lib/micropython/py/emit.h new file mode 100644 index 00000000..26f978ba --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emit.h @@ -0,0 +1,313 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_EMIT_H +#define MICROPY_INCLUDED_PY_EMIT_H + +#include "py/lexer.h" +#include "py/scope.h" + +/* Notes on passes: + * We don't know exactly the opcodes in pass 1 because they depend on the + * closing over of variables (LOAD_CLOSURE, BUILD_TUPLE, MAKE_CLOSURE), which + * depends on determining the scope of variables in each function, and this + * is not known until the end of pass 1. + * As a consequence, we don't know the maximum stack size until the end of pass 2. + * This is problematic for some emitters (x64) since they need to know the maximum + * stack size to compile the entry to the function, and this affects code size. + */ + +typedef enum { + MP_PASS_SCOPE = 1, // work out id's and their kind, and number of labels + MP_PASS_STACK_SIZE = 2, // work out maximum stack size + MP_PASS_CODE_SIZE = 3, // work out code size and label offsets + MP_PASS_EMIT = 4, // emit code (may be run multiple times if the emitter requests it) +} pass_kind_t; + +#define MP_EMIT_STAR_FLAG_SINGLE (0x01) +#define MP_EMIT_STAR_FLAG_DOUBLE (0x02) + +#define MP_EMIT_BREAK_FROM_FOR (0x8000) + +// Kind for emit_id_ops->local() +#define MP_EMIT_IDOP_LOCAL_FAST (0) +#define MP_EMIT_IDOP_LOCAL_DEREF (1) + +// Kind for emit_id_ops->global() +#define MP_EMIT_IDOP_GLOBAL_NAME (0) +#define MP_EMIT_IDOP_GLOBAL_GLOBAL (1) + +// Kind for emit->import() +#define MP_EMIT_IMPORT_NAME (0) +#define MP_EMIT_IMPORT_FROM (1) +#define MP_EMIT_IMPORT_STAR (2) + +// Kind for emit->subscr() +#define MP_EMIT_SUBSCR_LOAD (0) +#define MP_EMIT_SUBSCR_STORE (1) +#define MP_EMIT_SUBSCR_DELETE (2) + +// Kind for emit->attr() +#define MP_EMIT_ATTR_LOAD (0) +#define MP_EMIT_ATTR_STORE (1) +#define MP_EMIT_ATTR_DELETE (2) + +// Kind for emit->setup_block() +#define MP_EMIT_SETUP_BLOCK_WITH (0) +#define MP_EMIT_SETUP_BLOCK_EXCEPT (1) +#define MP_EMIT_SETUP_BLOCK_FINALLY (2) + +// Kind for emit->build() +#define MP_EMIT_BUILD_TUPLE (0) +#define MP_EMIT_BUILD_LIST (1) +#define MP_EMIT_BUILD_MAP (2) +#define MP_EMIT_BUILD_SET (3) +#define MP_EMIT_BUILD_SLICE (4) + +// Kind for emit->yield() +#define MP_EMIT_YIELD_VALUE (0) +#define MP_EMIT_YIELD_FROM (1) + +typedef struct _emit_t emit_t; + +typedef struct _mp_emit_common_t { + pass_kind_t pass; + uint16_t ct_cur_child; + mp_raw_code_t **children; + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + mp_map_t qstr_map; + #endif + mp_obj_list_t const_obj_list; +} mp_emit_common_t; + +typedef struct _mp_emit_method_table_id_ops_t { + void (*local)(emit_t *emit, qstr qst, mp_uint_t local_num, int kind); + void (*global)(emit_t *emit, qstr qst, int kind); +} mp_emit_method_table_id_ops_t; + +typedef struct _emit_method_table_t { + #if MICROPY_DYNAMIC_COMPILER + emit_t *(*emit_new)(mp_emit_common_t * emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); + void (*emit_free)(emit_t *emit); + #endif + + void (*start_pass)(emit_t *emit, pass_kind_t pass, scope_t *scope); + bool (*end_pass)(emit_t *emit); + void (*adjust_stack_size)(emit_t *emit, mp_int_t delta); + void (*set_source_line)(emit_t *emit, mp_uint_t line); + + mp_emit_method_table_id_ops_t load_id; + mp_emit_method_table_id_ops_t store_id; + mp_emit_method_table_id_ops_t delete_id; + + void (*label_assign)(emit_t *emit, mp_uint_t l); + void (*import)(emit_t *emit, qstr qst, int kind); + void (*load_const_tok)(emit_t *emit, mp_token_kind_t tok); + void (*load_const_small_int)(emit_t *emit, mp_int_t arg); + void (*load_const_str)(emit_t *emit, qstr qst); + void (*load_const_obj)(emit_t *emit, mp_obj_t obj); + void (*load_null)(emit_t *emit); + void (*load_method)(emit_t *emit, qstr qst, bool is_super); + void (*load_build_class)(emit_t *emit); + void (*subscr)(emit_t *emit, int kind); + void (*attr)(emit_t *emit, qstr qst, int kind); + void (*dup_top)(emit_t *emit); + void (*dup_top_two)(emit_t *emit); + void (*pop_top)(emit_t *emit); + void (*rot_two)(emit_t *emit); + void (*rot_three)(emit_t *emit); + void (*jump)(emit_t *emit, mp_uint_t label); + void (*pop_jump_if)(emit_t *emit, bool cond, mp_uint_t label); + void (*jump_if_or_pop)(emit_t *emit, bool cond, mp_uint_t label); + void (*unwind_jump)(emit_t *emit, mp_uint_t label, mp_uint_t except_depth); + void (*setup_block)(emit_t *emit, mp_uint_t label, int kind); + void (*with_cleanup)(emit_t *emit, mp_uint_t label); + void (*end_finally)(emit_t *emit); + void (*get_iter)(emit_t *emit, bool use_stack); + void (*for_iter)(emit_t *emit, mp_uint_t label); + void (*for_iter_end)(emit_t *emit); + void (*pop_except_jump)(emit_t *emit, mp_uint_t label, bool within_exc_handler); + void (*unary_op)(emit_t *emit, mp_unary_op_t op); + void (*binary_op)(emit_t *emit, mp_binary_op_t op); + void (*build)(emit_t *emit, mp_uint_t n_args, int kind); + void (*store_map)(emit_t *emit); + void (*store_comp)(emit_t *emit, scope_kind_t kind, mp_uint_t set_stack_index); + void (*unpack_sequence)(emit_t *emit, mp_uint_t n_args); + void (*unpack_ex)(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right); + void (*make_function)(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults); + void (*make_closure)(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults); + void (*call_function)(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags); + void (*call_method)(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags); + void (*return_value)(emit_t *emit); + void (*raise_varargs)(emit_t *emit, mp_uint_t n_args); + void (*yield)(emit_t *emit, int kind); + + // these methods are used to control entry to/exit from an exception handler + // they may or may not emit code + void (*start_except_handler)(emit_t *emit); + void (*end_except_handler)(emit_t *emit); +} emit_method_table_t; + +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE +qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst); +#else +static inline qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst) { + return qst; +} +#endif + +size_t mp_emit_common_use_const_obj(mp_emit_common_t *emit, mp_obj_t const_obj); + +static inline size_t mp_emit_common_alloc_const_child(mp_emit_common_t *emit, mp_raw_code_t *rc) { + if (emit->pass == MP_PASS_EMIT) { + emit->children[emit->ct_cur_child] = rc; + } + return emit->ct_cur_child++; +} + +static inline void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst) { + scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT); +} + +id_info_t *mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst); +void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst); + +extern const emit_method_table_t emit_bc_method_table; +extern const emit_method_table_t emit_native_x64_method_table; +extern const emit_method_table_t emit_native_x86_method_table; +extern const emit_method_table_t emit_native_thumb_method_table; +extern const emit_method_table_t emit_native_arm_method_table; +extern const emit_method_table_t emit_native_xtensa_method_table; +extern const emit_method_table_t emit_native_xtensawin_method_table; + +extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops; +extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops; +extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops; + +emit_t *emit_bc_new(mp_emit_common_t *emit_common); +emit_t *emit_native_x64_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_x86_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_thumb_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_arm_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_xtensa_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_xtensawin_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); + +void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels); + +void emit_bc_free(emit_t *emit); +void emit_native_x64_free(emit_t *emit); +void emit_native_x86_free(emit_t *emit); +void emit_native_thumb_free(emit_t *emit); +void emit_native_arm_free(emit_t *emit); +void emit_native_xtensa_free(emit_t *emit); +void emit_native_xtensawin_free(emit_t *emit); + +void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope); +bool mp_emit_bc_end_pass(emit_t *emit); +void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta); +void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t line); + +void mp_emit_bc_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind); +void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind); +void mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind); +void mp_emit_bc_store_global(emit_t *emit, qstr qst, int kind); +void mp_emit_bc_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind); +void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind); + +void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l); +void mp_emit_bc_import(emit_t *emit, qstr qst, int kind); +void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok); +void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg); +void mp_emit_bc_load_const_str(emit_t *emit, qstr qst); +void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj); +void mp_emit_bc_load_null(emit_t *emit); +void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super); +void mp_emit_bc_load_build_class(emit_t *emit); +void mp_emit_bc_subscr(emit_t *emit, int kind); +void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind); +void mp_emit_bc_dup_top(emit_t *emit); +void mp_emit_bc_dup_top_two(emit_t *emit); +void mp_emit_bc_pop_top(emit_t *emit); +void mp_emit_bc_rot_two(emit_t *emit); +void mp_emit_bc_rot_three(emit_t *emit); +void mp_emit_bc_jump(emit_t *emit, mp_uint_t label); +void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label); +void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label); +void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth); +void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind); +void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label); +void mp_emit_bc_end_finally(emit_t *emit); +void mp_emit_bc_get_iter(emit_t *emit, bool use_stack); +void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label); +void mp_emit_bc_for_iter_end(emit_t *emit); +void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler); +void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op); +void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op); +void mp_emit_bc_build(emit_t *emit, mp_uint_t n_args, int kind); +void mp_emit_bc_store_map(emit_t *emit); +void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t list_stack_index); +void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args); +void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right); +void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults); +void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults); +void mp_emit_bc_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags); +void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags); +void mp_emit_bc_return_value(emit_t *emit); +void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args); +void mp_emit_bc_yield(emit_t *emit, int kind); +void mp_emit_bc_start_except_handler(emit_t *emit); +void mp_emit_bc_end_except_handler(emit_t *emit); + +typedef struct _emit_inline_asm_t emit_inline_asm_t; + +typedef struct _emit_inline_asm_method_table_t { + #if MICROPY_DYNAMIC_COMPILER + emit_inline_asm_t *(*asm_new)(mp_uint_t max_num_labels); + void (*asm_free)(emit_inline_asm_t *emit); + #endif + + void (*start_pass)(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot); + void (*end_pass)(emit_inline_asm_t *emit, mp_uint_t type_sig); + mp_uint_t (*count_params)(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params); + bool (*label)(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id); + void (*op)(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args); +} emit_inline_asm_method_table_t; + +extern const emit_inline_asm_method_table_t emit_inline_thumb_method_table; +extern const emit_inline_asm_method_table_t emit_inline_xtensa_method_table; + +emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels); +emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels); + +void emit_inline_thumb_free(emit_inline_asm_t *emit); +void emit_inline_xtensa_free(emit_inline_asm_t *emit); + +#if MICROPY_WARNINGS +void mp_emitter_warning(pass_kind_t pass, const char *msg); +#else +#define mp_emitter_warning(pass, msg) +#endif + +#endif // MICROPY_INCLUDED_PY_EMIT_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitbc.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitbc.c new file mode 100644 index 00000000..05754cfa --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitbc.c @@ -0,0 +1,905 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * + * 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 "py/mpstate.h" +#include "py/smallint.h" +#include "py/emit.h" +#include "py/bc0.h" + +#if MICROPY_ENABLE_COMPILER + +#define DUMMY_DATA_SIZE (MP_ENCODE_UINT_MAX_BYTES) + +struct _emit_t { + // Accessed as mp_obj_t, so must be aligned as such, and we rely on the + // memory allocator returning a suitably aligned pointer. + // Should work for cases when mp_obj_t is 64-bit on a 32-bit machine. + byte dummy_data[DUMMY_DATA_SIZE]; + + pass_kind_t pass : 8; + + // Set to true if the code generator should suppress emitted code due to it + // being dead code. This can happen when opcodes immediately follow an + // unconditional flow control (eg jump or raise). + bool suppress; + + int stack_size; + + mp_emit_common_t *emit_common; + scope_t *scope; + + mp_uint_t last_source_line_offset; + mp_uint_t last_source_line; + + size_t max_num_labels; + size_t *label_offsets; + + size_t code_info_offset; + size_t code_info_size; + size_t bytecode_offset; + size_t bytecode_size; + byte *code_base; // stores both byte code and code info + bool overflow; + + size_t n_info; + size_t n_cell; +}; + +emit_t *emit_bc_new(mp_emit_common_t *emit_common) { + emit_t *emit = m_new0(emit_t, 1); + emit->emit_common = emit_common; + return emit; +} + +void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels) { + emit->max_num_labels = max_num_labels; + emit->label_offsets = m_new(size_t, emit->max_num_labels); +} + +void emit_bc_free(emit_t *emit) { + m_del(size_t, emit->label_offsets, emit->max_num_labels); + m_del_obj(emit_t, emit); +} + +// all functions must go through this one to emit code info +static uint8_t *emit_get_cur_to_write_code_info(void *emit_in, size_t num_bytes_to_write) { + emit_t *emit = emit_in; + if (emit->pass < MP_PASS_EMIT) { + emit->code_info_offset += num_bytes_to_write; + return emit->dummy_data; + } else { + assert(emit->code_info_offset + num_bytes_to_write <= emit->code_info_size); + byte *c = emit->code_base + emit->code_info_offset; + emit->code_info_offset += num_bytes_to_write; + return c; + } +} + +static void emit_write_code_info_byte(emit_t *emit, byte val) { + *emit_get_cur_to_write_code_info(emit, 1) = val; +} + +static void emit_write_code_info_qstr(emit_t *emit, qstr qst) { + mp_encode_uint(emit, emit_get_cur_to_write_code_info, mp_emit_common_use_qstr(emit->emit_common, qst)); +} + +#if MICROPY_ENABLE_SOURCE_LINE +static void emit_write_code_info_bytes_lines(emit_t *emit, mp_uint_t bytes_to_skip, mp_uint_t lines_to_skip) { + assert(bytes_to_skip > 0 || lines_to_skip > 0); + while (bytes_to_skip > 0 || lines_to_skip > 0) { + mp_uint_t b, l; + if (lines_to_skip <= 6 || bytes_to_skip > 0xf) { + // use 0b0LLBBBBB encoding + b = MIN(bytes_to_skip, 0x1f); + if (b < bytes_to_skip) { + // we can't skip any lines until we skip all the bytes + l = 0; + } else { + l = MIN(lines_to_skip, 0x3); + } + *emit_get_cur_to_write_code_info(emit, 1) = b | (l << 5); + } else { + // use 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + b = MIN(bytes_to_skip, 0xf); + l = MIN(lines_to_skip, 0x7ff); + byte *ci = emit_get_cur_to_write_code_info(emit, 2); + ci[0] = 0x80 | b | ((l >> 4) & 0x70); + ci[1] = l; + } + bytes_to_skip -= b; + lines_to_skip -= l; + } +} +#endif + +// all functions must go through this one to emit byte code +static uint8_t *emit_get_cur_to_write_bytecode(void *emit_in, size_t num_bytes_to_write) { + emit_t *emit = emit_in; + if (emit->suppress) { + return emit->dummy_data; + } + if (emit->pass < MP_PASS_EMIT) { + emit->bytecode_offset += num_bytes_to_write; + return emit->dummy_data; + } else { + assert(emit->bytecode_offset + num_bytes_to_write <= emit->bytecode_size); + byte *c = emit->code_base + emit->code_info_size + emit->bytecode_offset; + emit->bytecode_offset += num_bytes_to_write; + return c; + } +} + +static void emit_write_bytecode_raw_byte(emit_t *emit, byte b1) { + byte *c = emit_get_cur_to_write_bytecode(emit, 1); + c[0] = b1; +} + +static void emit_write_bytecode_byte(emit_t *emit, int stack_adj, byte b1) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); + byte *c = emit_get_cur_to_write_bytecode(emit, 1); + c[0] = b1; +} + +// Similar to mp_encode_uint(), just some extra handling to encode sign +static void emit_write_bytecode_byte_int(emit_t *emit, int stack_adj, byte b1, mp_int_t num) { + emit_write_bytecode_byte(emit, stack_adj, b1); + + // We store each 7 bits in a separate byte, and that's how many bytes needed + byte buf[MP_ENCODE_UINT_MAX_BYTES]; + byte *p = buf + sizeof(buf); + // We encode in little-ending order, but store in big-endian, to help decoding + do { + *--p = num & 0x7f; + num >>= 7; + } while (num != 0 && num != -1); + // Make sure that highest bit we stored (mask 0x40) matches sign + // of the number. If not, store extra byte just to encode sign + if (num == -1 && (*p & 0x40) == 0) { + *--p = 0x7f; + } else if (num == 0 && (*p & 0x40) != 0) { + *--p = 0; + } + + byte *c = emit_get_cur_to_write_bytecode(emit, buf + sizeof(buf) - p); + while (p != buf + sizeof(buf) - 1) { + *c++ = *p++ | 0x80; + } + *c = *p; +} + +static void emit_write_bytecode_byte_uint(emit_t *emit, int stack_adj, byte b, mp_uint_t val) { + emit_write_bytecode_byte(emit, stack_adj, b); + mp_encode_uint(emit, emit_get_cur_to_write_bytecode, val); +} + +static void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n) { + emit_write_bytecode_byte_uint(emit, stack_adj, b, n); +} + +static void emit_write_bytecode_byte_qstr(emit_t *emit, int stack_adj, byte b, qstr qst) { + emit_write_bytecode_byte_uint(emit, stack_adj, b, mp_emit_common_use_qstr(emit->emit_common, qst)); +} + +static void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) { + emit_write_bytecode_byte_const(emit, stack_adj, b, mp_emit_common_use_const_obj(emit->emit_common, obj)); +} + +static void emit_write_bytecode_byte_child(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) { + emit_write_bytecode_byte_const(emit, stack_adj, b, + mp_emit_common_alloc_const_child(emit->emit_common, rc)); + #if MICROPY_PY_SYS_SETTRACE + rc->line_of_definition = emit->last_source_line; + #endif +} + +// Emit a jump opcode to a destination label. +// The offset to the label is relative to the ip following this instruction. +// The offset is encoded as either 1 or 2 bytes, depending on how big it is. +// The encoding of this jump opcode can change size from one pass to the next, +// but it must only ever decrease in size on successive passes. +static void emit_write_bytecode_byte_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); + + if (emit->suppress) { + return; + } + + // Determine if the jump offset is signed or unsigned, based on the opcode. + const bool is_signed = b1 <= MP_BC_POP_JUMP_IF_FALSE; + + // Default to a 2-byte encoding (the largest) with an unknown jump offset. + unsigned int jump_encoding_size = 1; + ssize_t bytecode_offset = 0; + + // Compute the jump size and offset only when code size is known. + if (emit->pass >= MP_PASS_CODE_SIZE) { + // The -2 accounts for this jump opcode taking 2 bytes (at least). + bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 2; + + // Check if the bytecode_offset is small enough to use a 1-byte encoding. + if ((is_signed && -64 <= bytecode_offset && bytecode_offset <= 63) + || (!is_signed && (size_t)bytecode_offset <= 127)) { + // Use a 1-byte jump offset. + jump_encoding_size = 0; + } + + // Adjust the offset depending on the size of the encoding of the offset. + bytecode_offset -= jump_encoding_size; + + assert(is_signed || bytecode_offset >= 0); + } + + // Emit the opcode. + byte *c = emit_get_cur_to_write_bytecode(emit, 2 + jump_encoding_size); + c[0] = b1; + if (jump_encoding_size == 0) { + if (is_signed) { + bytecode_offset += 0x40; + } + assert(0 <= bytecode_offset && bytecode_offset <= 0x7f); + c[1] = bytecode_offset; + } else { + if (is_signed) { + bytecode_offset += 0x4000; + } + if (emit->pass == MP_PASS_EMIT && !(0 <= bytecode_offset && bytecode_offset <= 0x7fff)) { + emit->overflow = true; + } + c[1] = 0x80 | (bytecode_offset & 0x7f); + c[2] = bytecode_offset >> 7; + } +} + +void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { + emit->pass = pass; + emit->stack_size = 0; + emit->suppress = false; + emit->scope = scope; + emit->last_source_line_offset = 0; + emit->last_source_line = 1; + emit->bytecode_offset = 0; + emit->code_info_offset = 0; + emit->overflow = false; + + // Write local state size, exception stack size, scope flags and number of arguments + { + mp_uint_t n_state = scope->num_locals + scope->stack_size; + if (n_state == 0) { + // Need at least 1 entry in the state, in the case an exception is + // propagated through this function, the exception is returned in + // the highest slot in the state (fastn[0], see vm.c). + n_state = 1; + } + #if MICROPY_DEBUG_VM_STACK_OVERFLOW + // An extra slot in the stack is needed to detect VM stack overflow + n_state += 1; + #endif + + size_t n_exc_stack = scope->exc_stack_size; + MP_BC_PRELUDE_SIG_ENCODE(n_state, n_exc_stack, scope, emit_write_code_info_byte, emit); + } + + // Write number of cells and size of the source code info + if (emit->pass >= MP_PASS_CODE_SIZE) { + size_t n_info = emit->n_info; + size_t n_cell = emit->n_cell; + MP_BC_PRELUDE_SIZE_ENCODE(n_info, n_cell, emit_write_code_info_byte, emit); + } + + emit->n_info = emit->code_info_offset; + + // Write the name of this function. + emit_write_code_info_qstr(emit, scope->simple_name); + + // Write argument names, needed to resolve positional args passed as keywords. + { + // For a given argument position (indexed by i) we need to find the + // corresponding id_info which is a parameter, as it has the correct + // qstr name to use as the argument name. Note that it's not a simple + // 1-1 mapping (ie i!=j in general) because of possible closed-over + // variables. In the case that the argument i has no corresponding + // parameter we use "*" as its name (since no argument can ever be named + // "*"). We could use a blank qstr but "*" is better for debugging. + // Note: there is some wasted RAM here for the case of storing a qstr + // for each closed-over variable, and maybe there is a better way to do + // it, but that would require changes to mp_setup_code_state. + for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) { + qstr qst = MP_QSTR__star_; + for (int j = 0; j < scope->id_info_len; ++j) { + id_info_t *id = &scope->id_info[j]; + if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) { + qst = id->qst; + break; + } + } + emit_write_code_info_qstr(emit, qst); + } + } +} + +bool mp_emit_bc_end_pass(emit_t *emit) { + if (emit->pass == MP_PASS_SCOPE) { + return true; + } + + // check stack is back to zero size + assert(emit->stack_size == 0); + + // Calculate size of source code info section + emit->n_info = emit->code_info_offset - emit->n_info; + + // Emit closure section of prelude + emit->n_cell = 0; + for (size_t i = 0; i < emit->scope->id_info_len; ++i) { + id_info_t *id = &emit->scope->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL) { + assert(id->local_num <= 255); + emit_write_code_info_byte(emit, id->local_num); // write the local which should be converted to a cell + ++emit->n_cell; + } + } + + if (emit->pass == MP_PASS_CODE_SIZE) { + // calculate size of total code-info + bytecode, in bytes + emit->code_info_size = emit->code_info_offset; + emit->bytecode_size = emit->bytecode_offset; + emit->code_base = m_new0(byte, emit->code_info_size + emit->bytecode_size); + + } else if (emit->pass == MP_PASS_EMIT) { + // Code info and/or bytecode can shrink during this pass. + assert(emit->code_info_offset <= emit->code_info_size); + assert(emit->bytecode_offset <= emit->bytecode_size); + + if (emit->code_info_offset != emit->code_info_size + || emit->bytecode_offset != emit->bytecode_size) { + // Code info and/or bytecode changed size in this pass, so request the + // compiler to do another pass with these updated sizes. + emit->code_info_size = emit->code_info_offset; + emit->bytecode_size = emit->bytecode_offset; + return false; + } + + if (emit->overflow) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("bytecode overflow")); + } + + #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS + size_t bytecode_len = emit->code_info_size + emit->bytecode_size; + #if MICROPY_DEBUG_PRINTERS + emit->scope->raw_code_data_len = bytecode_len; + #endif + #endif + + // Bytecode is finalised, assign it to the raw code object. + mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base, + emit->emit_common->children, + #if MICROPY_PERSISTENT_CODE_SAVE + bytecode_len, + emit->emit_common->ct_cur_child, + #endif + emit->scope->scope_flags); + } + + return true; +} + +void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) { + if (emit->pass == MP_PASS_SCOPE) { + return; + } + assert((mp_int_t)emit->stack_size + delta >= 0); + emit->stack_size += delta; + if (emit->stack_size > emit->scope->stack_size) { + emit->scope->stack_size = emit->stack_size; + } +} + +void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { + #if MICROPY_ENABLE_SOURCE_LINE + if (MP_STATE_VM(mp_optimise_value) >= 3) { + // If we compile with -O3, don't store line numbers. + return; + } + if (source_line > emit->last_source_line) { + mp_uint_t bytes_to_skip = emit->bytecode_offset - emit->last_source_line_offset; + mp_uint_t lines_to_skip = source_line - emit->last_source_line; + emit_write_code_info_bytes_lines(emit, bytes_to_skip, lines_to_skip); + emit->last_source_line_offset = emit->bytecode_offset; + emit->last_source_line = source_line; + } + #else + (void)emit; + (void)source_line; + #endif +} + +void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { + // Assigning a label ends any dead-code region, and all following opcodes + // should be emitted (until another unconditional flow control). + emit->suppress = false; + + if (emit->pass == MP_PASS_SCOPE) { + return; + } + + // Label offsets can change from one pass to the next, but they must only + // decrease (ie code can only shrink). There will be multiple MP_PASS_EMIT + // stages until the labels no longer change, which is when the code size + // stays constant after a MP_PASS_EMIT. + assert(l < emit->max_num_labels); + assert(emit->pass == MP_PASS_STACK_SIZE || emit->bytecode_offset <= emit->label_offsets[l]); + + // Assign label offset. + emit->label_offsets[l] = emit->bytecode_offset; +} + +void mp_emit_bc_import(emit_t *emit, qstr qst, int kind) { + MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_NAME == MP_BC_IMPORT_NAME); + MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_FROM == MP_BC_IMPORT_FROM); + int stack_adj = kind == MP_EMIT_IMPORT_FROM ? 1 : -1; + if (kind == MP_EMIT_IMPORT_STAR) { + emit_write_bytecode_byte(emit, stack_adj, MP_BC_IMPORT_STAR); + } else { + emit_write_bytecode_byte_qstr(emit, stack_adj, MP_BC_IMPORT_NAME + kind, qst); + } +} + +void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) { + MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_NONE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_NONE); + MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_TRUE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_TRUE); + if (tok == MP_TOKEN_ELLIPSIS) { + emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); + } else { + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_CONST_FALSE + (tok - MP_TOKEN_KW_FALSE)); + } +} + +void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg) { + assert(MP_SMALL_INT_FITS(arg)); + if (-MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS <= arg + && arg < MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS) { + emit_write_bytecode_byte(emit, 1, + MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS + arg); + } else { + emit_write_bytecode_byte_int(emit, 1, MP_BC_LOAD_CONST_SMALL_INT, arg); + } +} + +void mp_emit_bc_load_const_str(emit_t *emit, qstr qst) { + emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_CONST_STRING, qst); +} + +void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj) { + emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, obj); +} + +void mp_emit_bc_load_null(emit_t *emit) { + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_NULL); +} + +void mp_emit_bc_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { + MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_LOAD_FAST_N); + MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_LOAD_DEREF); + (void)qst; + if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) { + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_FAST_MULTI + local_num); + } else { + emit_write_bytecode_byte_uint(emit, 1, MP_BC_LOAD_FAST_N + kind, local_num); + } +} + +void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind) { + MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_LOAD_NAME); + MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_LOAD_GLOBAL); + (void)qst; + emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_NAME + kind, qst); +} + +void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super) { + int stack_adj = 1 - 2 * is_super; + emit_write_bytecode_byte_qstr(emit, stack_adj, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst); +} + +void mp_emit_bc_load_build_class(emit_t *emit) { + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_BUILD_CLASS); +} + +void mp_emit_bc_subscr(emit_t *emit, int kind) { + if (kind == MP_EMIT_SUBSCR_LOAD) { + emit_write_bytecode_byte(emit, -1, MP_BC_LOAD_SUBSCR); + } else { + if (kind == MP_EMIT_SUBSCR_DELETE) { + mp_emit_bc_load_null(emit); + mp_emit_bc_rot_three(emit); + } + emit_write_bytecode_byte(emit, -3, MP_BC_STORE_SUBSCR); + } +} + +void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind) { + if (kind == MP_EMIT_ATTR_LOAD) { + emit_write_bytecode_byte_qstr(emit, 0, MP_BC_LOAD_ATTR, qst); + } else { + if (kind == MP_EMIT_ATTR_DELETE) { + mp_emit_bc_load_null(emit); + mp_emit_bc_rot_two(emit); + } + emit_write_bytecode_byte_qstr(emit, -2, MP_BC_STORE_ATTR, qst); + } +} + +void mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { + MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_STORE_FAST_N); + MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_STORE_DEREF); + (void)qst; + if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) { + emit_write_bytecode_byte(emit, -1, MP_BC_STORE_FAST_MULTI + local_num); + } else { + emit_write_bytecode_byte_uint(emit, -1, MP_BC_STORE_FAST_N + kind, local_num); + } +} + +void mp_emit_bc_store_global(emit_t *emit, qstr qst, int kind) { + MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_STORE_NAME); + MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_STORE_GLOBAL); + emit_write_bytecode_byte_qstr(emit, -1, MP_BC_STORE_NAME + kind, qst); +} + +void mp_emit_bc_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { + MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_DELETE_FAST); + MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_DELETE_DEREF); + (void)qst; + emit_write_bytecode_byte_uint(emit, 0, MP_BC_DELETE_FAST + kind, local_num); +} + +void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind) { + MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_DELETE_NAME); + MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_DELETE_GLOBAL); + emit_write_bytecode_byte_qstr(emit, 0, MP_BC_DELETE_NAME + kind, qst); +} + +void mp_emit_bc_dup_top(emit_t *emit) { + emit_write_bytecode_byte(emit, 1, MP_BC_DUP_TOP); +} + +void mp_emit_bc_dup_top_two(emit_t *emit) { + emit_write_bytecode_byte(emit, 2, MP_BC_DUP_TOP_TWO); +} + +void mp_emit_bc_pop_top(emit_t *emit) { + emit_write_bytecode_byte(emit, -1, MP_BC_POP_TOP); +} + +void mp_emit_bc_rot_two(emit_t *emit) { + emit_write_bytecode_byte(emit, 0, MP_BC_ROT_TWO); +} + +void mp_emit_bc_rot_three(emit_t *emit) { + emit_write_bytecode_byte(emit, 0, MP_BC_ROT_THREE); +} + +void mp_emit_bc_jump(emit_t *emit, mp_uint_t label) { + emit_write_bytecode_byte_label(emit, 0, MP_BC_JUMP, label); + emit->suppress = true; +} + +void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) { + if (cond) { + emit_write_bytecode_byte_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label); + } else { + emit_write_bytecode_byte_label(emit, -1, MP_BC_POP_JUMP_IF_FALSE, label); + } +} + +void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label) { + if (cond) { + emit_write_bytecode_byte_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label); + } else { + emit_write_bytecode_byte_label(emit, -1, MP_BC_JUMP_IF_FALSE_OR_POP, label); + } +} + +void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth) { + if (except_depth == 0) { + if (label & MP_EMIT_BREAK_FROM_FOR) { + // need to pop the iterator if we are breaking out of a for loop + emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP); + // also pop the iter_buf + for (size_t i = 0; i < MP_OBJ_ITER_BUF_NSLOTS - 1; ++i) { + emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP); + } + } + emit_write_bytecode_byte_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + } else { + emit_write_bytecode_byte_label(emit, 0, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + emit_write_bytecode_raw_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth); + } + emit->suppress = true; +} + +void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) { + MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_WITH == MP_BC_SETUP_WITH); + MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_EXCEPT == MP_BC_SETUP_EXCEPT); + MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_FINALLY == MP_BC_SETUP_FINALLY); + // The SETUP_WITH opcode pops ctx_mgr from the top of the stack + // and then pushes 3 entries: __exit__, ctx_mgr, as_value. + int stack_adj = kind == MP_EMIT_SETUP_BLOCK_WITH ? 2 : 0; + emit_write_bytecode_byte_label(emit, stack_adj, MP_BC_SETUP_WITH + kind, label); +} + +void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) { + mp_emit_bc_load_const_tok(emit, MP_TOKEN_KW_NONE); + mp_emit_bc_label_assign(emit, label); + // The +2 is to ensure we have enough stack space to call the __exit__ method + emit_write_bytecode_byte(emit, 2, MP_BC_WITH_CLEANUP); + // Cancel the +2 above, plus the +2 from mp_emit_bc_setup_block(MP_EMIT_SETUP_BLOCK_WITH) + mp_emit_bc_adjust_stack_size(emit, -4); +} + +void mp_emit_bc_end_finally(emit_t *emit) { + emit_write_bytecode_byte(emit, -1, MP_BC_END_FINALLY); +} + +void mp_emit_bc_get_iter(emit_t *emit, bool use_stack) { + int stack_adj = use_stack ? MP_OBJ_ITER_BUF_NSLOTS - 1 : 0; + emit_write_bytecode_byte(emit, stack_adj, use_stack ? MP_BC_GET_ITER_STACK : MP_BC_GET_ITER); +} + +void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label) { + emit_write_bytecode_byte_label(emit, 1, MP_BC_FOR_ITER, label); +} + +void mp_emit_bc_for_iter_end(emit_t *emit) { + mp_emit_bc_adjust_stack_size(emit, -MP_OBJ_ITER_BUF_NSLOTS); +} + +void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { + (void)within_exc_handler; + emit_write_bytecode_byte_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label); + emit->suppress = true; +} + +void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) { + emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + op); +} + +void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op) { + bool invert = false; + if (op == MP_BINARY_OP_NOT_IN) { + invert = true; + op = MP_BINARY_OP_IN; + } else if (op == MP_BINARY_OP_IS_NOT) { + invert = true; + op = MP_BINARY_OP_IS; + } + emit_write_bytecode_byte(emit, -1, MP_BC_BINARY_OP_MULTI + op); + if (invert) { + emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NOT); + } +} + +void mp_emit_bc_build(emit_t *emit, mp_uint_t n_args, int kind) { + MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_TUPLE == MP_BC_BUILD_TUPLE); + MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_LIST == MP_BC_BUILD_LIST); + MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_MAP == MP_BC_BUILD_MAP); + MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SET == MP_BC_BUILD_SET); + MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SLICE == MP_BC_BUILD_SLICE); + int stack_adj = kind == MP_EMIT_BUILD_MAP ? 1 : 1 - n_args; + emit_write_bytecode_byte_uint(emit, stack_adj, MP_BC_BUILD_TUPLE + kind, n_args); +} + +void mp_emit_bc_store_map(emit_t *emit) { + emit_write_bytecode_byte(emit, -2, MP_BC_STORE_MAP); +} + +void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection_stack_index) { + int t; + int n; + if (kind == SCOPE_LIST_COMP) { + n = 0; + t = 0; + } else if (!MICROPY_PY_BUILTINS_SET || kind == SCOPE_DICT_COMP) { + n = 1; + t = 1; + } else if (MICROPY_PY_BUILTINS_SET) { + n = 0; + t = 2; + } + // the lower 2 bits of the opcode argument indicate the collection type + emit_write_bytecode_byte_uint(emit, -1 - n, MP_BC_STORE_COMP, ((collection_stack_index + n) << 2) | t); +} + +void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args) { + emit_write_bytecode_byte_uint(emit, -1 + n_args, MP_BC_UNPACK_SEQUENCE, n_args); +} + +void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right) { + emit_write_bytecode_byte_uint(emit, -1 + n_left + n_right + 1, MP_BC_UNPACK_EX, n_left | (n_right << 8)); +} + +void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { + if (n_pos_defaults == 0 && n_kw_defaults == 0) { + emit_write_bytecode_byte_child(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code); + } else { + emit_write_bytecode_byte_child(emit, -1, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code); + } +} + +void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { + if (n_pos_defaults == 0 && n_kw_defaults == 0) { + int stack_adj = -n_closed_over + 1; + emit_write_bytecode_byte_child(emit, stack_adj, MP_BC_MAKE_CLOSURE, scope->raw_code); + emit_write_bytecode_raw_byte(emit, n_closed_over); + } else { + assert(n_closed_over <= 255); + int stack_adj = -2 - (mp_int_t)n_closed_over + 1; + emit_write_bytecode_byte_child(emit, stack_adj, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code); + emit_write_bytecode_raw_byte(emit, n_closed_over); + } +} + +static void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { + if (star_flags) { + // each positional arg is one object, each kwarg is two objects, the key + // and the value and one extra object for the star args bitmap. + stack_adj -= (int)n_positional + 2 * (int)n_keyword + 1; + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + } else { + stack_adj -= (int)n_positional + 2 * (int)n_keyword; + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + } +} + +void mp_emit_bc_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { + emit_bc_call_function_method_helper(emit, 0, MP_BC_CALL_FUNCTION, n_positional, n_keyword, star_flags); +} + +void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { + emit_bc_call_function_method_helper(emit, -1, MP_BC_CALL_METHOD, n_positional, n_keyword, star_flags); +} + +void mp_emit_bc_return_value(emit_t *emit) { + emit_write_bytecode_byte(emit, -1, MP_BC_RETURN_VALUE); + emit->suppress = true; +} + +void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args) { + MP_STATIC_ASSERT(MP_BC_RAISE_LAST + 1 == MP_BC_RAISE_OBJ); + MP_STATIC_ASSERT(MP_BC_RAISE_LAST + 2 == MP_BC_RAISE_FROM); + assert(n_args <= 2); + emit_write_bytecode_byte(emit, -n_args, MP_BC_RAISE_LAST + n_args); + emit->suppress = true; +} + +void mp_emit_bc_yield(emit_t *emit, int kind) { + MP_STATIC_ASSERT(MP_BC_YIELD_VALUE + 1 == MP_BC_YIELD_FROM); + emit_write_bytecode_byte(emit, -kind, MP_BC_YIELD_VALUE + kind); + emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR; +} + +void mp_emit_bc_start_except_handler(emit_t *emit) { + mp_emit_bc_adjust_stack_size(emit, 4); // stack adjust for the exception instance, +3 for possible UNWIND_JUMP state +} + +void mp_emit_bc_end_except_handler(emit_t *emit) { + mp_emit_bc_adjust_stack_size(emit, -3); // stack adjust +} + +#if MICROPY_EMIT_NATIVE +const emit_method_table_t emit_bc_method_table = { + #if MICROPY_DYNAMIC_COMPILER + NULL, + NULL, + #endif + + mp_emit_bc_start_pass, + mp_emit_bc_end_pass, + mp_emit_bc_adjust_stack_size, + mp_emit_bc_set_source_line, + + { + mp_emit_bc_load_local, + mp_emit_bc_load_global, + }, + { + mp_emit_bc_store_local, + mp_emit_bc_store_global, + }, + { + mp_emit_bc_delete_local, + mp_emit_bc_delete_global, + }, + + mp_emit_bc_label_assign, + mp_emit_bc_import, + mp_emit_bc_load_const_tok, + mp_emit_bc_load_const_small_int, + mp_emit_bc_load_const_str, + mp_emit_bc_load_const_obj, + mp_emit_bc_load_null, + mp_emit_bc_load_method, + mp_emit_bc_load_build_class, + mp_emit_bc_subscr, + mp_emit_bc_attr, + mp_emit_bc_dup_top, + mp_emit_bc_dup_top_two, + mp_emit_bc_pop_top, + mp_emit_bc_rot_two, + mp_emit_bc_rot_three, + mp_emit_bc_jump, + mp_emit_bc_pop_jump_if, + mp_emit_bc_jump_if_or_pop, + mp_emit_bc_unwind_jump, + mp_emit_bc_setup_block, + mp_emit_bc_with_cleanup, + mp_emit_bc_end_finally, + mp_emit_bc_get_iter, + mp_emit_bc_for_iter, + mp_emit_bc_for_iter_end, + mp_emit_bc_pop_except_jump, + mp_emit_bc_unary_op, + mp_emit_bc_binary_op, + mp_emit_bc_build, + mp_emit_bc_store_map, + mp_emit_bc_store_comp, + mp_emit_bc_unpack_sequence, + mp_emit_bc_unpack_ex, + mp_emit_bc_make_function, + mp_emit_bc_make_closure, + mp_emit_bc_call_function, + mp_emit_bc_call_method, + mp_emit_bc_return_value, + mp_emit_bc_raise_varargs, + mp_emit_bc_yield, + + mp_emit_bc_start_except_handler, + mp_emit_bc_end_except_handler, +}; +#else +const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops = { + mp_emit_bc_load_local, + mp_emit_bc_load_global, +}; + +const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops = { + mp_emit_bc_store_local, + mp_emit_bc_store_global, +}; + +const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops = { + mp_emit_bc_delete_local, + mp_emit_bc_delete_global, +}; +#endif + +#endif // MICROPY_ENABLE_COMPILER diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitcommon.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitcommon.c new file mode 100644 index 00000000..a9eb6e20 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitcommon.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/emit.h" +#include "py/nativeglue.h" + +#if MICROPY_ENABLE_COMPILER + +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE +qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst) { + mp_map_elem_t *elem = mp_map_lookup(&emit->qstr_map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem->value == MP_OBJ_NULL) { + elem->value = MP_OBJ_NEW_SMALL_INT(emit->qstr_map.used - 1); + } + return MP_OBJ_SMALL_INT_VALUE(elem->value); +} +#endif + +// Compare two objects for strict equality, including equality of type. This is +// different to the semantics of mp_obj_equal which, eg, has (True,) == (1.0,). +static bool strictly_equal(mp_obj_t a, mp_obj_t b) { + if (a == b) { + return true; + } + + #if MICROPY_EMIT_NATIVE + if (a == MP_OBJ_FROM_PTR(&mp_fun_table) || b == MP_OBJ_FROM_PTR(&mp_fun_table)) { + return false; + } + #endif + + const mp_obj_type_t *a_type = mp_obj_get_type(a); + const mp_obj_type_t *b_type = mp_obj_get_type(b); + if (a_type != b_type) { + return false; + } + if (a_type == &mp_type_tuple) { + mp_obj_tuple_t *a_tuple = MP_OBJ_TO_PTR(a); + mp_obj_tuple_t *b_tuple = MP_OBJ_TO_PTR(b); + if (a_tuple->len != b_tuple->len) { + return false; + } + for (size_t i = 0; i < a_tuple->len; ++i) { + if (!strictly_equal(a_tuple->items[i], b_tuple->items[i])) { + return false; + } + } + return true; + } else { + return mp_obj_equal(a, b); + } +} + +size_t mp_emit_common_use_const_obj(mp_emit_common_t *emit, mp_obj_t const_obj) { + for (size_t i = 0; i < emit->const_obj_list.len; ++i) { + if (strictly_equal(emit->const_obj_list.items[i], const_obj)) { + return i; + } + } + mp_obj_list_append(MP_OBJ_FROM_PTR(&emit->const_obj_list), const_obj); + return emit->const_obj_list.len - 1; +} + +id_info_t *mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst) { + // name adding/lookup + id_info_t *id = scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT); + if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { + if (SCOPE_IS_FUNC_LIKE(scope->kind)) { + // rebind as a local variable + id->kind = ID_INFO_KIND_LOCAL; + } else { + // mark this as assigned, to prevent it from being closed over + id->kind = ID_INFO_KIND_GLOBAL_IMPLICIT_ASSIGNED; + } + } + return id; +} + +void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst) { + // assumes pass is greater than 1, ie that all identifiers are defined in the scope + + id_info_t *id = scope_find(scope, qst); + assert(id != NULL); + + // call the emit backend with the correct code + if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT || id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT_ASSIGNED) { + emit_method_table->global(emit, qst, MP_EMIT_IDOP_GLOBAL_NAME); + } else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) { + emit_method_table->global(emit, qst, MP_EMIT_IDOP_GLOBAL_GLOBAL); + } else if (id->kind == ID_INFO_KIND_LOCAL) { + emit_method_table->local(emit, qst, id->local_num, MP_EMIT_IDOP_LOCAL_FAST); + } else { + assert(id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE); + emit_method_table->local(emit, qst, id->local_num, MP_EMIT_IDOP_LOCAL_DEREF); + } +} + +#endif // MICROPY_ENABLE_COMPILER diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitglue.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitglue.c new file mode 100644 index 00000000..6b6d5ccb --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitglue.c @@ -0,0 +1,250 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ + +// This code glues the code emitters to the runtime. + +#include +#include +#include +#include + +#include "py/emitglue.h" +#include "py/runtime0.h" +#include "py/bc.h" +#include "py/objfun.h" +#include "py/profile.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#define WRITE_CODE (1) +#define DEBUG_printf DEBUG_printf +#define DEBUG_OP_printf(...) DEBUG_printf(__VA_ARGS__) +#else // don't print debugging info +#define DEBUG_printf(...) (void)0 +#define DEBUG_OP_printf(...) (void)0 +#endif + +#if MICROPY_DEBUG_PRINTERS +mp_uint_t mp_verbose_flag = 0; +#endif + +mp_raw_code_t *mp_emit_glue_new_raw_code(void) { + mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1); + rc->kind = MP_CODE_RESERVED; + #if MICROPY_PY_SYS_SETTRACE + rc->line_of_definition = 0; + #endif + return rc; +} + +void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, + mp_raw_code_t **children, + #if MICROPY_PERSISTENT_CODE_SAVE + size_t len, + uint16_t n_children, + #endif + uint16_t scope_flags) { + + rc->kind = MP_CODE_BYTECODE; + rc->is_generator = (scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0; + rc->fun_data = code; + rc->children = children; + + #if MICROPY_PERSISTENT_CODE_SAVE + rc->fun_data_len = len; + rc->n_children = n_children; + #endif + + #if MICROPY_PY_SYS_SETTRACE + mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_prof_extract_prelude(code, prelude); + #endif + + #if DEBUG_PRINT + #if !MICROPY_PERSISTENT_CODE_SAVE + const size_t len = 0; + #endif + DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags); + #endif +} + +#if MICROPY_EMIT_MACHINE_CODE +void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, const void *fun_data, mp_uint_t fun_len, + mp_raw_code_t **children, + #if MICROPY_PERSISTENT_CODE_SAVE + uint16_t n_children, + uint16_t prelude_offset, + #endif + uint16_t scope_flags, uint32_t asm_n_pos_args, uint32_t asm_type_sig + ) { + + assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM); + + // Some architectures require flushing/invalidation of the I/D caches, + // so that the generated native code which was created in data RAM will + // be available for execution from instruction RAM. + #if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB + #if __ICACHE_PRESENT == 1 + // Flush D-cache, so the code emitted is stored in RAM. + MP_HAL_CLEAN_DCACHE(fun_data, fun_len); + // Invalidate I-cache, so the newly-created code is reloaded from RAM. + SCB_InvalidateICache(); + #endif + #elif MICROPY_EMIT_ARM + #if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7 + __builtin___clear_cache((void *)fun_data, (uint8_t *)fun_data + fun_len); + #elif defined(__arm__) + // Flush I-cache and D-cache. + asm volatile ( + "0:" + "mrc p15, 0, r15, c7, c10, 3\n" // test and clean D-cache + "bne 0b\n" + "mov r0, #0\n" + "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache + : : : "r0", "cc"); + #endif + #endif + + rc->kind = kind; + rc->is_generator = (scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0; + rc->fun_data = fun_data; + + #if MICROPY_PERSISTENT_CODE_SAVE + rc->fun_data_len = fun_len; + #endif + rc->children = children; + + #if MICROPY_PERSISTENT_CODE_SAVE + rc->n_children = n_children; + rc->prelude_offset = prelude_offset; + #endif + + #if MICROPY_EMIT_INLINE_ASM + // These two entries are only needed for MP_CODE_NATIVE_ASM. + rc->asm_n_pos_args = asm_n_pos_args; + rc->asm_type_sig = asm_type_sig; + #endif + + #if DEBUG_PRINT + DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, (uint)scope_flags); + for (mp_uint_t i = 0; i < fun_len; i++) { + if (i > 0 && i % 16 == 0) { + DEBUG_printf("\n"); + } + DEBUG_printf(" %02x", ((const byte *)fun_data)[i]); + } + DEBUG_printf("\n"); + + #ifdef WRITE_CODE + FILE *fp_write_code = fopen("out-code", "wb"); + fwrite(fun_data, fun_len, 1, fp_write_code); + fclose(fp_write_code); + #endif + #else + (void)fun_len; + #endif +} +#endif + +mp_obj_t mp_make_function_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, const mp_obj_t *def_args) { + DEBUG_OP_printf("make_function_from_proto_fun %p\n", proto_fun); + assert(proto_fun != NULL); + + // def_args must be MP_OBJ_NULL or a tuple + assert(def_args == NULL || def_args[0] == MP_OBJ_NULL || mp_obj_is_type(def_args[0], &mp_type_tuple)); + + // def_kw_args must be MP_OBJ_NULL or a dict + assert(def_args == NULL || def_args[1] == MP_OBJ_NULL || mp_obj_is_type(def_args[1], &mp_type_dict)); + + #if MICROPY_MODULE_FROZEN_MPY + if (mp_proto_fun_is_bytecode(proto_fun)) { + const uint8_t *bc = proto_fun; + mp_obj_t fun = mp_obj_new_fun_bc(def_args, bc, context, NULL); + MP_BC_PRELUDE_SIG_DECODE(bc); + if (scope_flags & MP_SCOPE_FLAG_GENERATOR) { + ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap; + } + return fun; + } + #endif + + // the proto-function is a mp_raw_code_t + const mp_raw_code_t *rc = proto_fun; + + // make the function, depending on the raw code kind + mp_obj_t fun; + switch (rc->kind) { + #if MICROPY_EMIT_NATIVE + case MP_CODE_NATIVE_PY: + fun = mp_obj_new_fun_native(def_args, rc->fun_data, context, rc->children); + // Check for a generator function, and if so change the type of the object + if (rc->is_generator) { + ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap; + } + break; + case MP_CODE_NATIVE_VIPER: + fun = mp_obj_new_fun_viper(rc->fun_data, context, rc->children); + break; + #endif + #if MICROPY_EMIT_INLINE_ASM + case MP_CODE_NATIVE_ASM: + fun = mp_obj_new_fun_asm(rc->asm_n_pos_args, rc->fun_data, rc->asm_type_sig); + break; + #endif + default: + // rc->kind should always be set and BYTECODE is the only remaining case + assert(rc->kind == MP_CODE_BYTECODE); + fun = mp_obj_new_fun_bc(def_args, rc->fun_data, context, rc->children); + // check for generator functions and if so change the type of the object + if (rc->is_generator) { + ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap; + } + + #if MICROPY_PY_SYS_SETTRACE + mp_obj_fun_bc_t *self_fun = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(fun); + self_fun->rc = rc; + #endif + + break; + } + + return fun; +} + +mp_obj_t mp_make_closure_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args) { + DEBUG_OP_printf("make_closure_from_proto_fun %p " UINT_FMT " %p\n", proto_fun, n_closed_over, args); + // make function object + mp_obj_t ffun; + if (n_closed_over & 0x100) { + // default positional and keyword args given + ffun = mp_make_function_from_proto_fun(proto_fun, context, args); + } else { + // default positional and keyword args not given + ffun = mp_make_function_from_proto_fun(proto_fun, context, NULL); + } + // wrap function in closure object + return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2)); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitglue.h b/non_catalog_apps/mp_flipper/lib/micropython/py/emitglue.h new file mode 100644 index 00000000..12646267 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitglue.h @@ -0,0 +1,144 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_EMITGLUE_H +#define MICROPY_INCLUDED_PY_EMITGLUE_H + +#include "py/obj.h" +#include "py/bc.h" + +// These variables and functions glue the code emitters to the runtime. + +// Used with mp_raw_code_t::proto_fun_indicator to detect if a mp_proto_fun_t is a +// mp_raw_code_t struct or a direct pointer to bytecode. +#define MP_PROTO_FUN_INDICATOR_RAW_CODE_0 (0) +#define MP_PROTO_FUN_INDICATOR_RAW_CODE_1 (0) + +// These must fit in 8 bits; see scope.h +enum { + MP_EMIT_OPT_NONE, + MP_EMIT_OPT_BYTECODE, + MP_EMIT_OPT_NATIVE_PYTHON, + MP_EMIT_OPT_VIPER, + MP_EMIT_OPT_ASM, +}; + +typedef enum { + MP_CODE_UNUSED, + MP_CODE_RESERVED, + MP_CODE_BYTECODE, + MP_CODE_NATIVE_PY, + MP_CODE_NATIVE_VIPER, + MP_CODE_NATIVE_ASM, +} mp_raw_code_kind_t; + +// An mp_proto_fun_t points to static information about a non-instantiated function. +// A function object is created from this information, and that object can then be executed. +// It points either to bytecode, or an mp_raw_code_t struct. +typedef const void *mp_proto_fun_t; + +// Bytecode is distinguished from an mp_raw_code_t struct by the first two bytes: bytecode +// is guaranteed to have either its first or second byte non-zero. So if both bytes are +// zero then the mp_proto_fun_t pointer must be an mp_raw_code_t. +static inline bool mp_proto_fun_is_bytecode(mp_proto_fun_t proto_fun) { + const uint8_t *header = (const uint8_t *)proto_fun; + return (header[0] | (header[1] << 8)) != (MP_PROTO_FUN_INDICATOR_RAW_CODE_0 | (MP_PROTO_FUN_INDICATOR_RAW_CODE_1 << 8)); +} + +// The mp_raw_code_t struct appears in the following places: +// compiled bytecode: instance in RAM, referenced by outer scope, usually freed after first (and only) use +// mpy file: instance in RAM, created when .mpy file is loaded (same comments as above) +// frozen: instance in ROM +typedef struct _mp_raw_code_t { + uint8_t proto_fun_indicator[2]; + uint8_t kind; // of type mp_raw_code_kind_t; only 3 bits used + bool is_generator; + const void *fun_data; + struct _mp_raw_code_t **children; + #if MICROPY_PERSISTENT_CODE_SAVE + uint32_t fun_data_len; // for mp_raw_code_save + uint16_t n_children; + #if MICROPY_EMIT_MACHINE_CODE + uint16_t prelude_offset; + #endif + #if MICROPY_PY_SYS_SETTRACE + // line_of_definition is a Python source line where the raw_code was + // created e.g. MP_BC_MAKE_FUNCTION. This is different from lineno info + // stored in prelude, which provides line number for first statement of + // a function. Required to properly implement "call" trace event. + uint32_t line_of_definition; + mp_bytecode_prelude_t prelude; + #endif + #endif + #if MICROPY_EMIT_INLINE_ASM + uint32_t asm_n_pos_args : 8; + uint32_t asm_type_sig : 24; // compressed as 2-bit types; ret is MSB, then arg0, arg1, etc + #endif +} mp_raw_code_t; + +// Version of mp_raw_code_t but without the asm_n_pos_args/asm_type_sig entries, which are +// only needed when the kind is MP_CODE_NATIVE_ASM. So this struct can be used when the +// kind is MP_CODE_BYTECODE, MP_CODE_NATIVE_PY or MP_CODE_NATIVE_VIPER, to reduce its size. +typedef struct _mp_raw_code_truncated_t { + uint8_t proto_fun_indicator[2]; + uint8_t kind; + bool is_generator; + const void *fun_data; + struct _mp_raw_code_t **children; + #if MICROPY_PERSISTENT_CODE_SAVE + uint32_t fun_data_len; + uint16_t n_children; + #if MICROPY_EMIT_MACHINE_CODE + uint16_t prelude_offset; + #endif + #if MICROPY_PY_SYS_SETTRACE + uint32_t line_of_definition; + mp_bytecode_prelude_t prelude; + #endif + #endif +} mp_raw_code_truncated_t; + +mp_raw_code_t *mp_emit_glue_new_raw_code(void); + +void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, + mp_raw_code_t **children, + #if MICROPY_PERSISTENT_CODE_SAVE + size_t len, + uint16_t n_children, + #endif + uint16_t scope_flags); + +void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, const void *fun_data, mp_uint_t fun_len, + mp_raw_code_t **children, + #if MICROPY_PERSISTENT_CODE_SAVE + uint16_t n_children, + uint16_t prelude_offset, + #endif + uint16_t scope_flags, uint32_t asm_n_pos_args, uint32_t asm_type_sig); + +mp_obj_t mp_make_function_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, const mp_obj_t *def_args); +mp_obj_t mp_make_closure_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args); + +#endif // MICROPY_INCLUDED_PY_EMITGLUE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitinlinethumb.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitinlinethumb.c new file mode 100644 index 00000000..7818bb4f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitinlinethumb.c @@ -0,0 +1,865 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/emit.h" +#include "py/asmthumb.h" + +#if MICROPY_EMIT_INLINE_THUMB + +typedef enum { +// define rules with a compile function +#define DEF_RULE(rule, comp, kind, ...) PN_##rule, +#define DEF_RULE_NC(rule, kind, ...) + #include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + PN_const_object, // special node for a constant, generic Python object +// define rules without a compile function +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) PN_##rule, + #include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +} pn_kind_t; + +struct _emit_inline_asm_t { + asm_thumb_t as; + uint16_t pass; + mp_obj_t *error_slot; + mp_uint_t max_num_labels; + qstr *label_lookup; +}; + +#if MICROPY_DYNAMIC_COMPILER + +static inline bool emit_inline_thumb_allow_float(emit_inline_asm_t *emit) { + return MP_NATIVE_ARCH_ARMV7EMSP <= mp_dynamic_compiler.native_arch + && mp_dynamic_compiler.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP; +} + +#else + +static inline bool emit_inline_thumb_allow_float(emit_inline_asm_t *emit) { + return MICROPY_EMIT_INLINE_THUMB_FLOAT; +} + +#endif + +static void emit_inline_thumb_error_msg(emit_inline_asm_t *emit, mp_rom_error_text_t msg) { + *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); +} + +static void emit_inline_thumb_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) { + *emit->error_slot = exc; +} + +emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) { + emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); + memset(&emit->as, 0, sizeof(emit->as)); + mp_asm_base_init(&emit->as.base, max_num_labels); + emit->max_num_labels = max_num_labels; + emit->label_lookup = m_new(qstr, max_num_labels); + return emit; +} + +void emit_inline_thumb_free(emit_inline_asm_t *emit) { + m_del(qstr, emit->label_lookup, emit->max_num_labels); + mp_asm_base_deinit(&emit->as.base, false); + m_del_obj(emit_inline_asm_t, emit); +} + +static void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) { + emit->pass = pass; + emit->error_slot = error_slot; + if (emit->pass == MP_PASS_CODE_SIZE) { + memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); + } + mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); + asm_thumb_entry(&emit->as, 0); +} + +static void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { + asm_thumb_exit(&emit->as); + asm_thumb_end_pass(&emit->as); +} + +static mp_uint_t emit_inline_thumb_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) { + if (n_params > 4) { + emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("can only have up to 4 parameters to Thumb assembly")); + return 0; + } + for (mp_uint_t i = 0; i < n_params; i++) { + if (!MP_PARSE_NODE_IS_ID(pn_params[i])) { + emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3")); + return 0; + } + const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); + if (!(strlen(p) == 2 && p[0] == 'r' && (mp_uint_t)p[1] == '0' + i)) { + emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3")); + return 0; + } + } + return n_params; +} + +static bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) { + assert(label_num < emit->max_num_labels); + if (emit->pass == MP_PASS_CODE_SIZE) { + // check for duplicate label on first pass + for (uint i = 0; i < emit->max_num_labels; i++) { + if (emit->label_lookup[i] == label_id) { + return false; + } + } + } + emit->label_lookup[label_num] = label_id; + mp_asm_base_label_assign(&emit->as.base, label_num); + return true; +} + +typedef struct _reg_name_t { byte reg; + byte name[3]; +} reg_name_t; +static const reg_name_t reg_name_table[] = { + {0, "r0\0"}, + {1, "r1\0"}, + {2, "r2\0"}, + {3, "r3\0"}, + {4, "r4\0"}, + {5, "r5\0"}, + {6, "r6\0"}, + {7, "r7\0"}, + {8, "r8\0"}, + {9, "r9\0"}, + {10, "r10"}, + {11, "r11"}, + {12, "r12"}, + {13, "r13"}, + {14, "r14"}, + {15, "r15"}, + {10, "sl\0"}, + {11, "fp\0"}, + {13, "sp\0"}, + {14, "lr\0"}, + {15, "pc\0"}, +}; + +#define MAX_SPECIAL_REGISTER_NAME_LENGTH 7 +typedef struct _special_reg_name_t { byte reg; + char name[MAX_SPECIAL_REGISTER_NAME_LENGTH + 1]; +} special_reg_name_t; +static const special_reg_name_t special_reg_name_table[] = { + {5, "IPSR"}, + {17, "BASEPRI"}, +}; + +// return empty string in case of error, so we can attempt to parse the string +// without a special check if it was in fact a string +static const char *get_arg_str(mp_parse_node_t pn) { + if (MP_PARSE_NODE_IS_ID(pn)) { + qstr qst = MP_PARSE_NODE_LEAF_ARG(pn); + return qstr_str(qst); + } else { + return ""; + } +} + +static mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_uint_t max_reg) { + const char *reg_str = get_arg_str(pn); + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) { + const reg_name_t *r = ®_name_table[i]; + if (reg_str[0] == r->name[0] + && reg_str[1] == r->name[1] + && reg_str[2] == r->name[2] + && (reg_str[2] == '\0' || reg_str[3] == '\0')) { + if (r->reg > max_reg) { + emit_inline_thumb_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("'%s' expects at most r%d"), op, max_reg)); + return 0; + } else { + return r->reg; + } + } + } + emit_inline_thumb_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("'%s' expects a register"), op)); + return 0; +} + +static mp_uint_t get_arg_special_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + const char *reg_str = get_arg_str(pn); + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(special_reg_name_table); i++) { + const special_reg_name_t *r = &special_reg_name_table[i]; + if (strcmp(r->name, reg_str) == 0) { + return r->reg; + } + } + emit_inline_thumb_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("'%s' expects a special register"), op)); + return 0; +} + +static mp_uint_t get_arg_vfpreg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + const char *reg_str = get_arg_str(pn); + if (reg_str[0] == 's' && reg_str[1] != '\0') { + mp_uint_t regno = 0; + for (++reg_str; *reg_str; ++reg_str) { + mp_uint_t v = *reg_str; + if (!('0' <= v && v <= '9')) { + goto malformed; + } + regno = 10 * regno + v - '0'; + } + if (regno > 31) { + emit_inline_thumb_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("'%s' expects at most r%d"), op, 31)); + return 0; + } else { + return regno; + } + } +malformed: + emit_inline_thumb_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("'%s' expects an FPU register"), op)); + return 0; +} + +static mp_uint_t get_arg_reglist(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + // a register list looks like {r0, r1, r2} and is parsed as a Python set + + if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_brace)) { + goto bad_arg; + } + + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 1); // should always be + pn = pns->nodes[0]; + + mp_uint_t reglist = 0; + + if (MP_PARSE_NODE_IS_ID(pn)) { + // set with one element + reglist |= 1 << get_arg_reg(emit, op, pn, 15); + } else if (MP_PARSE_NODE_IS_STRUCT(pn)) { + pns = (mp_parse_node_struct_t *)pn; + if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) { + assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1]; + if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) { + // set with multiple elements + + // get first element of set (we rely on get_arg_reg to catch syntax errors) + reglist |= 1 << get_arg_reg(emit, op, pns->nodes[0], 15); + + // get tail elements (2nd, 3rd, ...) + mp_parse_node_t *nodes; + int n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes); + + // process rest of elements + for (int i = 0; i < n; i++) { + reglist |= 1 << get_arg_reg(emit, op, nodes[i], 15); + } + } else { + goto bad_arg; + } + } else { + goto bad_arg; + } + } else { + goto bad_arg; + } + + return reglist; + +bad_arg: + emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects {r0, r1, ...}"), op)); + return 0; +} + +static uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, uint32_t fit_mask) { + mp_obj_t o; + if (!mp_parse_node_get_int_maybe(pn, &o)) { + emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an integer"), op)); + return 0; + } + uint32_t i = mp_obj_get_int_truncated(o); + if ((i & (~fit_mask)) != 0) { + emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' integer 0x%x doesn't fit in mask 0x%x"), op, i, fit_mask)); + return 0; + } + return i; +} + +static bool get_arg_addr(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_parse_node_t *pn_base, mp_parse_node_t *pn_offset) { + if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) { + goto bad_arg; + } + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) { + goto bad_arg; + } + pns = (mp_parse_node_struct_t *)pns->nodes[0]; + if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) { + goto bad_arg; + } + + *pn_base = pns->nodes[0]; + *pn_offset = pns->nodes[1]; + return true; + +bad_arg: + emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an address of the form [a, b]"), op)); + return false; +} + +static int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + if (!MP_PARSE_NODE_IS_ID(pn)) { + emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects a label"), op)); + return 0; + } + qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn); + for (uint i = 0; i < emit->max_num_labels; i++) { + if (emit->label_lookup[i] == label_qstr) { + return i; + } + } + // only need to have the labels on the last pass + if (emit->pass == MP_PASS_EMIT) { + emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("label '%q' not defined"), label_qstr)); + } + return 0; +} + +typedef struct _cc_name_t { byte cc; + byte name[2]; +} cc_name_t; +static const cc_name_t cc_name_table[] = { + { ASM_THUMB_CC_EQ, "eq" }, + { ASM_THUMB_CC_NE, "ne" }, + { ASM_THUMB_CC_CS, "cs" }, + { ASM_THUMB_CC_CC, "cc" }, + { ASM_THUMB_CC_MI, "mi" }, + { ASM_THUMB_CC_PL, "pl" }, + { ASM_THUMB_CC_VS, "vs" }, + { ASM_THUMB_CC_VC, "vc" }, + { ASM_THUMB_CC_HI, "hi" }, + { ASM_THUMB_CC_LS, "ls" }, + { ASM_THUMB_CC_GE, "ge" }, + { ASM_THUMB_CC_LT, "lt" }, + { ASM_THUMB_CC_GT, "gt" }, + { ASM_THUMB_CC_LE, "le" }, +}; + +typedef struct _format_4_op_t { byte op; + char name[3]; +} format_4_op_t; +#define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops +static const format_4_op_t format_4_op_table[] = { + { X(ASM_THUMB_FORMAT_4_EOR), "eor" }, + { X(ASM_THUMB_FORMAT_4_LSL), "lsl" }, + { X(ASM_THUMB_FORMAT_4_LSR), "lsr" }, + { X(ASM_THUMB_FORMAT_4_ASR), "asr" }, + { X(ASM_THUMB_FORMAT_4_ADC), "adc" }, + { X(ASM_THUMB_FORMAT_4_SBC), "sbc" }, + { X(ASM_THUMB_FORMAT_4_ROR), "ror" }, + { X(ASM_THUMB_FORMAT_4_TST), "tst" }, + { X(ASM_THUMB_FORMAT_4_NEG), "neg" }, + { X(ASM_THUMB_FORMAT_4_CMP), "cmp" }, + { X(ASM_THUMB_FORMAT_4_CMN), "cmn" }, + { X(ASM_THUMB_FORMAT_4_ORR), "orr" }, + { X(ASM_THUMB_FORMAT_4_MUL), "mul" }, + { X(ASM_THUMB_FORMAT_4_BIC), "bic" }, + { X(ASM_THUMB_FORMAT_4_MVN), "mvn" }, +}; +#undef X + +// name is actually a qstr, which should fit in 16 bits +typedef struct _format_9_10_op_t { uint16_t op; + uint16_t name; +} format_9_10_op_t; +#define X(x) (x) +static const format_9_10_op_t format_9_10_op_table[] = { + { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_ldr }, + { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_ldrb }, + { X(ASM_THUMB_FORMAT_10_LDRH), MP_QSTR_ldrh }, + { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_str }, + { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_strb }, + { X(ASM_THUMB_FORMAT_10_STRH), MP_QSTR_strh }, +}; +#undef X + +// actual opcodes are: 0xee00 | op.hi_nibble, 0x0a00 | op.lo_nibble +typedef struct _format_vfp_op_t { + byte op; + char name[3]; +} format_vfp_op_t; +static const format_vfp_op_t format_vfp_op_table[] = { + { 0x30, "add" }, + { 0x34, "sub" }, + { 0x20, "mul" }, + { 0x80, "div" }, +}; + +// shorthand alias for whether we allow ARMv7-M instructions +#define ARMV7M asm_thumb_allow_armv7m(&emit->as) + +static void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) { + // TODO perhaps make two tables: + // one_args = + // "b", LAB, asm_thumb_b_n, + // "bgt", LAB, asm_thumb_bgt_n, + // two_args = + // "movs", RLO, I8, asm_thumb_movs_reg_i8 + // "movw", REG, REG, asm_thumb_movw_reg_i16 + // three_args = + // "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3 + + size_t op_len; + const char *op_str = (const char *)qstr_data(op, &op_len); + + if (emit_inline_thumb_allow_float(emit) && op_str[0] == 'v') { + // floating point operations + if (n_args == 2) { + mp_uint_t op_code = 0x0ac0, op_code_hi; + if (op == MP_QSTR_vcmp) { + op_code_hi = 0xeeb4; + op_vfp_twoargs:; + mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]); + mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[1]); + asm_thumb_op32(&emit->as, + op_code_hi | ((vd & 1) << 6), + op_code | ((vd & 0x1e) << 11) | ((vm & 1) << 5) | (vm & 0x1e) >> 1); + } else if (op == MP_QSTR_vsqrt) { + op_code_hi = 0xeeb1; + goto op_vfp_twoargs; + } else if (op == MP_QSTR_vneg) { + op_code_hi = 0xeeb1; + op_code = 0x0a40; + goto op_vfp_twoargs; + } else if (op == MP_QSTR_vcvt_f32_s32) { + op_code_hi = 0xeeb8; // int to float + goto op_vfp_twoargs; + } else if (op == MP_QSTR_vcvt_s32_f32) { + op_code_hi = 0xeebd; // float to int + goto op_vfp_twoargs; + } else if (op == MP_QSTR_vmrs) { + mp_uint_t reg_dest; + const char *reg_str0 = get_arg_str(pn_args[0]); + if (strcmp(reg_str0, "APSR_nzcv") == 0) { + reg_dest = 15; + } else { + reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); + } + const char *reg_str1 = get_arg_str(pn_args[1]); + if (strcmp(reg_str1, "FPSCR") == 0) { + // FP status to ARM reg + asm_thumb_op32(&emit->as, 0xeef1, 0x0a10 | (reg_dest << 12)); + } else { + goto unknown_op; + } + } else if (op == MP_QSTR_vmov) { + op_code_hi = 0xee00; + mp_uint_t r_arm, vm; + const char *reg_str = get_arg_str(pn_args[0]); + if (reg_str[0] == 'r') { + r_arm = get_arg_reg(emit, op_str, pn_args[0], 15); + vm = get_arg_vfpreg(emit, op_str, pn_args[1]); + op_code_hi |= 0x10; + } else { + vm = get_arg_vfpreg(emit, op_str, pn_args[0]); + r_arm = get_arg_reg(emit, op_str, pn_args[1], 15); + } + asm_thumb_op32(&emit->as, + op_code_hi | ((vm & 0x1e) >> 1), + 0x0a10 | (r_arm << 12) | ((vm & 1) << 7)); + } else if (op == MP_QSTR_vldr) { + op_code_hi = 0xed90; + op_vldr_vstr:; + mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]); + mp_parse_node_t pn_base, pn_offset; + if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) { + mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7); + mp_uint_t i8; + i8 = get_arg_i(emit, op_str, pn_offset, 0x3fc) >> 2; + asm_thumb_op32(&emit->as, + op_code_hi | rlo_base | ((vd & 1) << 6), + 0x0a00 | ((vd & 0x1e) << 11) | i8); + } + } else if (op == MP_QSTR_vstr) { + op_code_hi = 0xed80; + goto op_vldr_vstr; + } else { + goto unknown_op; + } + } else if (n_args == 3) { + // search table for arith ops + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_vfp_op_table); i++) { + if (strncmp(op_str + 1, format_vfp_op_table[i].name, 3) == 0 && op_str[4] == '\0') { + mp_uint_t op_code_hi = 0xee00 | (format_vfp_op_table[i].op & 0xf0); + mp_uint_t op_code = 0x0a00 | ((format_vfp_op_table[i].op & 0x0f) << 4); + mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]); + mp_uint_t vn = get_arg_vfpreg(emit, op_str, pn_args[1]); + mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[2]); + asm_thumb_op32(&emit->as, + op_code_hi | ((vd & 1) << 6) | (vn >> 1), + op_code | (vm >> 1) | ((vm & 1) << 5) | ((vd & 0x1e) << 11) | ((vn & 1) << 7)); + return; + } + } + goto unknown_op; + } else { + goto unknown_op; + } + return; + } + + if (n_args == 0) { + if (op == MP_QSTR_nop) { + asm_thumb_op16(&emit->as, ASM_THUMB_OP_NOP); + } else if (op == MP_QSTR_wfi) { + asm_thumb_op16(&emit->as, ASM_THUMB_OP_WFI); + } else { + goto unknown_op; + } + + } else if (n_args == 1) { + if (op == MP_QSTR_b) { + int label_num = get_arg_label(emit, op_str, pn_args[0]); + if (!asm_thumb_b_n_label(&emit->as, label_num)) { + goto branch_not_in_range; + } + } else if (op == MP_QSTR_bl) { + int label_num = get_arg_label(emit, op_str, pn_args[0]); + if (!asm_thumb_bl_label(&emit->as, label_num)) { + goto branch_not_in_range; + } + } else if (op == MP_QSTR_bx) { + mp_uint_t r = get_arg_reg(emit, op_str, pn_args[0], 15); + asm_thumb_op16(&emit->as, 0x4700 | (r << 3)); + } else if (op_str[0] == 'b' && (op_len == 3 + || (op_len == 5 && op_str[3] == '_' + && (op_str[4] == 'n' || (ARMV7M && op_str[4] == 'w'))))) { + mp_uint_t cc = -1; + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) { + if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) { + cc = cc_name_table[i].cc; + } + } + if (cc == (mp_uint_t)-1) { + goto unknown_op; + } + int label_num = get_arg_label(emit, op_str, pn_args[0]); + bool wide = op_len == 5 && op_str[4] == 'w'; + if (wide && !ARMV7M) { + goto unknown_op; + } + if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, wide)) { + goto branch_not_in_range; + } + } else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') { + const char *arg_str = get_arg_str(pn_args[0]); + mp_uint_t cc = -1; + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) { + if (arg_str[0] == cc_name_table[i].name[0] + && arg_str[1] == cc_name_table[i].name[1] + && arg_str[2] == '\0') { + cc = cc_name_table[i].cc; + break; + } + } + if (cc == (mp_uint_t)-1) { + goto unknown_op; + } + const char *os = op_str + 2; + while (*os != '\0') { + os++; + } + if (os > op_str + 5) { + goto unknown_op; + } + mp_uint_t it_mask = 8; + while (--os >= op_str + 2) { + it_mask >>= 1; + if (*os == 't') { + it_mask |= (cc & 1) << 3; + } else if (*os == 'e') { + it_mask |= ((~cc) & 1) << 3; + } else { + goto unknown_op; + } + } + asm_thumb_it_cc(&emit->as, cc, it_mask); + } else if (op == MP_QSTR_cpsid) { + // TODO check pn_args[0] == i + asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSID_I); + } else if (op == MP_QSTR_cpsie) { + // TODO check pn_args[0] == i + asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSIE_I); + } else if (op == MP_QSTR_push) { + mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]); + if ((reglist & 0xbf00) == 0) { + if ((reglist & (1 << 14)) == 0) { + asm_thumb_op16(&emit->as, 0xb400 | reglist); + } else { + // 16-bit encoding for pushing low registers and LR + asm_thumb_op16(&emit->as, 0xb500 | (reglist & 0xff)); + } + } else { + if (!ARMV7M) { + goto unknown_op; + } + asm_thumb_op32(&emit->as, 0xe92d, reglist); + } + } else if (op == MP_QSTR_pop) { + mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]); + if ((reglist & 0x7f00) == 0) { + if ((reglist & (1 << 15)) == 0) { + asm_thumb_op16(&emit->as, 0xbc00 | reglist); + } else { + // 16-bit encoding for popping low registers and PC, i.e., returning + asm_thumb_op16(&emit->as, 0xbd00 | (reglist & 0xff)); + } + } else { + if (!ARMV7M) { + goto unknown_op; + } + asm_thumb_op32(&emit->as, 0xe8bd, reglist); + } + } else { + goto unknown_op; + } + + } else if (n_args == 2) { + if (MP_PARSE_NODE_IS_ID(pn_args[1])) { + // second arg is a register (or should be) + mp_uint_t op_code, op_code_hi; + if (op == MP_QSTR_mov) { + mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); + mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15); + asm_thumb_mov_reg_reg(&emit->as, reg_dest, reg_src); + } else if (ARMV7M && op == MP_QSTR_clz) { + op_code_hi = 0xfab0; + op_code = 0xf080; + mp_uint_t rd, rm; + op_clz_rbit: + rd = get_arg_reg(emit, op_str, pn_args[0], 15); + rm = get_arg_reg(emit, op_str, pn_args[1], 15); + asm_thumb_op32(&emit->as, op_code_hi | rm, op_code | (rd << 8) | rm); + } else if (ARMV7M && op == MP_QSTR_rbit) { + op_code_hi = 0xfa90; + op_code = 0xf0a0; + goto op_clz_rbit; + } else if (ARMV7M && op == MP_QSTR_mrs) { + mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 12); + mp_uint_t reg_src = get_arg_special_reg(emit, op_str, pn_args[1]); + asm_thumb_op32(&emit->as, 0xf3ef, 0x8000 | (reg_dest << 8) | reg_src); + } else { + if (op == MP_QSTR_and_) { + op_code = ASM_THUMB_FORMAT_4_AND; + mp_uint_t reg_dest, reg_src; + op_format_4: + reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7); + reg_src = get_arg_reg(emit, op_str, pn_args[1], 7); + asm_thumb_format_4(&emit->as, op_code, reg_dest, reg_src); + return; + } + // search table for ALU ops + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_4_op_table); i++) { + if (strncmp(op_str, format_4_op_table[i].name, 3) == 0 && op_str[3] == '\0') { + op_code = 0x4000 | (format_4_op_table[i].op << 4); + goto op_format_4; + } + } + goto unknown_op; + } + } else { + // second arg is not a register + mp_uint_t op_code; + if (op == MP_QSTR_mov) { + op_code = ASM_THUMB_FORMAT_3_MOV; + mp_uint_t rlo_dest, i8_src; + op_format_3: + rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7); + i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff); + asm_thumb_format_3(&emit->as, op_code, rlo_dest, i8_src); + } else if (op == MP_QSTR_cmp) { + op_code = ASM_THUMB_FORMAT_3_CMP; + goto op_format_3; + } else if (op == MP_QSTR_add) { + op_code = ASM_THUMB_FORMAT_3_ADD; + goto op_format_3; + } else if (op == MP_QSTR_sub) { + op_code = ASM_THUMB_FORMAT_3_SUB; + goto op_format_3; + } else if (ARMV7M && op == MP_QSTR_movw) { + op_code = ASM_THUMB_OP_MOVW; + mp_uint_t reg_dest; + op_movw_movt: + reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); + int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff); + asm_thumb_mov_reg_i16(&emit->as, op_code, reg_dest, i_src); + } else if (ARMV7M && op == MP_QSTR_movt) { + op_code = ASM_THUMB_OP_MOVT; + goto op_movw_movt; + } else if (ARMV7M && op == MP_QSTR_movwt) { + // this is a convenience instruction + mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); + uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff); + asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff); + asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff); + } else if (ARMV7M && op == MP_QSTR_ldrex) { + mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15); + mp_parse_node_t pn_base, pn_offset; + if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) { + mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15); + mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2; + asm_thumb_op32(&emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8); + } + } else { + // search table for ldr/str instructions + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) { + if (op == format_9_10_op_table[i].name) { + op_code = format_9_10_op_table[i].op; + mp_parse_node_t pn_base, pn_offset; + mp_uint_t rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7); + if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) { + mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7); + mp_uint_t i5; + if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) { + i5 = get_arg_i(emit, op_str, pn_offset, 0x1f); + } else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH + i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1; + } else { + i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2; + } + asm_thumb_format_9_10(&emit->as, op_code, rlo_dest, rlo_base, i5); + return; + } + break; + } + } + goto unknown_op; + } + } + + } else if (n_args == 3) { + mp_uint_t op_code; + if (op == MP_QSTR_lsl) { + op_code = ASM_THUMB_FORMAT_1_LSL; + mp_uint_t rlo_dest, rlo_src, i5; + op_format_1: + rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7); + rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7); + i5 = get_arg_i(emit, op_str, pn_args[2], 0x1f); + asm_thumb_format_1(&emit->as, op_code, rlo_dest, rlo_src, i5); + } else if (op == MP_QSTR_lsr) { + op_code = ASM_THUMB_FORMAT_1_LSR; + goto op_format_1; + } else if (op == MP_QSTR_asr) { + op_code = ASM_THUMB_FORMAT_1_ASR; + goto op_format_1; + } else if (op == MP_QSTR_add) { + op_code = ASM_THUMB_FORMAT_2_ADD; + mp_uint_t rlo_dest, rlo_src; + op_format_2: + rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7); + rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7); + int src_b; + if (MP_PARSE_NODE_IS_ID(pn_args[2])) { + op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND; + src_b = get_arg_reg(emit, op_str, pn_args[2], 7); + } else { + op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND; + src_b = get_arg_i(emit, op_str, pn_args[2], 0x7); + } + asm_thumb_format_2(&emit->as, op_code, rlo_dest, rlo_src, src_b); + } else if (ARMV7M && op == MP_QSTR_sdiv) { + op_code = 0xfb90; // sdiv high part + mp_uint_t rd, rn, rm; + op_sdiv_udiv: + rd = get_arg_reg(emit, op_str, pn_args[0], 15); + rn = get_arg_reg(emit, op_str, pn_args[1], 15); + rm = get_arg_reg(emit, op_str, pn_args[2], 15); + asm_thumb_op32(&emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm); + } else if (ARMV7M && op == MP_QSTR_udiv) { + op_code = 0xfbb0; // udiv high part + goto op_sdiv_udiv; + } else if (op == MP_QSTR_sub) { + op_code = ASM_THUMB_FORMAT_2_SUB; + goto op_format_2; + } else if (ARMV7M && op == MP_QSTR_strex) { + mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15); + mp_uint_t r_src = get_arg_reg(emit, op_str, pn_args[1], 15); + mp_parse_node_t pn_base, pn_offset; + if (get_arg_addr(emit, op_str, pn_args[2], &pn_base, &pn_offset)) { + mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15); + mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2; + asm_thumb_op32(&emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8); + } + } else { + goto unknown_op; + } + + } else { + goto unknown_op; + } + + return; + +unknown_op: + emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("unsupported Thumb instruction '%s' with %d arguments"), op_str, n_args)); + return; + +branch_not_in_range: + emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("branch not in range")); + return; +} + +const emit_inline_asm_method_table_t emit_inline_thumb_method_table = { + #if MICROPY_DYNAMIC_COMPILER + emit_inline_thumb_new, + emit_inline_thumb_free, + #endif + + emit_inline_thumb_start_pass, + emit_inline_thumb_end_pass, + emit_inline_thumb_count_params, + emit_inline_thumb_label, + emit_inline_thumb_op, +}; + +#endif // MICROPY_EMIT_INLINE_THUMB diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitinlinextensa.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitinlinextensa.c new file mode 100644 index 00000000..57056d59 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitinlinextensa.c @@ -0,0 +1,352 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * + * 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 "py/emit.h" +#include "py/asmxtensa.h" + +#if MICROPY_EMIT_INLINE_XTENSA + +struct _emit_inline_asm_t { + asm_xtensa_t as; + uint16_t pass; + mp_obj_t *error_slot; + mp_uint_t max_num_labels; + qstr *label_lookup; +}; + +static void emit_inline_xtensa_error_msg(emit_inline_asm_t *emit, mp_rom_error_text_t msg) { + *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); +} + +static void emit_inline_xtensa_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) { + *emit->error_slot = exc; +} + +emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels) { + emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); + memset(&emit->as, 0, sizeof(emit->as)); + mp_asm_base_init(&emit->as.base, max_num_labels); + emit->max_num_labels = max_num_labels; + emit->label_lookup = m_new(qstr, max_num_labels); + return emit; +} + +void emit_inline_xtensa_free(emit_inline_asm_t *emit) { + m_del(qstr, emit->label_lookup, emit->max_num_labels); + mp_asm_base_deinit(&emit->as.base, false); + m_del_obj(emit_inline_asm_t, emit); +} + +static void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) { + emit->pass = pass; + emit->error_slot = error_slot; + if (emit->pass == MP_PASS_CODE_SIZE) { + memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); + } + mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); + asm_xtensa_entry(&emit->as, 0); +} + +static void emit_inline_xtensa_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { + asm_xtensa_exit(&emit->as); + asm_xtensa_end_pass(&emit->as); +} + +static mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) { + if (n_params > 4) { + emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("can only have up to 4 parameters to Xtensa assembly")); + return 0; + } + for (mp_uint_t i = 0; i < n_params; i++) { + if (!MP_PARSE_NODE_IS_ID(pn_params[i])) { + emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence a2 to a5")); + return 0; + } + const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); + if (!(strlen(p) == 2 && p[0] == 'a' && (mp_uint_t)p[1] == '2' + i)) { + emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence a2 to a5")); + return 0; + } + } + return n_params; +} + +static bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) { + assert(label_num < emit->max_num_labels); + if (emit->pass == MP_PASS_CODE_SIZE) { + // check for duplicate label on first pass + for (uint i = 0; i < emit->max_num_labels; i++) { + if (emit->label_lookup[i] == label_id) { + return false; + } + } + } + emit->label_lookup[label_num] = label_id; + mp_asm_base_label_assign(&emit->as.base, label_num); + return true; +} + +typedef struct _reg_name_t { byte reg; + byte name[3]; +} reg_name_t; +static const reg_name_t reg_name_table[] = { + {0, "a0\0"}, + {1, "a1\0"}, + {2, "a2\0"}, + {3, "a3\0"}, + {4, "a4\0"}, + {5, "a5\0"}, + {6, "a6\0"}, + {7, "a7\0"}, + {8, "a8\0"}, + {9, "a9\0"}, + {10, "a10"}, + {11, "a11"}, + {12, "a12"}, + {13, "a13"}, + {14, "a14"}, + {15, "a15"}, +}; + +// return empty string in case of error, so we can attempt to parse the string +// without a special check if it was in fact a string +static const char *get_arg_str(mp_parse_node_t pn) { + if (MP_PARSE_NODE_IS_ID(pn)) { + qstr qst = MP_PARSE_NODE_LEAF_ARG(pn); + return qstr_str(qst); + } else { + return ""; + } +} + +static mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + const char *reg_str = get_arg_str(pn); + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) { + const reg_name_t *r = ®_name_table[i]; + if (reg_str[0] == r->name[0] + && reg_str[1] == r->name[1] + && reg_str[2] == r->name[2] + && (reg_str[2] == '\0' || reg_str[3] == '\0')) { + return r->reg; + } + } + emit_inline_xtensa_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("'%s' expects a register"), op)); + return 0; +} + +static uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, int min, int max) { + mp_obj_t o; + if (!mp_parse_node_get_int_maybe(pn, &o)) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an integer"), op)); + return 0; + } + uint32_t i = mp_obj_get_int_truncated(o); + if (min != max && ((int)i < min || (int)i > max)) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' integer %d isn't within range %d..%d"), op, i, min, max)); + return 0; + } + return i; +} + +static int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + if (!MP_PARSE_NODE_IS_ID(pn)) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects a label"), op)); + return 0; + } + qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn); + for (uint i = 0; i < emit->max_num_labels; i++) { + if (emit->label_lookup[i] == label_qstr) { + return i; + } + } + // only need to have the labels on the last pass + if (emit->pass == MP_PASS_EMIT) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("label '%q' not defined"), label_qstr)); + } + return 0; +} + +#define RRR (0) +#define RRI8 (1) +#define RRI8_B (2) + +typedef struct _opcode_table_3arg_t { + uint16_t name; // actually a qstr, which should fit in 16 bits + uint8_t type; + uint8_t a0 : 4; + uint8_t a1 : 4; +} opcode_table_3arg_t; + +static const opcode_table_3arg_t opcode_table_3arg[] = { + // arithmetic opcodes: reg, reg, reg + {MP_QSTR_and_, RRR, 0, 1}, + {MP_QSTR_or_, RRR, 0, 2}, + {MP_QSTR_xor, RRR, 0, 3}, + {MP_QSTR_add, RRR, 0, 8}, + {MP_QSTR_sub, RRR, 0, 12}, + {MP_QSTR_mull, RRR, 2, 8}, + + // load/store/addi opcodes: reg, reg, imm + // upper nibble of type encodes the range of the immediate arg + {MP_QSTR_l8ui, RRI8 | 0x10, 2, 0}, + {MP_QSTR_l16ui, RRI8 | 0x30, 2, 1}, + {MP_QSTR_l32i, RRI8 | 0x50, 2, 2}, + {MP_QSTR_s8i, RRI8 | 0x10, 2, 4}, + {MP_QSTR_s16i, RRI8 | 0x30, 2, 5}, + {MP_QSTR_s32i, RRI8 | 0x50, 2, 6}, + {MP_QSTR_l16si, RRI8 | 0x30, 2, 9}, + {MP_QSTR_addi, RRI8 | 0x00, 2, 12}, + + // branch opcodes: reg, reg, label + {MP_QSTR_ball, RRI8_B, ASM_XTENSA_CC_ALL, 0}, + {MP_QSTR_bany, RRI8_B, ASM_XTENSA_CC_ANY, 0}, + {MP_QSTR_bbc, RRI8_B, ASM_XTENSA_CC_BC, 0}, + {MP_QSTR_bbs, RRI8_B, ASM_XTENSA_CC_BS, 0}, + {MP_QSTR_beq, RRI8_B, ASM_XTENSA_CC_EQ, 0}, + {MP_QSTR_bge, RRI8_B, ASM_XTENSA_CC_GE, 0}, + {MP_QSTR_bgeu, RRI8_B, ASM_XTENSA_CC_GEU, 0}, + {MP_QSTR_blt, RRI8_B, ASM_XTENSA_CC_LT, 0}, + {MP_QSTR_bnall, RRI8_B, ASM_XTENSA_CC_NALL, 0}, + {MP_QSTR_bne, RRI8_B, ASM_XTENSA_CC_NE, 0}, + {MP_QSTR_bnone, RRI8_B, ASM_XTENSA_CC_NONE, 0}, +}; + +static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) { + size_t op_len; + const char *op_str = (const char *)qstr_data(op, &op_len); + + if (n_args == 0) { + if (op == MP_QSTR_ret_n) { + asm_xtensa_op_ret_n(&emit->as); + } else { + goto unknown_op; + } + + } else if (n_args == 1) { + if (op == MP_QSTR_callx0) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_callx0(&emit->as, r0); + } else if (op == MP_QSTR_j) { + int label = get_arg_label(emit, op_str, pn_args[0]); + asm_xtensa_j_label(&emit->as, label); + } else if (op == MP_QSTR_jx) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_jx(&emit->as, r0); + } else { + goto unknown_op; + } + + } else if (n_args == 2) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + if (op == MP_QSTR_beqz) { + int label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_EQ, r0, label); + } else if (op == MP_QSTR_bnez) { + int label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_NE, r0, label); + } else if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) { + // we emit mov.n for both "mov" and "mov_n" opcodes + uint r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op_mov_n(&emit->as, r0, r1); + } else if (op == MP_QSTR_movi) { + // for convenience we emit l32r if the integer doesn't fit in movi + uint32_t imm = get_arg_i(emit, op_str, pn_args[1], 0, 0); + asm_xtensa_mov_reg_i32(&emit->as, r0, imm); + } else { + goto unknown_op; + } + + } else if (n_args == 3) { + // search table for 3 arg instructions + for (uint i = 0; i < MP_ARRAY_SIZE(opcode_table_3arg); i++) { + const opcode_table_3arg_t *o = &opcode_table_3arg[i]; + if (op == o->name) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + uint r1 = get_arg_reg(emit, op_str, pn_args[1]); + if (o->type == RRR) { + uint r2 = get_arg_reg(emit, op_str, pn_args[2]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, o->a0, o->a1, r0, r1, r2)); + } else if (o->type == RRI8_B) { + int label = get_arg_label(emit, op_str, pn_args[2]); + asm_xtensa_bcc_reg_reg_label(&emit->as, o->a0, r0, r1, label); + } else { + int shift, min, max; + if ((o->type & 0xf0) == 0) { + shift = 0; + min = -128; + max = 127; + } else { + shift = (o->type & 0xf0) >> 5; + min = 0; + max = 0xff << shift; + } + uint32_t imm = get_arg_i(emit, op_str, pn_args[2], min, max); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRI8(o->a0, o->a1, r1, r0, (imm >> shift) & 0xff)); + } + return; + } + } + goto unknown_op; + + } else { + goto unknown_op; + } + + return; + +unknown_op: + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("unsupported Xtensa instruction '%s' with %d arguments"), op_str, n_args)); + return; + + /* +branch_not_in_range: + emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("branch not in range")); + return; + */ +} + +const emit_inline_asm_method_table_t emit_inline_xtensa_method_table = { + #if MICROPY_DYNAMIC_COMPILER + emit_inline_xtensa_new, + emit_inline_xtensa_free, + #endif + + emit_inline_xtensa_start_pass, + emit_inline_xtensa_end_pass, + emit_inline_xtensa_count_params, + emit_inline_xtensa_label, + emit_inline_xtensa_op, +}; + +#endif // MICROPY_EMIT_INLINE_XTENSA diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitnarm.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnarm.c new file mode 100644 index 00000000..59075b60 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnarm.c @@ -0,0 +1,18 @@ +// ARM specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_ARM + +// This is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmarm.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (3) // r4 + +#define N_ARM (1) +#define EXPORT_FUN(name) emit_native_arm_##name +#include "py/emitnative.c" + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitnative.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnative.c new file mode 100644 index 00000000..0b84a2ec --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnative.c @@ -0,0 +1,3009 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ + +// Essentially normal Python has 1 type: Python objects +// Viper has more than 1 type, and is just a more complicated (a superset of) Python. +// If you declare everything in Viper as a Python object (ie omit type decls) then +// it should in principle be exactly the same as Python native. +// Having types means having more opcodes, like binary_op_nat_nat, binary_op_nat_obj etc. +// In practice we won't have a VM but rather do this in asm which is actually very minimal. + +// Because it breaks strict Python equivalence it should be a completely separate +// decorator. It breaks equivalence because overflow on integers wraps around. +// It shouldn't break equivalence if you don't use the new types, but since the +// type decls might be used in normal Python for other reasons, it's probably safest, +// cleanest and clearest to make it a separate decorator. + +// Actually, it does break equivalence because integers default to native integers, +// not Python objects. + +// for x in l[0:8]: can be compiled into a native loop if l has pointer type + +#include +#include +#include + +#include "py/emit.h" +#include "py/nativeglue.h" +#include "py/objfun.h" +#include "py/objstr.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_printf(...) (void)0 +#endif + +// wrapper around everything in this file +#if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA || N_XTENSAWIN + +// C stack layout for native functions: +// 0: nlr_buf_t [optional] +// return_value [optional word] +// exc_handler_unwind [optional word] +// emit->code_state_start: mp_code_state_native_t +// emit->stack_start: Python object stack | emit->n_state +// locals (reversed, L0 at end) | +// +// C stack layout for native generator functions: +// 0=emit->stack_start: nlr_buf_t +// return_value +// exc_handler_unwind [optional word] +// +// Then REG_GENERATOR_STATE points to: +// 0=emit->code_state_start: mp_code_state_native_t +// emit->stack_start: Python object stack | emit->n_state +// locals (reversed, L0 at end) | +// +// C stack layout for viper functions: +// 0: nlr_buf_t [optional] +// return_value [optional word] +// exc_handler_unwind [optional word] +// emit->code_state_start: fun_obj, old_globals [optional] +// emit->stack_start: Python object stack | emit->n_state +// locals (reversed, L0 at end) | +// (L0-L2 may be in regs instead) + +// Native emitter needs to know the following sizes and offsets of C structs (on the target): +#if MICROPY_DYNAMIC_COMPILER +#define SIZEOF_NLR_BUF (2 + mp_dynamic_compiler.nlr_buf_num_regs + 1) // the +1 is conservative in case MICROPY_ENABLE_PYSTACK enabled +#else +#define SIZEOF_NLR_BUF (sizeof(nlr_buf_t) / sizeof(uintptr_t)) +#endif +#define SIZEOF_CODE_STATE (sizeof(mp_code_state_native_t) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_STATE (offsetof(mp_code_state_native_t, state) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_FUN_BC (offsetof(mp_code_state_native_t, fun_bc) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_IP (offsetof(mp_code_state_native_t, ip) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_SP (offsetof(mp_code_state_native_t, sp) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_N_STATE (offsetof(mp_code_state_native_t, n_state) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_CONTEXT (offsetof(mp_obj_fun_bc_t, context) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_CHILD_TABLE (offsetof(mp_obj_fun_bc_t, child_table) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_BYTECODE (offsetof(mp_obj_fun_bc_t, bytecode) / sizeof(uintptr_t)) +#define OFFSETOF_MODULE_CONTEXT_QSTR_TABLE (offsetof(mp_module_context_t, constants.qstr_table) / sizeof(uintptr_t)) +#define OFFSETOF_MODULE_CONTEXT_OBJ_TABLE (offsetof(mp_module_context_t, constants.obj_table) / sizeof(uintptr_t)) +#define OFFSETOF_MODULE_CONTEXT_GLOBALS (offsetof(mp_module_context_t, module.globals) / sizeof(uintptr_t)) + +// If not already defined, set parent args to same as child call registers +#ifndef REG_PARENT_RET +#define REG_PARENT_RET REG_RET +#define REG_PARENT_ARG_1 REG_ARG_1 +#define REG_PARENT_ARG_2 REG_ARG_2 +#define REG_PARENT_ARG_3 REG_ARG_3 +#define REG_PARENT_ARG_4 REG_ARG_4 +#endif + +// Word index of nlr_buf_t.ret_val +#define NLR_BUF_IDX_RET_VAL (1) + +// Whether the viper function needs access to fun_obj +#define NEED_FUN_OBJ(emit) ((emit)->scope->exc_stack_size > 0 \ + || ((emit)->scope->scope_flags & (MP_SCOPE_FLAG_REFGLOBALS | MP_SCOPE_FLAG_HASCONSTS))) + +// Whether the native/viper function needs to be wrapped in an exception handler +#define NEED_GLOBAL_EXC_HANDLER(emit) ((emit)->scope->exc_stack_size > 0 \ + || ((emit)->scope->scope_flags & (MP_SCOPE_FLAG_GENERATOR | MP_SCOPE_FLAG_REFGLOBALS))) + +// Whether a slot is needed to store LOCAL_IDX_EXC_HANDLER_UNWIND +#define NEED_EXC_HANDLER_UNWIND(emit) ((emit)->scope->exc_stack_size > 0) + +// Whether registers can be used to store locals (only true if there are no +// exception handlers, because otherwise an nlr_jump will restore registers to +// their state at the start of the function and updates to locals will be lost) +#define CAN_USE_REGS_FOR_LOCALS(emit) ((emit)->scope->exc_stack_size == 0 && !(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) + +// Indices within the local C stack for various variables +#define LOCAL_IDX_EXC_VAL(emit) (NLR_BUF_IDX_RET_VAL) +#define LOCAL_IDX_EXC_HANDLER_PC(emit) (NLR_BUF_IDX_LOCAL_1) +#define LOCAL_IDX_EXC_HANDLER_UNWIND(emit) (SIZEOF_NLR_BUF + 1) // this needs a dedicated variable outside nlr_buf_t +#define LOCAL_IDX_RET_VAL(emit) (SIZEOF_NLR_BUF) // needed when NEED_GLOBAL_EXC_HANDLER is true +#define LOCAL_IDX_FUN_OBJ(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_FUN_BC) +#define LOCAL_IDX_OLD_GLOBALS(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_IP) +#define LOCAL_IDX_GEN_PC(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_IP) +#define LOCAL_IDX_LOCAL_VAR(emit, local_num) ((emit)->stack_start + (emit)->n_state - 1 - (local_num)) + +#if MICROPY_PERSISTENT_CODE_SAVE + +// When building with the ability to save native code to .mpy files: +// - Qstrs are indirect via qstr_table, and REG_LOCAL_3 always points to qstr_table. +// - In a generator no registers are used to store locals, and REG_LOCAL_2 points to the generator state. +// - At most 2 registers hold local variables (see CAN_USE_REGS_FOR_LOCALS for when this is possible). + +#define REG_GENERATOR_STATE (REG_LOCAL_2) +#define REG_QSTR_TABLE (REG_LOCAL_3) +#define MAX_REGS_FOR_LOCAL_VARS (2) + +static const uint8_t reg_local_table[MAX_REGS_FOR_LOCAL_VARS] = {REG_LOCAL_1, REG_LOCAL_2}; + +#else + +// When building without the ability to save native code to .mpy files: +// - Qstrs values are written directly into the machine code. +// - In a generator no registers are used to store locals, and REG_LOCAL_3 points to the generator state. +// - At most 3 registers hold local variables (see CAN_USE_REGS_FOR_LOCALS for when this is possible). + +#define REG_GENERATOR_STATE (REG_LOCAL_3) +#define MAX_REGS_FOR_LOCAL_VARS (3) + +static const uint8_t reg_local_table[MAX_REGS_FOR_LOCAL_VARS] = {REG_LOCAL_1, REG_LOCAL_2, REG_LOCAL_3}; + +#endif + +#define REG_LOCAL_LAST (reg_local_table[MAX_REGS_FOR_LOCAL_VARS - 1]) + +#define EMIT_NATIVE_VIPER_TYPE_ERROR(emit, ...) do { \ + *emit->error_slot = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, __VA_ARGS__); \ +} while (0) + +typedef enum { + STACK_VALUE, + STACK_REG, + STACK_IMM, +} stack_info_kind_t; + +// these enums must be distinct and the bottom 4 bits +// must correspond to the correct MP_NATIVE_TYPE_xxx value +typedef enum { + VTYPE_PYOBJ = 0x00 | MP_NATIVE_TYPE_OBJ, + VTYPE_BOOL = 0x00 | MP_NATIVE_TYPE_BOOL, + VTYPE_INT = 0x00 | MP_NATIVE_TYPE_INT, + VTYPE_UINT = 0x00 | MP_NATIVE_TYPE_UINT, + VTYPE_PTR = 0x00 | MP_NATIVE_TYPE_PTR, + VTYPE_PTR8 = 0x00 | MP_NATIVE_TYPE_PTR8, + VTYPE_PTR16 = 0x00 | MP_NATIVE_TYPE_PTR16, + VTYPE_PTR32 = 0x00 | MP_NATIVE_TYPE_PTR32, + + VTYPE_PTR_NONE = 0x50 | MP_NATIVE_TYPE_PTR, + + VTYPE_UNBOUND = 0x60 | MP_NATIVE_TYPE_OBJ, + VTYPE_BUILTIN_CAST = 0x70 | MP_NATIVE_TYPE_OBJ, +} vtype_kind_t; + +static qstr vtype_to_qstr(vtype_kind_t vtype) { + switch (vtype) { + case VTYPE_PYOBJ: + return MP_QSTR_object; + case VTYPE_BOOL: + return MP_QSTR_bool; + case VTYPE_INT: + return MP_QSTR_int; + case VTYPE_UINT: + return MP_QSTR_uint; + case VTYPE_PTR: + return MP_QSTR_ptr; + case VTYPE_PTR8: + return MP_QSTR_ptr8; + case VTYPE_PTR16: + return MP_QSTR_ptr16; + case VTYPE_PTR32: + return MP_QSTR_ptr32; + case VTYPE_PTR_NONE: + default: + return MP_QSTR_None; + } +} + +typedef struct _stack_info_t { + vtype_kind_t vtype; + stack_info_kind_t kind; + union { + int u_reg; + mp_int_t u_imm; + } data; +} stack_info_t; + +#define UNWIND_LABEL_UNUSED (0x7fff) +#define UNWIND_LABEL_DO_FINAL_UNWIND (0x7ffe) + +typedef struct _exc_stack_entry_t { + uint16_t label : 15; + uint16_t is_finally : 1; + uint16_t unwind_label : 15; + uint16_t is_active : 1; +} exc_stack_entry_t; + +struct _emit_t { + mp_emit_common_t *emit_common; + mp_obj_t *error_slot; + uint *label_slot; + uint exit_label; + int pass; + + bool do_viper_types; + + mp_uint_t local_vtype_alloc; + vtype_kind_t *local_vtype; + + mp_uint_t stack_info_alloc; + stack_info_t *stack_info; + vtype_kind_t saved_stack_vtype; + + size_t exc_stack_alloc; + size_t exc_stack_size; + exc_stack_entry_t *exc_stack; + + int prelude_offset; + int prelude_ptr_index; + int start_offset; + int n_state; + uint16_t code_state_start; + uint16_t stack_start; + int stack_size; + uint16_t n_info; + uint16_t n_cell; + + scope_t *scope; + + ASM_T *as; +}; + +static void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj); +static void emit_native_global_exc_entry(emit_t *emit); +static void emit_native_global_exc_exit(emit_t *emit); +static void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj); + +emit_t *EXPORT_FUN(new)(mp_emit_common_t * emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels) { + emit_t *emit = m_new0(emit_t, 1); + emit->emit_common = emit_common; + emit->error_slot = error_slot; + emit->label_slot = label_slot; + emit->stack_info_alloc = 8; + emit->stack_info = m_new(stack_info_t, emit->stack_info_alloc); + emit->exc_stack_alloc = 8; + emit->exc_stack = m_new(exc_stack_entry_t, emit->exc_stack_alloc); + emit->as = m_new0(ASM_T, 1); + mp_asm_base_init(&emit->as->base, max_num_labels); + return emit; +} + +void EXPORT_FUN(free)(emit_t * emit) { + mp_asm_base_deinit(&emit->as->base, false); + m_del_obj(ASM_T, emit->as); + m_del(exc_stack_entry_t, emit->exc_stack, emit->exc_stack_alloc); + m_del(vtype_kind_t, emit->local_vtype, emit->local_vtype_alloc); + m_del(stack_info_t, emit->stack_info, emit->stack_info_alloc); + m_del_obj(emit_t, emit); +} + +static void emit_call_with_imm_arg(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val, int arg_reg); + +static void emit_native_mov_reg_const(emit_t *emit, int reg_dest, int const_val) { + ASM_LOAD_REG_REG_OFFSET(emit->as, reg_dest, REG_FUN_TABLE, const_val); +} + +static void emit_native_mov_state_reg(emit_t *emit, int local_num, int reg_src) { + if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + ASM_STORE_REG_REG_OFFSET(emit->as, reg_src, REG_GENERATOR_STATE, local_num); + } else { + ASM_MOV_LOCAL_REG(emit->as, local_num, reg_src); + } +} + +static void emit_native_mov_reg_state(emit_t *emit, int reg_dest, int local_num) { + if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + ASM_LOAD_REG_REG_OFFSET(emit->as, reg_dest, REG_GENERATOR_STATE, local_num); + } else { + ASM_MOV_REG_LOCAL(emit->as, reg_dest, local_num); + } +} + +static void emit_native_mov_reg_state_addr(emit_t *emit, int reg_dest, int local_num) { + if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + ASM_MOV_REG_IMM(emit->as, reg_dest, local_num * ASM_WORD_SIZE); + ASM_ADD_REG_REG(emit->as, reg_dest, REG_GENERATOR_STATE); + } else { + ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, local_num); + } +} + +static void emit_native_mov_reg_qstr(emit_t *emit, int arg_reg, qstr qst) { + #if MICROPY_PERSISTENT_CODE_SAVE + ASM_LOAD16_REG_REG_OFFSET(emit->as, arg_reg, REG_QSTR_TABLE, mp_emit_common_use_qstr(emit->emit_common, qst)); + #else + ASM_MOV_REG_IMM(emit->as, arg_reg, qst); + #endif +} + +static void emit_native_mov_reg_qstr_obj(emit_t *emit, int reg_dest, qstr qst) { + #if MICROPY_PERSISTENT_CODE_SAVE + emit_load_reg_with_object(emit, reg_dest, MP_OBJ_NEW_QSTR(qst)); + #else + ASM_MOV_REG_IMM(emit->as, reg_dest, (mp_uint_t)MP_OBJ_NEW_QSTR(qst)); + #endif +} + +#define emit_native_mov_state_imm_via(emit, local_num, imm, reg_temp) \ + do { \ + ASM_MOV_REG_IMM((emit)->as, (reg_temp), (imm)); \ + emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ + } while (false) + +static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { + DEBUG_printf("start_pass(pass=%u, scope=%p)\n", pass, scope); + + emit->pass = pass; + emit->do_viper_types = scope->emit_options == MP_EMIT_OPT_VIPER; + emit->stack_size = 0; + emit->scope = scope; + + // allocate memory for keeping track of the types of locals + if (emit->local_vtype_alloc < scope->num_locals) { + emit->local_vtype = m_renew(vtype_kind_t, emit->local_vtype, emit->local_vtype_alloc, scope->num_locals); + emit->local_vtype_alloc = scope->num_locals; + } + + // set default type for arguments + mp_uint_t num_args = emit->scope->num_pos_args + emit->scope->num_kwonly_args; + if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) { + num_args += 1; + } + if (scope->scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) { + num_args += 1; + } + for (mp_uint_t i = 0; i < num_args; i++) { + emit->local_vtype[i] = VTYPE_PYOBJ; + } + + // Set viper type for arguments + if (emit->do_viper_types) { + for (int i = 0; i < emit->scope->id_info_len; ++i) { + id_info_t *id = &emit->scope->id_info[i]; + if (id->flags & ID_FLAG_IS_PARAM) { + assert(id->local_num < emit->local_vtype_alloc); + emit->local_vtype[id->local_num] = id->flags >> ID_FLAG_VIPER_TYPE_POS; + } + } + } + + // local variables begin unbound, and have unknown type + for (mp_uint_t i = num_args; i < emit->local_vtype_alloc; i++) { + emit->local_vtype[i] = emit->do_viper_types ? VTYPE_UNBOUND : VTYPE_PYOBJ; + } + + // values on stack begin unbound + for (mp_uint_t i = 0; i < emit->stack_info_alloc; i++) { + emit->stack_info[i].kind = STACK_VALUE; + emit->stack_info[i].vtype = VTYPE_UNBOUND; + } + + mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); + + // generate code for entry to function + + // Work out start of code state (mp_code_state_native_t or reduced version for viper) + emit->code_state_start = 0; + if (NEED_GLOBAL_EXC_HANDLER(emit)) { + emit->code_state_start = SIZEOF_NLR_BUF; // for nlr_buf_t + emit->code_state_start += 1; // for return_value + if (NEED_EXC_HANDLER_UNWIND(emit)) { + emit->code_state_start += 1; + } + } + + size_t fun_table_off = mp_emit_common_use_const_obj(emit->emit_common, MP_OBJ_FROM_PTR(&mp_fun_table)); + + if (emit->do_viper_types) { + // Work out size of state (locals plus stack) + // n_state counts all stack and locals, even those in registers + emit->n_state = scope->num_locals + scope->stack_size; + int num_locals_in_regs = 0; + if (CAN_USE_REGS_FOR_LOCALS(emit)) { + num_locals_in_regs = scope->num_locals; + if (num_locals_in_regs > MAX_REGS_FOR_LOCAL_VARS) { + num_locals_in_regs = MAX_REGS_FOR_LOCAL_VARS; + } + // Need a spot for REG_LOCAL_LAST (see below) + if (scope->num_pos_args >= MAX_REGS_FOR_LOCAL_VARS + 1) { + --num_locals_in_regs; + } + } + + // Work out where the locals and Python stack start within the C stack + if (NEED_GLOBAL_EXC_HANDLER(emit)) { + // Reserve 2 words for function object and old globals + emit->stack_start = emit->code_state_start + 2; + } else if (scope->scope_flags & MP_SCOPE_FLAG_HASCONSTS) { + // Reserve 1 word for function object, to access const table + emit->stack_start = emit->code_state_start + 1; + } else { + emit->stack_start = emit->code_state_start + 0; + } + + // Entry to function + ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs); + + #if N_X86 + asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1); + #endif + + // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + #if MICROPY_PERSISTENT_CODE_SAVE + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_QSTR_TABLE, REG_FUN_TABLE, OFFSETOF_MODULE_CONTEXT_QSTR_TABLE); + #endif + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, fun_table_off); + + // Store function object (passed as first arg) to stack if needed + if (NEED_FUN_OBJ(emit)) { + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); + } + + // Put n_args in REG_ARG_1, n_kw in REG_ARG_2, args array in REG_LOCAL_LAST + #if N_X86 + asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_1); + asm_x86_mov_arg_to_r32(emit->as, 2, REG_ARG_2); + asm_x86_mov_arg_to_r32(emit->as, 3, REG_LOCAL_LAST); + #else + ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_PARENT_ARG_2); + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_PARENT_ARG_3); + ASM_MOV_REG_REG(emit->as, REG_LOCAL_LAST, REG_PARENT_ARG_4); + #endif + + // Check number of args matches this function, and call mp_arg_check_num_sig if not + ASM_JUMP_IF_REG_NONZERO(emit->as, REG_ARG_2, *emit->label_slot + 4, true); + ASM_MOV_REG_IMM(emit->as, REG_ARG_3, scope->num_pos_args); + ASM_JUMP_IF_REG_EQ(emit->as, REG_ARG_1, REG_ARG_3, *emit->label_slot + 5); + mp_asm_base_label_assign(&emit->as->base, *emit->label_slot + 4); + ASM_MOV_REG_IMM(emit->as, REG_ARG_3, MP_OBJ_FUN_MAKE_SIG(scope->num_pos_args, scope->num_pos_args, false)); + ASM_CALL_IND(emit->as, MP_F_ARG_CHECK_NUM_SIG); + mp_asm_base_label_assign(&emit->as->base, *emit->label_slot + 5); + + // Store arguments into locals (reg or stack), converting to native if needed + for (int i = 0; i < emit->scope->num_pos_args; i++) { + int r = REG_ARG_1; + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_LOCAL_LAST, i); + if (emit->local_vtype[i] != VTYPE_PYOBJ) { + emit_call_with_imm_arg(emit, MP_F_CONVERT_OBJ_TO_NATIVE, emit->local_vtype[i], REG_ARG_2); + r = REG_RET; + } + // REG_LOCAL_LAST points to the args array so be sure not to overwrite it if it's still needed + if (i < MAX_REGS_FOR_LOCAL_VARS && CAN_USE_REGS_FOR_LOCALS(emit) && (i != MAX_REGS_FOR_LOCAL_VARS - 1 || emit->scope->num_pos_args == MAX_REGS_FOR_LOCAL_VARS)) { + ASM_MOV_REG_REG(emit->as, reg_local_table[i], r); + } else { + emit_native_mov_state_reg(emit, LOCAL_IDX_LOCAL_VAR(emit, i), r); + } + } + // Get local from the stack back into REG_LOCAL_LAST if this reg couldn't be written to above + if (emit->scope->num_pos_args >= MAX_REGS_FOR_LOCAL_VARS + 1 && CAN_USE_REGS_FOR_LOCALS(emit)) { + ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_LAST, LOCAL_IDX_LOCAL_VAR(emit, MAX_REGS_FOR_LOCAL_VARS - 1)); + } + + emit_native_global_exc_entry(emit); + + } else { + // work out size of state (locals plus stack) + emit->n_state = scope->num_locals + scope->stack_size; + + // Store in the first machine-word an index used to the function's prelude. + // This is used at runtime by mp_obj_fun_native_get_prelude_ptr(). + mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->prelude_ptr_index); + + if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->start_offset); + ASM_ENTRY(emit->as, emit->code_state_start); + + // Reset the state size for the state pointed to by REG_GENERATOR_STATE + emit->code_state_start = 0; + emit->stack_start = SIZEOF_CODE_STATE; + + // Put address of code_state into REG_GENERATOR_STATE + #if N_X86 + asm_x86_mov_arg_to_r32(emit->as, 0, REG_GENERATOR_STATE); + #else + ASM_MOV_REG_REG(emit->as, REG_GENERATOR_STATE, REG_PARENT_ARG_1); + #endif + + // Put throw value into LOCAL_IDX_EXC_VAL slot, for yield/yield-from + #if N_X86 + asm_x86_mov_arg_to_r32(emit->as, 1, REG_PARENT_ARG_2); + #endif + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_PARENT_ARG_2); + + // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONTEXT); + #if MICROPY_PERSISTENT_CODE_SAVE + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_QSTR_TABLE, REG_TEMP0, OFFSETOF_MODULE_CONTEXT_QSTR_TABLE); + #endif + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_TEMP0, fun_table_off); + } else { + // The locals and stack start after the code_state structure + emit->stack_start = emit->code_state_start + SIZEOF_CODE_STATE; + + // Allocate space on C-stack for code_state structure, which includes state + ASM_ENTRY(emit->as, emit->stack_start + emit->n_state); + + // Prepare incoming arguments for call to mp_setup_code_state + + #if N_X86 + asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1); + asm_x86_mov_arg_to_r32(emit->as, 1, REG_PARENT_ARG_2); + asm_x86_mov_arg_to_r32(emit->as, 2, REG_PARENT_ARG_3); + asm_x86_mov_arg_to_r32(emit->as, 3, REG_PARENT_ARG_4); + #endif + + // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + #if MICROPY_PERSISTENT_CODE_SAVE + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_QSTR_TABLE, REG_FUN_TABLE, OFFSETOF_MODULE_CONTEXT_QSTR_TABLE); + #endif + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, fun_table_off); + + // Set code_state.fun_bc + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); + + // Set code_state.n_state (only works on little endian targets due to n_state being uint16_t) + emit_native_mov_state_imm_via(emit, emit->code_state_start + OFFSETOF_CODE_STATE_N_STATE, emit->n_state, REG_ARG_1); + + // Put address of code_state into first arg + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, emit->code_state_start); + + // Copy next 3 args if needed + #if REG_ARG_2 != REG_PARENT_ARG_2 + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_PARENT_ARG_2); + #endif + #if REG_ARG_3 != REG_PARENT_ARG_3 + ASM_MOV_REG_REG(emit->as, REG_ARG_3, REG_PARENT_ARG_3); + #endif + #if REG_ARG_4 != REG_PARENT_ARG_4 + ASM_MOV_REG_REG(emit->as, REG_ARG_4, REG_PARENT_ARG_4); + #endif + + // Call mp_setup_code_state to prepare code_state structure + #if N_THUMB + asm_thumb_bl_ind(emit->as, MP_F_SETUP_CODE_STATE, ASM_THUMB_REG_R4); + #elif N_ARM + asm_arm_bl_ind(emit->as, MP_F_SETUP_CODE_STATE, ASM_ARM_REG_R4); + #else + ASM_CALL_IND(emit->as, MP_F_SETUP_CODE_STATE); + #endif + } + + emit_native_global_exc_entry(emit); + + // cache some locals in registers, but only if no exception handlers + if (CAN_USE_REGS_FOR_LOCALS(emit)) { + for (int i = 0; i < MAX_REGS_FOR_LOCAL_VARS && i < scope->num_locals; ++i) { + ASM_MOV_REG_LOCAL(emit->as, reg_local_table[i], LOCAL_IDX_LOCAL_VAR(emit, i)); + } + } + + // set the type of closed over variables + for (mp_uint_t i = 0; i < scope->id_info_len; i++) { + id_info_t *id = &scope->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL) { + emit->local_vtype[id->local_num] = VTYPE_PYOBJ; + } + } + } +} + +static inline void emit_native_write_code_info_byte(emit_t *emit, byte val) { + mp_asm_base_data(&emit->as->base, 1, val); +} + +static inline void emit_native_write_code_info_qstr(emit_t *emit, qstr qst) { + mp_encode_uint(&emit->as->base, mp_asm_base_get_cur_to_write_bytes, mp_emit_common_use_qstr(emit->emit_common, qst)); +} + +static bool emit_native_end_pass(emit_t *emit) { + emit_native_global_exc_exit(emit); + + if (!emit->do_viper_types) { + emit->prelude_offset = mp_asm_base_get_code_pos(&emit->as->base); + emit->prelude_ptr_index = emit->emit_common->ct_cur_child; + + size_t n_state = emit->n_state; + size_t n_exc_stack = 0; // exc-stack not needed for native code + MP_BC_PRELUDE_SIG_ENCODE(n_state, n_exc_stack, emit->scope, emit_native_write_code_info_byte, emit); + + size_t n_info = emit->n_info; + size_t n_cell = emit->n_cell; + MP_BC_PRELUDE_SIZE_ENCODE(n_info, n_cell, emit_native_write_code_info_byte, emit); + + // bytecode prelude: source info (function and argument qstrs) + size_t info_start = mp_asm_base_get_code_pos(&emit->as->base); + emit_native_write_code_info_qstr(emit, emit->scope->simple_name); + for (int i = 0; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) { + qstr qst = MP_QSTR__star_; + for (int j = 0; j < emit->scope->id_info_len; ++j) { + id_info_t *id = &emit->scope->id_info[j]; + if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) { + qst = id->qst; + break; + } + } + emit_native_write_code_info_qstr(emit, qst); + } + emit->n_info = mp_asm_base_get_code_pos(&emit->as->base) - info_start; + + // bytecode prelude: initialise closed over variables + size_t cell_start = mp_asm_base_get_code_pos(&emit->as->base); + for (int i = 0; i < emit->scope->id_info_len; i++) { + id_info_t *id = &emit->scope->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL) { + assert(id->local_num <= 255); + mp_asm_base_data(&emit->as->base, 1, id->local_num); // write the local which should be converted to a cell + } + } + emit->n_cell = mp_asm_base_get_code_pos(&emit->as->base) - cell_start; + + } + + ASM_END_PASS(emit->as); + + // check stack is back to zero size + assert(emit->stack_size == 0); + assert(emit->exc_stack_size == 0); + + if (emit->pass == MP_PASS_EMIT) { + void *f = mp_asm_base_get_code(&emit->as->base); + mp_uint_t f_len = mp_asm_base_get_code_size(&emit->as->base); + + mp_raw_code_t **children = emit->emit_common->children; + if (!emit->do_viper_types) { + #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE + // Executable code cannot be accessed byte-wise on this architecture, so copy + // the prelude to a separate memory region that is byte-wise readable. + void *buf = emit->as->base.code_base + emit->prelude_offset; + size_t n = emit->as->base.code_offset - emit->prelude_offset; + const uint8_t *prelude_ptr = memcpy(m_new(uint8_t, n), buf, n); + #else + // Point to the prelude directly, at the end of the machine code data. + const uint8_t *prelude_ptr = (const uint8_t *)f + emit->prelude_offset; + #endif + + // Store the pointer to the prelude using the child_table. + assert(emit->prelude_ptr_index == emit->emit_common->ct_cur_child); + if (emit->prelude_ptr_index == 0) { + children = (void *)prelude_ptr; + } else { + children = m_renew(mp_raw_code_t *, children, emit->prelude_ptr_index, emit->prelude_ptr_index + 1); + children[emit->prelude_ptr_index] = (void *)prelude_ptr; + } + } + + mp_emit_glue_assign_native(emit->scope->raw_code, + emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, + f, f_len, + children, + #if MICROPY_PERSISTENT_CODE_SAVE + emit->emit_common->ct_cur_child, + emit->prelude_offset, + #endif + emit->scope->scope_flags, 0, 0); + } + + return true; +} + +static void ensure_extra_stack(emit_t *emit, size_t delta) { + if (emit->stack_size + delta > emit->stack_info_alloc) { + size_t new_alloc = (emit->stack_size + delta + 8) & ~3; + emit->stack_info = m_renew(stack_info_t, emit->stack_info, emit->stack_info_alloc, new_alloc); + emit->stack_info_alloc = new_alloc; + } +} + +static void adjust_stack(emit_t *emit, mp_int_t stack_size_delta) { + assert((mp_int_t)emit->stack_size + stack_size_delta >= 0); + assert((mp_int_t)emit->stack_size + stack_size_delta <= (mp_int_t)emit->stack_info_alloc); + emit->stack_size += stack_size_delta; + if (emit->pass > MP_PASS_SCOPE && emit->stack_size > emit->scope->stack_size) { + emit->scope->stack_size = emit->stack_size; + } + #if DEBUG_PRINT + DEBUG_printf(" adjust_stack; stack_size=%d+%d; stack now:", emit->stack_size - stack_size_delta, stack_size_delta); + for (int i = 0; i < emit->stack_size; i++) { + stack_info_t *si = &emit->stack_info[i]; + DEBUG_printf(" (v=%d k=%d %d)", si->vtype, si->kind, si->data.u_reg); + } + DEBUG_printf("\n"); + #endif +} + +static void emit_native_adjust_stack_size(emit_t *emit, mp_int_t delta) { + DEBUG_printf("adjust_stack_size(" INT_FMT ")\n", delta); + if (delta > 0) { + ensure_extra_stack(emit, delta); + } + // If we are adjusting the stack in a positive direction (pushing) then we + // need to fill in values for the stack kind and vtype of the newly-pushed + // entries. These should be set to "value" (ie not reg or imm) because we + // should only need to adjust the stack due to a jump to this part in the + // code (and hence we have settled the stack before the jump). + for (mp_int_t i = 0; i < delta; i++) { + stack_info_t *si = &emit->stack_info[emit->stack_size + i]; + si->kind = STACK_VALUE; + // TODO we don't know the vtype to use here. At the moment this is a + // hack to get the case of multi comparison working. + if (delta == 1) { + si->vtype = emit->saved_stack_vtype; + } else { + si->vtype = VTYPE_PYOBJ; + } + } + adjust_stack(emit, delta); +} + +static void emit_native_set_source_line(emit_t *emit, mp_uint_t source_line) { + (void)emit; + (void)source_line; +} + +// this must be called at start of emit functions +static void emit_native_pre(emit_t *emit) { + (void)emit; +} + +// depth==0 is top, depth==1 is before top, etc +static stack_info_t *peek_stack(emit_t *emit, mp_uint_t depth) { + return &emit->stack_info[emit->stack_size - 1 - depth]; +} + +// depth==0 is top, depth==1 is before top, etc +static vtype_kind_t peek_vtype(emit_t *emit, mp_uint_t depth) { + if (emit->do_viper_types) { + return peek_stack(emit, depth)->vtype; + } else { + // Type is always PYOBJ even if the intermediate stored value is not + return VTYPE_PYOBJ; + } +} + +// pos=1 is TOS, pos=2 is next, etc +// use pos=0 for no skipping +static void need_reg_single(emit_t *emit, int reg_needed, int skip_stack_pos) { + skip_stack_pos = emit->stack_size - skip_stack_pos; + for (int i = 0; i < emit->stack_size; i++) { + if (i != skip_stack_pos) { + stack_info_t *si = &emit->stack_info[i]; + if (si->kind == STACK_REG && si->data.u_reg == reg_needed) { + si->kind = STACK_VALUE; + emit_native_mov_state_reg(emit, emit->stack_start + i, si->data.u_reg); + } + } + } +} + +// Ensures all unsettled registers that hold Python values are copied to the +// concrete Python stack. All registers are then free to use. +static void need_reg_all(emit_t *emit) { + for (int i = 0; i < emit->stack_size; i++) { + stack_info_t *si = &emit->stack_info[i]; + if (si->kind == STACK_REG) { + DEBUG_printf(" reg(%u) to local(%u)\n", si->data.u_reg, emit->stack_start + i); + si->kind = STACK_VALUE; + emit_native_mov_state_reg(emit, emit->stack_start + i, si->data.u_reg); + } + } +} + +static vtype_kind_t load_reg_stack_imm(emit_t *emit, int reg_dest, const stack_info_t *si, bool convert_to_pyobj) { + if (!convert_to_pyobj && emit->do_viper_types) { + ASM_MOV_REG_IMM(emit->as, reg_dest, si->data.u_imm); + return si->vtype; + } else { + if (si->vtype == VTYPE_PYOBJ) { + ASM_MOV_REG_IMM(emit->as, reg_dest, si->data.u_imm); + } else if (si->vtype == VTYPE_BOOL) { + emit_native_mov_reg_const(emit, reg_dest, MP_F_CONST_FALSE_OBJ + si->data.u_imm); + } else if (si->vtype == VTYPE_INT || si->vtype == VTYPE_UINT) { + ASM_MOV_REG_IMM(emit->as, reg_dest, (uintptr_t)MP_OBJ_NEW_SMALL_INT(si->data.u_imm)); + } else if (si->vtype == VTYPE_PTR_NONE) { + emit_native_mov_reg_const(emit, reg_dest, MP_F_CONST_NONE_OBJ); + } else { + mp_raise_NotImplementedError(MP_ERROR_TEXT("conversion to object")); + } + return VTYPE_PYOBJ; + } +} + +// Copies all unsettled registers and immediates that are Python values into the +// concrete Python stack. This ensures the concrete Python stack holds valid +// values for the current stack_size. +// This function may clobber REG_TEMP1. +static void need_stack_settled(emit_t *emit) { + DEBUG_printf(" need_stack_settled; stack_size=%d\n", emit->stack_size); + need_reg_all(emit); + for (int i = 0; i < emit->stack_size; i++) { + stack_info_t *si = &emit->stack_info[i]; + if (si->kind == STACK_IMM) { + DEBUG_printf(" imm(" INT_FMT ") to local(%u)\n", si->data.u_imm, emit->stack_start + i); + si->kind = STACK_VALUE; + // using REG_TEMP1 to avoid clobbering REG_TEMP0 (aka REG_RET) + si->vtype = load_reg_stack_imm(emit, REG_TEMP1, si, false); + emit_native_mov_state_reg(emit, emit->stack_start + i, REG_TEMP1); + } + } +} + +// pos=1 is TOS, pos=2 is next, etc +static void emit_access_stack(emit_t *emit, int pos, vtype_kind_t *vtype, int reg_dest) { + need_reg_single(emit, reg_dest, pos); + stack_info_t *si = &emit->stack_info[emit->stack_size - pos]; + *vtype = si->vtype; + switch (si->kind) { + case STACK_VALUE: + emit_native_mov_reg_state(emit, reg_dest, emit->stack_start + emit->stack_size - pos); + break; + + case STACK_REG: + if (si->data.u_reg != reg_dest) { + ASM_MOV_REG_REG(emit->as, reg_dest, si->data.u_reg); + } + break; + + case STACK_IMM: + *vtype = load_reg_stack_imm(emit, reg_dest, si, false); + break; + } +} + +// does an efficient X=pop(); discard(); push(X) +// needs a (non-temp) register in case the popped element was stored in the stack +static void emit_fold_stack_top(emit_t *emit, int reg_dest) { + stack_info_t *si = &emit->stack_info[emit->stack_size - 2]; + si[0] = si[1]; + if (si->kind == STACK_VALUE) { + // if folded element was on the stack we need to put it in a register + emit_native_mov_reg_state(emit, reg_dest, emit->stack_start + emit->stack_size - 1); + si->kind = STACK_REG; + si->data.u_reg = reg_dest; + } + adjust_stack(emit, -1); +} + +// If stacked value is in a register and the register is not r1 or r2, then +// *reg_dest is set to that register. Otherwise the value is put in *reg_dest. +static void emit_pre_pop_reg_flexible(emit_t *emit, vtype_kind_t *vtype, int *reg_dest, int not_r1, int not_r2) { + stack_info_t *si = peek_stack(emit, 0); + if (si->kind == STACK_REG && si->data.u_reg != not_r1 && si->data.u_reg != not_r2) { + *vtype = si->vtype; + *reg_dest = si->data.u_reg; + need_reg_single(emit, *reg_dest, 1); + } else { + emit_access_stack(emit, 1, vtype, *reg_dest); + } + adjust_stack(emit, -1); +} + +static void emit_pre_pop_discard(emit_t *emit) { + adjust_stack(emit, -1); +} + +static void emit_pre_pop_reg(emit_t *emit, vtype_kind_t *vtype, int reg_dest) { + emit_access_stack(emit, 1, vtype, reg_dest); + adjust_stack(emit, -1); +} + +static void emit_pre_pop_reg_reg(emit_t *emit, vtype_kind_t *vtypea, int rega, vtype_kind_t *vtypeb, int regb) { + emit_pre_pop_reg(emit, vtypea, rega); + emit_pre_pop_reg(emit, vtypeb, regb); +} + +static void emit_pre_pop_reg_reg_reg(emit_t *emit, vtype_kind_t *vtypea, int rega, vtype_kind_t *vtypeb, int regb, vtype_kind_t *vtypec, int regc) { + emit_pre_pop_reg(emit, vtypea, rega); + emit_pre_pop_reg(emit, vtypeb, regb); + emit_pre_pop_reg(emit, vtypec, regc); +} + +static void emit_post(emit_t *emit) { + (void)emit; +} + +static void emit_post_top_set_vtype(emit_t *emit, vtype_kind_t new_vtype) { + stack_info_t *si = &emit->stack_info[emit->stack_size - 1]; + si->vtype = new_vtype; +} + +static void emit_post_push_reg(emit_t *emit, vtype_kind_t vtype, int reg) { + ensure_extra_stack(emit, 1); + stack_info_t *si = &emit->stack_info[emit->stack_size]; + si->vtype = vtype; + si->kind = STACK_REG; + si->data.u_reg = reg; + adjust_stack(emit, 1); +} + +static void emit_post_push_imm(emit_t *emit, vtype_kind_t vtype, mp_int_t imm) { + ensure_extra_stack(emit, 1); + stack_info_t *si = &emit->stack_info[emit->stack_size]; + si->vtype = vtype; + si->kind = STACK_IMM; + si->data.u_imm = imm; + adjust_stack(emit, 1); +} + +static void emit_post_push_reg_reg(emit_t *emit, vtype_kind_t vtypea, int rega, vtype_kind_t vtypeb, int regb) { + emit_post_push_reg(emit, vtypea, rega); + emit_post_push_reg(emit, vtypeb, regb); +} + +static void emit_post_push_reg_reg_reg(emit_t *emit, vtype_kind_t vtypea, int rega, vtype_kind_t vtypeb, int regb, vtype_kind_t vtypec, int regc) { + emit_post_push_reg(emit, vtypea, rega); + emit_post_push_reg(emit, vtypeb, regb); + emit_post_push_reg(emit, vtypec, regc); +} + +static void emit_post_push_reg_reg_reg_reg(emit_t *emit, vtype_kind_t vtypea, int rega, vtype_kind_t vtypeb, int regb, vtype_kind_t vtypec, int regc, vtype_kind_t vtyped, int regd) { + emit_post_push_reg(emit, vtypea, rega); + emit_post_push_reg(emit, vtypeb, regb); + emit_post_push_reg(emit, vtypec, regc); + emit_post_push_reg(emit, vtyped, regd); +} + +static void emit_call(emit_t *emit, mp_fun_kind_t fun_kind) { + need_reg_all(emit); + ASM_CALL_IND(emit->as, fun_kind); +} + +static void emit_call_with_imm_arg(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val, int arg_reg) { + need_reg_all(emit); + ASM_MOV_REG_IMM(emit->as, arg_reg, arg_val); + ASM_CALL_IND(emit->as, fun_kind); +} + +static void emit_call_with_2_imm_args(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val1, int arg_reg1, mp_int_t arg_val2, int arg_reg2) { + need_reg_all(emit); + ASM_MOV_REG_IMM(emit->as, arg_reg1, arg_val1); + ASM_MOV_REG_IMM(emit->as, arg_reg2, arg_val2); + ASM_CALL_IND(emit->as, fun_kind); +} + +static void emit_call_with_qstr_arg(emit_t *emit, mp_fun_kind_t fun_kind, qstr qst, int arg_reg) { + need_reg_all(emit); + emit_native_mov_reg_qstr(emit, arg_reg, qst); + ASM_CALL_IND(emit->as, fun_kind); +} + +// vtype of all n_pop objects is VTYPE_PYOBJ +// Will convert any items that are not VTYPE_PYOBJ to this type and put them back on the stack. +// If any conversions of non-immediate values are needed, then it uses REG_ARG_1, REG_ARG_2 and REG_RET. +// Otherwise, it does not use any temporary registers (but may use reg_dest before loading it with stack pointer). +static void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_dest, mp_uint_t n_pop) { + need_reg_all(emit); + + // First, store any immediate values to their respective place on the stack. + for (mp_uint_t i = 0; i < n_pop; i++) { + stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i]; + // must push any imm's to stack + // must convert them to VTYPE_PYOBJ for viper code + if (si->kind == STACK_IMM) { + si->kind = STACK_VALUE; + si->vtype = load_reg_stack_imm(emit, reg_dest, si, true); + emit_native_mov_state_reg(emit, emit->stack_start + emit->stack_size - 1 - i, reg_dest); + } + + // verify that this value is on the stack + assert(si->kind == STACK_VALUE); + } + + // Second, convert any non-VTYPE_PYOBJ to that type. + for (mp_uint_t i = 0; i < n_pop; i++) { + stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i]; + if (si->vtype != VTYPE_PYOBJ) { + mp_uint_t local_num = emit->stack_start + emit->stack_size - 1 - i; + emit_native_mov_reg_state(emit, REG_ARG_1, local_num); + emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, si->vtype, REG_ARG_2); // arg2 = type + emit_native_mov_state_reg(emit, local_num, REG_RET); + si->vtype = VTYPE_PYOBJ; + DEBUG_printf(" convert_native_to_obj(local_num=" UINT_FMT ")\n", local_num); + } + } + + // Adujust the stack for a pop of n_pop items, and load the stack pointer into reg_dest. + adjust_stack(emit, -n_pop); + emit_native_mov_reg_state_addr(emit, reg_dest, emit->stack_start + emit->stack_size); +} + +// vtype of all n_push objects is VTYPE_PYOBJ +static void emit_get_stack_pointer_to_reg_for_push(emit_t *emit, mp_uint_t reg_dest, mp_uint_t n_push) { + need_reg_all(emit); + ensure_extra_stack(emit, n_push); + for (mp_uint_t i = 0; i < n_push; i++) { + emit->stack_info[emit->stack_size + i].kind = STACK_VALUE; + emit->stack_info[emit->stack_size + i].vtype = VTYPE_PYOBJ; + } + emit_native_mov_reg_state_addr(emit, reg_dest, emit->stack_start + emit->stack_size); + adjust_stack(emit, n_push); +} + +static void emit_native_push_exc_stack(emit_t *emit, uint label, bool is_finally) { + if (emit->exc_stack_size + 1 > emit->exc_stack_alloc) { + size_t new_alloc = emit->exc_stack_alloc + 4; + emit->exc_stack = m_renew(exc_stack_entry_t, emit->exc_stack, emit->exc_stack_alloc, new_alloc); + emit->exc_stack_alloc = new_alloc; + } + + exc_stack_entry_t *e = &emit->exc_stack[emit->exc_stack_size++]; + e->label = label; + e->is_finally = is_finally; + e->unwind_label = UNWIND_LABEL_UNUSED; + e->is_active = true; + + ASM_MOV_REG_PCREL(emit->as, REG_RET, label); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_RET); +} + +static void emit_native_leave_exc_stack(emit_t *emit, bool start_of_handler) { + assert(emit->exc_stack_size > 0); + + // Get current exception handler and deactivate it + exc_stack_entry_t *e = &emit->exc_stack[emit->exc_stack_size - 1]; + e->is_active = false; + + // Find next innermost active exception handler, to restore as current handler + for (--e; e >= emit->exc_stack && !e->is_active; --e) { + } + + // Update the PC of the new exception handler + if (e < emit->exc_stack) { + // No active handler, clear handler PC to zero + if (start_of_handler) { + // Optimisation: PC is already cleared by global exc handler + return; + } + ASM_XOR_REG_REG(emit->as, REG_RET, REG_RET); + } else { + // Found new active handler, get its PC + ASM_MOV_REG_PCREL(emit->as, REG_RET, e->label); + } + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_RET); +} + +static exc_stack_entry_t *emit_native_pop_exc_stack(emit_t *emit) { + assert(emit->exc_stack_size > 0); + exc_stack_entry_t *e = &emit->exc_stack[--emit->exc_stack_size]; + assert(e->is_active == false); + return e; +} + +static void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj) { + emit->scope->scope_flags |= MP_SCOPE_FLAG_HASCONSTS; + size_t table_off = mp_emit_common_use_const_obj(emit->emit_common, obj); + emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, reg, REG_TEMP0, table_off); +} + +static void emit_load_reg_with_child(emit_t *emit, int reg, mp_raw_code_t *rc) { + size_t table_off = mp_emit_common_alloc_const_child(emit->emit_common, rc); + emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CHILD_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, reg, REG_TEMP0, table_off); +} + +static void emit_native_label_assign(emit_t *emit, mp_uint_t l) { + DEBUG_printf("label_assign(" UINT_FMT ")\n", l); + + bool is_finally = false; + if (emit->exc_stack_size > 0) { + exc_stack_entry_t *e = &emit->exc_stack[emit->exc_stack_size - 1]; + is_finally = e->is_finally && e->label == l; + } + + if (is_finally) { + // Label is at start of finally handler: store TOS into exception slot + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, REG_TEMP0); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + } + + emit_native_pre(emit); + // need to commit stack because we can jump here from elsewhere + need_stack_settled(emit); + mp_asm_base_label_assign(&emit->as->base, l); + emit_post(emit); + + if (is_finally) { + // Label is at start of finally handler: pop exception stack + emit_native_leave_exc_stack(emit, false); + } +} + +static void emit_native_global_exc_entry(emit_t *emit) { + // Note: 4 labels are reserved for this function, starting at *emit->label_slot + + emit->exit_label = *emit->label_slot; + + if (NEED_GLOBAL_EXC_HANDLER(emit)) { + mp_uint_t nlr_label = *emit->label_slot + 1; + mp_uint_t start_label = *emit->label_slot + 2; + mp_uint_t global_except_label = *emit->label_slot + 3; + + if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) { + // Set new globals + emit_native_mov_reg_state(emit, REG_ARG_1, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, OFFSETOF_MODULE_CONTEXT_GLOBALS); + emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS); + + // Save old globals (or NULL if globals didn't change) + emit_native_mov_state_reg(emit, LOCAL_IDX_OLD_GLOBALS(emit), REG_RET); + } + + if (emit->scope->exc_stack_size == 0) { + if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) { + // Optimisation: if globals didn't change don't push the nlr context + ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, start_label, false); + } + + // Wrap everything in an nlr context + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 0); + emit_call(emit, MP_F_NLR_PUSH); + #if N_NLR_SETJMP + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 2); + emit_call(emit, MP_F_SETJMP); + #endif + ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, start_label, true); + } else { + // Clear the unwind state + ASM_XOR_REG_REG(emit->as, REG_TEMP0, REG_TEMP0); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_TEMP0); + + // Put PC of start code block into REG_LOCAL_1 + ASM_MOV_REG_PCREL(emit->as, REG_LOCAL_1, start_label); + + // Wrap everything in an nlr context + emit_native_label_assign(emit, nlr_label); + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 0); + emit_call(emit, MP_F_NLR_PUSH); + #if N_NLR_SETJMP + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 2); + emit_call(emit, MP_F_SETJMP); + #endif + ASM_JUMP_IF_REG_NONZERO(emit->as, REG_RET, global_except_label, true); + + // Clear PC of current code block, and jump there to resume execution + ASM_XOR_REG_REG(emit->as, REG_TEMP0, REG_TEMP0); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_TEMP0); + ASM_JUMP_REG(emit->as, REG_LOCAL_1); + + // Global exception handler: check for valid exception handler + emit_native_label_assign(emit, global_except_label); + ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_1, LOCAL_IDX_EXC_HANDLER_PC(emit)); + ASM_JUMP_IF_REG_NONZERO(emit->as, REG_LOCAL_1, nlr_label, false); + } + + if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) { + // Restore old globals + emit_native_mov_reg_state(emit, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit)); + emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS); + } + + if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + // Store return value in state[0] + ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, LOCAL_IDX_EXC_VAL(emit)); + ASM_STORE_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, OFFSETOF_CODE_STATE_STATE); + + // Load return kind + ASM_MOV_REG_IMM(emit->as, REG_PARENT_RET, MP_VM_RETURN_EXCEPTION); + + ASM_EXIT(emit->as); + } else { + // Re-raise exception out to caller + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); + emit_call(emit, MP_F_NATIVE_RAISE); + } + + // Label for start of function + emit_native_label_assign(emit, start_label); + + if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_GEN_PC(emit)); + ASM_JUMP_REG(emit->as, REG_TEMP0); + emit->start_offset = mp_asm_base_get_code_pos(&emit->as->base); + + // This is the first entry of the generator + + // Check LOCAL_IDX_EXC_VAL for any injected value + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); + emit_call(emit, MP_F_NATIVE_RAISE); + } + } +} + +static void emit_native_global_exc_exit(emit_t *emit) { + // Label for end of function + emit_native_label_assign(emit, emit->exit_label); + + if (NEED_GLOBAL_EXC_HANDLER(emit)) { + // Get old globals + if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) { + emit_native_mov_reg_state(emit, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit)); + + if (emit->scope->exc_stack_size == 0) { + // Optimisation: if globals didn't change then don't restore them and don't do nlr_pop + ASM_JUMP_IF_REG_ZERO(emit->as, REG_ARG_1, emit->exit_label + 1, false); + } + + // Restore old globals + emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS); + } + + // Pop the nlr context + emit_call(emit, MP_F_NLR_POP); + + if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) { + if (emit->scope->exc_stack_size == 0) { + // Destination label for above optimisation + emit_native_label_assign(emit, emit->exit_label + 1); + } + } + + // Load return value + ASM_MOV_REG_LOCAL(emit->as, REG_PARENT_RET, LOCAL_IDX_RET_VAL(emit)); + } + + ASM_EXIT(emit->as); +} + +static void emit_native_import_name(emit_t *emit, qstr qst) { + DEBUG_printf("import_name %s\n", qstr_str(qst)); + + // get arguments from stack: arg2 = fromlist, arg3 = level + // If using viper types these arguments must be converted to proper objects, and + // to accomplish this viper types are turned off for the emit_pre_pop_reg_reg call. + bool orig_do_viper_types = emit->do_viper_types; + emit->do_viper_types = false; + vtype_kind_t vtype_fromlist; + vtype_kind_t vtype_level; + emit_pre_pop_reg_reg(emit, &vtype_fromlist, REG_ARG_2, &vtype_level, REG_ARG_3); + assert(vtype_fromlist == VTYPE_PYOBJ); + assert(vtype_level == VTYPE_PYOBJ); + emit->do_viper_types = orig_do_viper_types; + + emit_call_with_qstr_arg(emit, MP_F_IMPORT_NAME, qst, REG_ARG_1); // arg1 = import name + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_import_from(emit_t *emit, qstr qst) { + DEBUG_printf("import_from %s\n", qstr_str(qst)); + emit_native_pre(emit); + vtype_kind_t vtype_module; + emit_access_stack(emit, 1, &vtype_module, REG_ARG_1); // arg1 = module + assert(vtype_module == VTYPE_PYOBJ); + emit_call_with_qstr_arg(emit, MP_F_IMPORT_FROM, qst, REG_ARG_2); // arg2 = import name + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_import_star(emit_t *emit) { + DEBUG_printf("import_star\n"); + vtype_kind_t vtype_module; + emit_pre_pop_reg(emit, &vtype_module, REG_ARG_1); // arg1 = module + assert(vtype_module == VTYPE_PYOBJ); + emit_call(emit, MP_F_IMPORT_ALL); + emit_post(emit); +} + +static void emit_native_import(emit_t *emit, qstr qst, int kind) { + if (kind == MP_EMIT_IMPORT_NAME) { + emit_native_import_name(emit, qst); + } else if (kind == MP_EMIT_IMPORT_FROM) { + emit_native_import_from(emit, qst); + } else { + emit_native_import_star(emit); + } +} + +static void emit_native_load_const_tok(emit_t *emit, mp_token_kind_t tok) { + DEBUG_printf("load_const_tok(tok=%u)\n", tok); + if (tok == MP_TOKEN_ELLIPSIS) { + emit_native_load_const_obj(emit, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); + } else { + emit_native_pre(emit); + if (tok == MP_TOKEN_KW_NONE) { + emit_post_push_imm(emit, VTYPE_PTR_NONE, 0); + } else { + emit_post_push_imm(emit, VTYPE_BOOL, tok == MP_TOKEN_KW_FALSE ? 0 : 1); + } + } +} + +static void emit_native_load_const_small_int(emit_t *emit, mp_int_t arg) { + DEBUG_printf("load_const_small_int(int=" INT_FMT ")\n", arg); + emit_native_pre(emit); + emit_post_push_imm(emit, VTYPE_INT, arg); +} + +static void emit_native_load_const_str(emit_t *emit, qstr qst) { + emit_native_pre(emit); + // TODO: Eventually we want to be able to work with raw pointers in viper to + // do native array access. For now we just load them as any other object. + /* + if (emit->do_viper_types) { + // load a pointer to the asciiz string? + emit_post_push_imm(emit, VTYPE_PTR, (mp_uint_t)qstr_str(qst)); + } else + */ + { + need_reg_single(emit, REG_TEMP0, 0); + emit_native_mov_reg_qstr_obj(emit, REG_TEMP0, qst); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_TEMP0); + } +} + +static void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj) { + emit_native_pre(emit); + need_reg_single(emit, REG_RET, 0); + emit_load_reg_with_object(emit, REG_RET, obj); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_load_null(emit_t *emit) { + emit_native_pre(emit); + emit_post_push_imm(emit, VTYPE_PYOBJ, 0); +} + +static void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) { + DEBUG_printf("load_fast(%s, " UINT_FMT ")\n", qstr_str(qst), local_num); + vtype_kind_t vtype = emit->local_vtype[local_num]; + if (vtype == VTYPE_UNBOUND) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, MP_ERROR_TEXT("local '%q' used before type known"), qst); + } + emit_native_pre(emit); + if (local_num < MAX_REGS_FOR_LOCAL_VARS && CAN_USE_REGS_FOR_LOCALS(emit)) { + emit_post_push_reg(emit, vtype, reg_local_table[local_num]); + } else { + need_reg_single(emit, REG_TEMP0, 0); + emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_LOCAL_VAR(emit, local_num)); + emit_post_push_reg(emit, vtype, REG_TEMP0); + } +} + +static void emit_native_load_deref(emit_t *emit, qstr qst, mp_uint_t local_num) { + DEBUG_printf("load_deref(%s, " UINT_FMT ")\n", qstr_str(qst), local_num); + need_reg_single(emit, REG_RET, 0); + emit_native_load_fast(emit, qst, local_num); + vtype_kind_t vtype; + int reg_base = REG_RET; + emit_pre_pop_reg_flexible(emit, &vtype, ®_base, -1, -1); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_RET, reg_base, 1); + // closed over vars are always Python objects + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { + if (kind == MP_EMIT_IDOP_LOCAL_FAST) { + emit_native_load_fast(emit, qst, local_num); + } else { + emit_native_load_deref(emit, qst, local_num); + } +} + +static void emit_native_load_global(emit_t *emit, qstr qst, int kind) { + MP_STATIC_ASSERT(MP_F_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_F_LOAD_NAME); + MP_STATIC_ASSERT(MP_F_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_F_LOAD_GLOBAL); + emit_native_pre(emit); + if (kind == MP_EMIT_IDOP_GLOBAL_NAME) { + DEBUG_printf("load_name(%s)\n", qstr_str(qst)); + } else { + DEBUG_printf("load_global(%s)\n", qstr_str(qst)); + if (emit->do_viper_types) { + // check for builtin casting operators + int native_type = mp_native_type_from_qstr(qst); + if (native_type >= MP_NATIVE_TYPE_BOOL) { + emit_post_push_imm(emit, VTYPE_BUILTIN_CAST, native_type); + return; + } + } + } + emit_call_with_qstr_arg(emit, MP_F_LOAD_NAME + kind, qst, REG_ARG_1); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_load_attr(emit_t *emit, qstr qst) { + // depends on type of subject: + // - integer, function, pointer to integers: error + // - pointer to structure: get member, quite easy + // - Python object: call mp_load_attr, and needs to be typed to convert result + vtype_kind_t vtype_base; + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base + assert(vtype_base == VTYPE_PYOBJ); + emit_call_with_qstr_arg(emit, MP_F_LOAD_ATTR, qst, REG_ARG_2); // arg2 = attribute name + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_load_method(emit_t *emit, qstr qst, bool is_super) { + if (is_super) { + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_2, 3); // arg2 = dest ptr + emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_2, 2); // arg2 = dest ptr + emit_call_with_qstr_arg(emit, MP_F_LOAD_SUPER_METHOD, qst, REG_ARG_1); // arg1 = method name + } else { + vtype_kind_t vtype_base; + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base + assert(vtype_base == VTYPE_PYOBJ); + emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr + emit_call_with_qstr_arg(emit, MP_F_LOAD_METHOD, qst, REG_ARG_2); // arg2 = method name + } +} + +static void emit_native_load_build_class(emit_t *emit) { + emit_native_pre(emit); + emit_call(emit, MP_F_LOAD_BUILD_CLASS); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_load_subscr(emit_t *emit) { + DEBUG_printf("load_subscr\n"); + // need to compile: base[index] + + // pop: index, base + // optimise case where index is an immediate + vtype_kind_t vtype_base = peek_vtype(emit, 1); + + if (vtype_base == VTYPE_PYOBJ) { + // standard Python subscr + // TODO factor this implicit cast code with other uses of it + vtype_kind_t vtype_index = peek_vtype(emit, 0); + if (vtype_index == VTYPE_PYOBJ) { + emit_pre_pop_reg(emit, &vtype_index, REG_ARG_2); + } else { + emit_pre_pop_reg(emit, &vtype_index, REG_ARG_1); + emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, vtype_index, REG_ARG_2); // arg2 = type + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_RET); + } + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); + emit_call_with_imm_arg(emit, MP_F_OBJ_SUBSCR, (mp_uint_t)MP_OBJ_SENTINEL, REG_ARG_3); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } else { + // viper load + // TODO The different machine architectures have very different + // capabilities and requirements for loads, so probably best to + // write a completely separate load-optimiser for each one. + stack_info_t *top = peek_stack(emit, 0); + if (top->vtype == VTYPE_INT && top->kind == STACK_IMM) { + // index is an immediate + mp_int_t index_value = top->data.u_imm; + emit_pre_pop_discard(emit); // discard index + int reg_base = REG_ARG_1; + int reg_index = REG_ARG_2; + emit_pre_pop_reg_flexible(emit, &vtype_base, ®_base, reg_index, reg_index); + need_reg_single(emit, REG_RET, 0); + switch (vtype_base) { + case VTYPE_PTR8: { + // pointer to 8-bit memory + // TODO optimise to use thumb ldrb r1, [r2, r3] + if (index_value != 0) { + // index is non-zero + #if N_THUMB + if (index_value > 0 && index_value < 32) { + asm_thumb_ldrb_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); + break; + } + #endif + need_reg_single(emit, reg_index, 0); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value); + ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base + reg_base = reg_index; + } + ASM_LOAD8_REG_REG(emit->as, REG_RET, reg_base); // load from (base+index) + break; + } + case VTYPE_PTR16: { + // pointer to 16-bit memory + if (index_value != 0) { + // index is a non-zero immediate + #if N_THUMB + if (index_value > 0 && index_value < 32) { + asm_thumb_ldrh_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); + break; + } + #endif + need_reg_single(emit, reg_index, 0); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); + ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base + reg_base = reg_index; + } + ASM_LOAD16_REG_REG(emit->as, REG_RET, reg_base); // load from (base+2*index) + break; + } + case VTYPE_PTR32: { + // pointer to 32-bit memory + if (index_value != 0) { + // index is a non-zero immediate + #if N_THUMB + if (index_value > 0 && index_value < 32) { + asm_thumb_ldr_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); + break; + } + #endif + need_reg_single(emit, reg_index, 0); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); + ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base + reg_base = reg_index; + } + ASM_LOAD32_REG_REG(emit->as, REG_RET, reg_base); // load from (base+4*index) + break; + } + default: + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't load from '%q'"), vtype_to_qstr(vtype_base)); + } + } else { + // index is not an immediate + vtype_kind_t vtype_index; + int reg_index = REG_ARG_2; + emit_pre_pop_reg_flexible(emit, &vtype_index, ®_index, REG_ARG_1, REG_ARG_1); + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); + need_reg_single(emit, REG_RET, 0); + if (vtype_index != VTYPE_INT && vtype_index != VTYPE_UINT) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't load with '%q' index"), vtype_to_qstr(vtype_index)); + } + switch (vtype_base) { + case VTYPE_PTR8: { + // pointer to 8-bit memory + // TODO optimise to use thumb ldrb r1, [r2, r3] + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_LOAD8_REG_REG(emit->as, REG_RET, REG_ARG_1); // store value to (base+index) + break; + } + case VTYPE_PTR16: { + // pointer to 16-bit memory + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_LOAD16_REG_REG(emit->as, REG_RET, REG_ARG_1); // load from (base+2*index) + break; + } + case VTYPE_PTR32: { + // pointer to word-size memory + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_LOAD32_REG_REG(emit->as, REG_RET, REG_ARG_1); // load from (base+4*index) + break; + } + default: + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't load from '%q'"), vtype_to_qstr(vtype_base)); + } + } + emit_post_push_reg(emit, VTYPE_INT, REG_RET); + } +} + +static void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num) { + vtype_kind_t vtype; + if (local_num < MAX_REGS_FOR_LOCAL_VARS && CAN_USE_REGS_FOR_LOCALS(emit)) { + emit_pre_pop_reg(emit, &vtype, reg_local_table[local_num]); + } else { + emit_pre_pop_reg(emit, &vtype, REG_TEMP0); + emit_native_mov_state_reg(emit, LOCAL_IDX_LOCAL_VAR(emit, local_num), REG_TEMP0); + } + emit_post(emit); + + // check types + if (emit->local_vtype[local_num] == VTYPE_UNBOUND) { + // first time this local is assigned, so give it a type of the object stored in it + emit->local_vtype[local_num] = vtype; + } else if (emit->local_vtype[local_num] != vtype) { + // type of local is not the same as object stored in it + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("local '%q' has type '%q' but source is '%q'"), + qst, vtype_to_qstr(emit->local_vtype[local_num]), vtype_to_qstr(vtype)); + } +} + +static void emit_native_store_deref(emit_t *emit, qstr qst, mp_uint_t local_num) { + DEBUG_printf("store_deref(%s, " UINT_FMT ")\n", qstr_str(qst), local_num); + need_reg_single(emit, REG_TEMP0, 0); + need_reg_single(emit, REG_TEMP1, 0); + emit_native_load_fast(emit, qst, local_num); + vtype_kind_t vtype; + int reg_base = REG_TEMP0; + emit_pre_pop_reg_flexible(emit, &vtype, ®_base, -1, -1); + int reg_src = REG_TEMP1; + emit_pre_pop_reg_flexible(emit, &vtype, ®_src, reg_base, reg_base); + ASM_STORE_REG_REG_OFFSET(emit->as, reg_src, reg_base, 1); + emit_post(emit); +} + +static void emit_native_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { + if (kind == MP_EMIT_IDOP_LOCAL_FAST) { + emit_native_store_fast(emit, qst, local_num); + } else { + emit_native_store_deref(emit, qst, local_num); + } +} + +static void emit_native_store_global(emit_t *emit, qstr qst, int kind) { + MP_STATIC_ASSERT(MP_F_STORE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_F_STORE_NAME); + MP_STATIC_ASSERT(MP_F_STORE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_F_STORE_GLOBAL); + if (kind == MP_EMIT_IDOP_GLOBAL_NAME) { + // mp_store_name, but needs conversion of object (maybe have mp_viper_store_name(obj, type)) + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, REG_ARG_2); + assert(vtype == VTYPE_PYOBJ); + } else { + vtype_kind_t vtype = peek_vtype(emit, 0); + if (vtype == VTYPE_PYOBJ) { + emit_pre_pop_reg(emit, &vtype, REG_ARG_2); + } else { + emit_pre_pop_reg(emit, &vtype, REG_ARG_1); + emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, vtype, REG_ARG_2); // arg2 = type + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_RET); + } + } + emit_call_with_qstr_arg(emit, MP_F_STORE_NAME + kind, qst, REG_ARG_1); // arg1 = name + emit_post(emit); +} + +static void emit_native_store_attr(emit_t *emit, qstr qst) { + vtype_kind_t vtype_base; + vtype_kind_t vtype_val = peek_vtype(emit, 1); + if (vtype_val == VTYPE_PYOBJ) { + emit_pre_pop_reg_reg(emit, &vtype_base, REG_ARG_1, &vtype_val, REG_ARG_3); // arg1 = base, arg3 = value + } else { + emit_access_stack(emit, 2, &vtype_val, REG_ARG_1); // arg1 = value + emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, vtype_val, REG_ARG_2); // arg2 = type + ASM_MOV_REG_REG(emit->as, REG_ARG_3, REG_RET); // arg3 = value (converted) + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base + adjust_stack(emit, -1); // pop value + } + assert(vtype_base == VTYPE_PYOBJ); + emit_call_with_qstr_arg(emit, MP_F_STORE_ATTR, qst, REG_ARG_2); // arg2 = attribute name + emit_post(emit); +} + +static void emit_native_store_subscr(emit_t *emit) { + DEBUG_printf("store_subscr\n"); + // need to compile: base[index] = value + + // pop: index, base, value + // optimise case where index is an immediate + vtype_kind_t vtype_base = peek_vtype(emit, 1); + + if (vtype_base == VTYPE_PYOBJ) { + // standard Python subscr + vtype_kind_t vtype_index = peek_vtype(emit, 0); + vtype_kind_t vtype_value = peek_vtype(emit, 2); + if (vtype_index != VTYPE_PYOBJ || vtype_value != VTYPE_PYOBJ) { + // need to implicitly convert non-objects to objects + // TODO do this properly + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_1, 3); + adjust_stack(emit, 3); + } + emit_pre_pop_reg_reg_reg(emit, &vtype_index, REG_ARG_2, &vtype_base, REG_ARG_1, &vtype_value, REG_ARG_3); + emit_call(emit, MP_F_OBJ_SUBSCR); + } else { + // viper store + // TODO The different machine architectures have very different + // capabilities and requirements for stores, so probably best to + // write a completely separate store-optimiser for each one. + stack_info_t *top = peek_stack(emit, 0); + if (top->vtype == VTYPE_INT && top->kind == STACK_IMM) { + // index is an immediate + mp_int_t index_value = top->data.u_imm; + emit_pre_pop_discard(emit); // discard index + vtype_kind_t vtype_value; + int reg_base = REG_ARG_1; + int reg_index = REG_ARG_2; + int reg_value = REG_ARG_3; + emit_pre_pop_reg_flexible(emit, &vtype_base, ®_base, reg_index, reg_value); + #if N_X64 || N_X86 + // special case: x86 needs byte stores to be from lower 4 regs (REG_ARG_3 is EDX) + emit_pre_pop_reg(emit, &vtype_value, reg_value); + #else + emit_pre_pop_reg_flexible(emit, &vtype_value, ®_value, reg_base, reg_index); + #endif + if (vtype_value != VTYPE_BOOL && vtype_value != VTYPE_INT && vtype_value != VTYPE_UINT) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't store '%q'"), vtype_to_qstr(vtype_value)); + } + switch (vtype_base) { + case VTYPE_PTR8: { + // pointer to 8-bit memory + // TODO optimise to use thumb strb r1, [r2, r3] + if (index_value != 0) { + // index is non-zero + #if N_THUMB + if (index_value > 0 && index_value < 32) { + asm_thumb_strb_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); + break; + } + #endif + ASM_MOV_REG_IMM(emit->as, reg_index, index_value); + #if N_ARM + asm_arm_strb_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); + return; + #endif + ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base + reg_base = reg_index; + } + ASM_STORE8_REG_REG(emit->as, reg_value, reg_base); // store value to (base+index) + break; + } + case VTYPE_PTR16: { + // pointer to 16-bit memory + if (index_value != 0) { + // index is a non-zero immediate + #if N_THUMB + if (index_value > 0 && index_value < 32) { + asm_thumb_strh_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); + break; + } + #endif + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); + ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base + reg_base = reg_index; + } + ASM_STORE16_REG_REG(emit->as, reg_value, reg_base); // store value to (base+2*index) + break; + } + case VTYPE_PTR32: { + // pointer to 32-bit memory + if (index_value != 0) { + // index is a non-zero immediate + #if N_THUMB + if (index_value > 0 && index_value < 32) { + asm_thumb_str_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); + break; + } + #endif + #if N_ARM + ASM_MOV_REG_IMM(emit->as, reg_index, index_value); + asm_arm_str_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); + return; + #endif + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); + ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base + reg_base = reg_index; + } + ASM_STORE32_REG_REG(emit->as, reg_value, reg_base); // store value to (base+4*index) + break; + } + default: + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't store to '%q'"), vtype_to_qstr(vtype_base)); + } + } else { + // index is not an immediate + vtype_kind_t vtype_index, vtype_value; + int reg_index = REG_ARG_2; + int reg_value = REG_ARG_3; + emit_pre_pop_reg_flexible(emit, &vtype_index, ®_index, REG_ARG_1, reg_value); + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); + if (vtype_index != VTYPE_INT && vtype_index != VTYPE_UINT) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't store with '%q' index"), vtype_to_qstr(vtype_index)); + } + #if N_X64 || N_X86 + // special case: x86 needs byte stores to be from lower 4 regs (REG_ARG_3 is EDX) + emit_pre_pop_reg(emit, &vtype_value, reg_value); + #else + emit_pre_pop_reg_flexible(emit, &vtype_value, ®_value, REG_ARG_1, reg_index); + #endif + if (vtype_value != VTYPE_BOOL && vtype_value != VTYPE_INT && vtype_value != VTYPE_UINT) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't store '%q'"), vtype_to_qstr(vtype_value)); + } + switch (vtype_base) { + case VTYPE_PTR8: { + // pointer to 8-bit memory + // TODO optimise to use thumb strb r1, [r2, r3] + #if N_ARM + asm_arm_strb_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); + break; + #endif + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_STORE8_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+index) + break; + } + case VTYPE_PTR16: { + // pointer to 16-bit memory + #if N_ARM + asm_arm_strh_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); + break; + #endif + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_STORE16_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+2*index) + break; + } + case VTYPE_PTR32: { + // pointer to 32-bit memory + #if N_ARM + asm_arm_str_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); + break; + #endif + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base + ASM_STORE32_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+4*index) + break; + } + default: + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't store to '%q'"), vtype_to_qstr(vtype_base)); + } + } + + } +} + +static void emit_native_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { + if (kind == MP_EMIT_IDOP_LOCAL_FAST) { + // TODO: This is not compliant implementation. We could use MP_OBJ_SENTINEL + // to mark deleted vars but then every var would need to be checked on + // each access. Very inefficient, so just set value to None to enable GC. + emit_native_load_const_tok(emit, MP_TOKEN_KW_NONE); + emit_native_store_fast(emit, qst, local_num); + } else { + // TODO implement me! + } +} + +static void emit_native_delete_global(emit_t *emit, qstr qst, int kind) { + MP_STATIC_ASSERT(MP_F_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_F_DELETE_NAME); + MP_STATIC_ASSERT(MP_F_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_F_DELETE_GLOBAL); + emit_native_pre(emit); + emit_call_with_qstr_arg(emit, MP_F_DELETE_NAME + kind, qst, REG_ARG_1); + emit_post(emit); +} + +static void emit_native_delete_attr(emit_t *emit, qstr qst) { + vtype_kind_t vtype_base; + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base + assert(vtype_base == VTYPE_PYOBJ); + ASM_XOR_REG_REG(emit->as, REG_ARG_3, REG_ARG_3); // arg3 = value (null for delete) + emit_call_with_qstr_arg(emit, MP_F_STORE_ATTR, qst, REG_ARG_2); // arg2 = attribute name + emit_post(emit); +} + +static void emit_native_delete_subscr(emit_t *emit) { + vtype_kind_t vtype_index, vtype_base; + emit_pre_pop_reg_reg(emit, &vtype_index, REG_ARG_2, &vtype_base, REG_ARG_1); // index, base + assert(vtype_index == VTYPE_PYOBJ); + assert(vtype_base == VTYPE_PYOBJ); + emit_call_with_imm_arg(emit, MP_F_OBJ_SUBSCR, (mp_uint_t)MP_OBJ_NULL, REG_ARG_3); +} + +static void emit_native_subscr(emit_t *emit, int kind) { + if (kind == MP_EMIT_SUBSCR_LOAD) { + emit_native_load_subscr(emit); + } else if (kind == MP_EMIT_SUBSCR_STORE) { + emit_native_store_subscr(emit); + } else { + emit_native_delete_subscr(emit); + } +} + +static void emit_native_attr(emit_t *emit, qstr qst, int kind) { + if (kind == MP_EMIT_ATTR_LOAD) { + emit_native_load_attr(emit, qst); + } else if (kind == MP_EMIT_ATTR_STORE) { + emit_native_store_attr(emit, qst); + } else { + emit_native_delete_attr(emit, qst); + } +} + +static void emit_native_dup_top(emit_t *emit) { + DEBUG_printf("dup_top\n"); + vtype_kind_t vtype; + int reg = REG_TEMP0; + emit_pre_pop_reg_flexible(emit, &vtype, ®, -1, -1); + emit_post_push_reg_reg(emit, vtype, reg, vtype, reg); +} + +static void emit_native_dup_top_two(emit_t *emit) { + vtype_kind_t vtype0, vtype1; + emit_pre_pop_reg_reg(emit, &vtype0, REG_TEMP0, &vtype1, REG_TEMP1); + emit_post_push_reg_reg_reg_reg(emit, vtype1, REG_TEMP1, vtype0, REG_TEMP0, vtype1, REG_TEMP1, vtype0, REG_TEMP0); +} + +static void emit_native_pop_top(emit_t *emit) { + DEBUG_printf("pop_top\n"); + emit_pre_pop_discard(emit); + emit_post(emit); +} + +static void emit_native_rot_two(emit_t *emit) { + DEBUG_printf("rot_two\n"); + vtype_kind_t vtype0, vtype1; + emit_pre_pop_reg_reg(emit, &vtype0, REG_TEMP0, &vtype1, REG_TEMP1); + emit_post_push_reg_reg(emit, vtype0, REG_TEMP0, vtype1, REG_TEMP1); +} + +static void emit_native_rot_three(emit_t *emit) { + DEBUG_printf("rot_three\n"); + vtype_kind_t vtype0, vtype1, vtype2; + emit_pre_pop_reg_reg_reg(emit, &vtype0, REG_TEMP0, &vtype1, REG_TEMP1, &vtype2, REG_TEMP2); + emit_post_push_reg_reg_reg(emit, vtype0, REG_TEMP0, vtype2, REG_TEMP2, vtype1, REG_TEMP1); +} + +static void emit_native_jump(emit_t *emit, mp_uint_t label) { + DEBUG_printf("jump(label=" UINT_FMT ")\n", label); + emit_native_pre(emit); + // need to commit stack because we are jumping elsewhere + need_stack_settled(emit); + ASM_JUMP(emit->as, label); + emit_post(emit); + mp_asm_base_suppress_code(&emit->as->base); +} + +static void emit_native_jump_helper(emit_t *emit, bool cond, mp_uint_t label, bool pop) { + vtype_kind_t vtype = peek_vtype(emit, 0); + if (vtype == VTYPE_PYOBJ) { + emit_pre_pop_reg(emit, &vtype, REG_ARG_1); + if (!pop) { + adjust_stack(emit, 1); + } + emit_call(emit, MP_F_OBJ_IS_TRUE); + } else { + emit_pre_pop_reg(emit, &vtype, REG_RET); + if (!pop) { + adjust_stack(emit, 1); + } + if (!(vtype == VTYPE_BOOL || vtype == VTYPE_INT || vtype == VTYPE_UINT)) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't implicitly convert '%q' to 'bool'"), vtype_to_qstr(vtype)); + } + } + // For non-pop need to save the vtype so that emit_native_adjust_stack_size + // can use it. This is a bit of a hack. + if (!pop) { + emit->saved_stack_vtype = vtype; + } + // need to commit stack because we may jump elsewhere + need_stack_settled(emit); + // Emit the jump + if (cond) { + ASM_JUMP_IF_REG_NONZERO(emit->as, REG_RET, label, vtype == VTYPE_PYOBJ); + } else { + ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, label, vtype == VTYPE_PYOBJ); + } + if (!pop) { + adjust_stack(emit, -1); + } + emit_post(emit); +} + +static void emit_native_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) { + DEBUG_printf("pop_jump_if(cond=%u, label=" UINT_FMT ")\n", cond, label); + emit_native_jump_helper(emit, cond, label, true); +} + +static void emit_native_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label) { + DEBUG_printf("jump_if_or_pop(cond=%u, label=" UINT_FMT ")\n", cond, label); + emit_native_jump_helper(emit, cond, label, false); +} + +static void emit_native_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth) { + if (except_depth > 0) { + exc_stack_entry_t *first_finally = NULL; + exc_stack_entry_t *prev_finally = NULL; + exc_stack_entry_t *e = &emit->exc_stack[emit->exc_stack_size - 1]; + for (; except_depth > 0; --except_depth, --e) { + if (e->is_finally && e->is_active) { + // Found an active finally handler + if (first_finally == NULL) { + first_finally = e; + } + if (prev_finally != NULL) { + // Mark prev finally as needed to unwind a jump + prev_finally->unwind_label = e->label; + } + prev_finally = e; + } + } + if (prev_finally == NULL) { + // No finally, handle the jump ourselves + // First, restore the exception handler address for the jump + if (e < emit->exc_stack) { + ASM_XOR_REG_REG(emit->as, REG_RET, REG_RET); + } else { + ASM_MOV_REG_PCREL(emit->as, REG_RET, e->label); + } + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_RET); + } else { + // Last finally should do our jump for us + // Mark finally as needing to decide the type of jump + prev_finally->unwind_label = UNWIND_LABEL_DO_FINAL_UNWIND; + ASM_MOV_REG_PCREL(emit->as, REG_RET, label & ~MP_EMIT_BREAK_FROM_FOR); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_RET); + // Cancel any active exception (see also emit_native_pop_except_jump) + ASM_MOV_REG_IMM(emit->as, REG_RET, (mp_uint_t)MP_OBJ_NULL); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_RET); + // Jump to the innermost active finally + label = first_finally->label; + } + } + emit_native_jump(emit, label & ~MP_EMIT_BREAK_FROM_FOR); +} + +static void emit_native_setup_with(emit_t *emit, mp_uint_t label) { + // the context manager is on the top of the stack + // stack: (..., ctx_mgr) + + // get __exit__ method + vtype_kind_t vtype; + emit_access_stack(emit, 1, &vtype, REG_ARG_1); // arg1 = ctx_mgr + assert(vtype == VTYPE_PYOBJ); + emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr + emit_call_with_qstr_arg(emit, MP_F_LOAD_METHOD, MP_QSTR___exit__, REG_ARG_2); + // stack: (..., ctx_mgr, __exit__, self) + + emit_pre_pop_reg(emit, &vtype, REG_ARG_3); // self + emit_pre_pop_reg(emit, &vtype, REG_ARG_2); // __exit__ + emit_pre_pop_reg(emit, &vtype, REG_ARG_1); // ctx_mgr + emit_post_push_reg(emit, vtype, REG_ARG_2); // __exit__ + emit_post_push_reg(emit, vtype, REG_ARG_3); // self + // stack: (..., __exit__, self) + // REG_ARG_1=ctx_mgr + + // get __enter__ method + emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr + emit_call_with_qstr_arg(emit, MP_F_LOAD_METHOD, MP_QSTR___enter__, REG_ARG_2); // arg2 = method name + // stack: (..., __exit__, self, __enter__, self) + + // call __enter__ method + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 2); // pointer to items, including meth and self + emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW, 0, REG_ARG_1, 0, REG_ARG_2); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); // push return value of __enter__ + // stack: (..., __exit__, self, as_value) + + // need to commit stack because we may jump elsewhere + need_stack_settled(emit); + emit_native_push_exc_stack(emit, label, true); + + emit_native_dup_top(emit); + // stack: (..., __exit__, self, as_value, as_value) +} + +static void emit_native_setup_block(emit_t *emit, mp_uint_t label, int kind) { + if (kind == MP_EMIT_SETUP_BLOCK_WITH) { + emit_native_setup_with(emit, label); + } else { + // Set up except and finally + emit_native_pre(emit); + need_stack_settled(emit); + emit_native_push_exc_stack(emit, label, kind == MP_EMIT_SETUP_BLOCK_FINALLY); + emit_post(emit); + } +} + +static void emit_native_with_cleanup(emit_t *emit, mp_uint_t label) { + // Note: 3 labels are reserved for this function, starting at *emit->label_slot + + // stack: (..., __exit__, self, as_value) + emit_native_pre(emit); + emit_native_leave_exc_stack(emit, false); + adjust_stack(emit, -1); + // stack: (..., __exit__, self) + + // Label for case where __exit__ is called from an unwind jump + emit_native_label_assign(emit, *emit->label_slot + 2); + + // call __exit__ + emit_post_push_imm(emit, VTYPE_PTR_NONE, 0); + emit_post_push_imm(emit, VTYPE_PTR_NONE, 0); + emit_post_push_imm(emit, VTYPE_PTR_NONE, 0); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 5); + emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW, 3, REG_ARG_1, 0, REG_ARG_2); + + // Replace exc with None and finish + emit_native_jump(emit, *emit->label_slot); + + // nlr_catch + // Don't use emit_native_label_assign because this isn't a real finally label + mp_asm_base_label_assign(&emit->as->base, label); + + // Leave with's exception handler + emit_native_leave_exc_stack(emit, true); + + // Adjust stack counter for: __exit__, self (implicitly discard as_value which is above self) + emit_native_adjust_stack_size(emit, 2); + // stack: (..., __exit__, self) + + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); // get exc + + // Check if exc is MP_OBJ_NULL (i.e. zero) and jump to non-exc handler if it is + ASM_JUMP_IF_REG_ZERO(emit->as, REG_ARG_1, *emit->label_slot + 2, false); + + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_2, REG_ARG_1, 0); // get type(exc) + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_2); // push type(exc) + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_1); // push exc value + emit_post_push_imm(emit, VTYPE_PTR_NONE, 0); // traceback info + // Stack: (..., __exit__, self, type(exc), exc, traceback) + + // call __exit__ method + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 5); + emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW, 3, REG_ARG_1, 0, REG_ARG_2); + // Stack: (...) + + // If REG_RET is true then we need to replace exception with None (swallow exception) + if (REG_ARG_1 != REG_RET) { + ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_RET); + } + emit_call(emit, MP_F_OBJ_IS_TRUE); + ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, *emit->label_slot + 1, true); + + // Replace exception with MP_OBJ_NULL. + emit_native_label_assign(emit, *emit->label_slot); + ASM_MOV_REG_IMM(emit->as, REG_TEMP0, (mp_uint_t)MP_OBJ_NULL); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + + // end of with cleanup nlr_catch block + emit_native_label_assign(emit, *emit->label_slot + 1); + + // Exception is in nlr_buf.ret_val slot +} + +static void emit_native_end_finally(emit_t *emit) { + // logic: + // exc = pop_stack + // if exc == None: pass + // else: raise exc + // the check if exc is None is done in the MP_F_NATIVE_RAISE stub + emit_native_pre(emit); + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); + emit_call(emit, MP_F_NATIVE_RAISE); + + // Get state for this finally and see if we need to unwind + exc_stack_entry_t *e = emit_native_pop_exc_stack(emit); + if (e->unwind_label != UNWIND_LABEL_UNUSED) { + ASM_MOV_REG_LOCAL(emit->as, REG_RET, LOCAL_IDX_EXC_HANDLER_UNWIND(emit)); + ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, *emit->label_slot, false); + if (e->unwind_label == UNWIND_LABEL_DO_FINAL_UNWIND) { + ASM_JUMP_REG(emit->as, REG_RET); + } else { + emit_native_jump(emit, e->unwind_label); + } + emit_native_label_assign(emit, *emit->label_slot); + } + + emit_post(emit); +} + +static void emit_native_get_iter(emit_t *emit, bool use_stack) { + // perhaps the difficult one, as we want to rewrite for loops using native code + // in cases where we iterate over a Python object, can we use normal runtime calls? + + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, REG_ARG_1); + assert(vtype == VTYPE_PYOBJ); + if (use_stack) { + emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_2, MP_OBJ_ITER_BUF_NSLOTS); + emit_call(emit, MP_F_NATIVE_GETITER); + } else { + // mp_getiter will allocate the iter_buf on the heap + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, 0); + emit_call(emit, MP_F_NATIVE_GETITER); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } +} + +static void emit_native_for_iter(emit_t *emit, mp_uint_t label) { + emit_native_pre(emit); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_1, MP_OBJ_ITER_BUF_NSLOTS); + adjust_stack(emit, MP_OBJ_ITER_BUF_NSLOTS); + emit_call(emit, MP_F_NATIVE_ITERNEXT); + #if MICROPY_DEBUG_MP_OBJ_SENTINELS + ASM_MOV_REG_IMM(emit->as, REG_TEMP1, (mp_uint_t)MP_OBJ_STOP_ITERATION); + ASM_JUMP_IF_REG_EQ(emit->as, REG_RET, REG_TEMP1, label); + #else + MP_STATIC_ASSERT(MP_OBJ_STOP_ITERATION == 0); + ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, label, false); + #endif + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_for_iter_end(emit_t *emit) { + // adjust stack counter (we get here from for_iter ending, which popped the value for us) + emit_native_pre(emit); + adjust_stack(emit, -MP_OBJ_ITER_BUF_NSLOTS); + emit_post(emit); +} + +static void emit_native_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { + if (within_exc_handler) { + // Cancel any active exception so subsequent handlers don't see it + ASM_MOV_REG_IMM(emit->as, REG_TEMP0, (mp_uint_t)MP_OBJ_NULL); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + } else { + emit_native_leave_exc_stack(emit, false); + } + emit_native_jump(emit, label); +} + +static void emit_native_unary_op(emit_t *emit, mp_unary_op_t op) { + vtype_kind_t vtype = peek_vtype(emit, 0); + if (vtype == VTYPE_INT || vtype == VTYPE_UINT) { + if (op == MP_UNARY_OP_POSITIVE) { + // No-operation, just leave the argument on the stack. + } else if (op == MP_UNARY_OP_NEGATIVE) { + int reg = REG_RET; + emit_pre_pop_reg_flexible(emit, &vtype, ®, reg, reg); + ASM_NEG_REG(emit->as, reg); + emit_post_push_reg(emit, vtype, reg); + } else if (op == MP_UNARY_OP_INVERT) { + #ifdef ASM_NOT_REG + int reg = REG_RET; + emit_pre_pop_reg_flexible(emit, &vtype, ®, reg, reg); + ASM_NOT_REG(emit->as, reg); + #else + int reg = REG_RET; + emit_pre_pop_reg_flexible(emit, &vtype, ®, REG_ARG_1, reg); + ASM_MOV_REG_IMM(emit->as, REG_ARG_1, -1); + ASM_XOR_REG_REG(emit->as, reg, REG_ARG_1); + #endif + emit_post_push_reg(emit, vtype, reg); + } else { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("'not' not implemented"), mp_binary_op_method_name[op]); + } + } else if (vtype == VTYPE_PYOBJ) { + emit_pre_pop_reg(emit, &vtype, REG_ARG_2); + emit_call_with_imm_arg(emit, MP_F_UNARY_OP, op, REG_ARG_1); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } else { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't do unary op of '%q'"), vtype_to_qstr(vtype)); + } +} + +static void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { + DEBUG_printf("binary_op(" UINT_FMT ")\n", op); + vtype_kind_t vtype_lhs = peek_vtype(emit, 1); + vtype_kind_t vtype_rhs = peek_vtype(emit, 0); + if ((vtype_lhs == VTYPE_INT || vtype_lhs == VTYPE_UINT) + && (vtype_rhs == VTYPE_INT || vtype_rhs == VTYPE_UINT)) { + // for integers, inplace and normal ops are equivalent, so use just normal ops + if (MP_BINARY_OP_INPLACE_OR <= op && op <= MP_BINARY_OP_INPLACE_POWER) { + op += MP_BINARY_OP_OR - MP_BINARY_OP_INPLACE_OR; + } + + #if N_X64 || N_X86 + // special cases for x86 and shifting + if (op == MP_BINARY_OP_LSHIFT || op == MP_BINARY_OP_RSHIFT) { + #if N_X64 + emit_pre_pop_reg_reg(emit, &vtype_rhs, ASM_X64_REG_RCX, &vtype_lhs, REG_RET); + #else + emit_pre_pop_reg_reg(emit, &vtype_rhs, ASM_X86_REG_ECX, &vtype_lhs, REG_RET); + #endif + if (op == MP_BINARY_OP_LSHIFT) { + ASM_LSL_REG(emit->as, REG_RET); + } else { + if (vtype_lhs == VTYPE_UINT) { + ASM_LSR_REG(emit->as, REG_RET); + } else { + ASM_ASR_REG(emit->as, REG_RET); + } + } + emit_post_push_reg(emit, vtype_lhs, REG_RET); + return; + } + #endif + + // special cases for floor-divide and module because we dispatch to helper functions + if (op == MP_BINARY_OP_FLOOR_DIVIDE || op == MP_BINARY_OP_MODULO) { + emit_pre_pop_reg_reg(emit, &vtype_rhs, REG_ARG_2, &vtype_lhs, REG_ARG_1); + if (vtype_lhs != VTYPE_INT) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("div/mod not implemented for uint"), mp_binary_op_method_name[op]); + } + if (op == MP_BINARY_OP_FLOOR_DIVIDE) { + emit_call(emit, MP_F_SMALL_INT_FLOOR_DIVIDE); + } else { + emit_call(emit, MP_F_SMALL_INT_MODULO); + } + emit_post_push_reg(emit, VTYPE_INT, REG_RET); + return; + } + + int reg_rhs = REG_ARG_3; + emit_pre_pop_reg_flexible(emit, &vtype_rhs, ®_rhs, REG_RET, REG_ARG_2); + emit_pre_pop_reg(emit, &vtype_lhs, REG_ARG_2); + + #if !(N_X64 || N_X86) + if (op == MP_BINARY_OP_LSHIFT || op == MP_BINARY_OP_RSHIFT) { + if (op == MP_BINARY_OP_LSHIFT) { + ASM_LSL_REG_REG(emit->as, REG_ARG_2, reg_rhs); + } else { + if (vtype_lhs == VTYPE_UINT) { + ASM_LSR_REG_REG(emit->as, REG_ARG_2, reg_rhs); + } else { + ASM_ASR_REG_REG(emit->as, REG_ARG_2, reg_rhs); + } + } + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); + return; + } + #endif + + if (op == MP_BINARY_OP_OR) { + ASM_OR_REG_REG(emit->as, REG_ARG_2, reg_rhs); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); + } else if (op == MP_BINARY_OP_XOR) { + ASM_XOR_REG_REG(emit->as, REG_ARG_2, reg_rhs); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); + } else if (op == MP_BINARY_OP_AND) { + ASM_AND_REG_REG(emit->as, REG_ARG_2, reg_rhs); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); + } else if (op == MP_BINARY_OP_ADD) { + ASM_ADD_REG_REG(emit->as, REG_ARG_2, reg_rhs); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); + } else if (op == MP_BINARY_OP_SUBTRACT) { + ASM_SUB_REG_REG(emit->as, REG_ARG_2, reg_rhs); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); + } else if (op == MP_BINARY_OP_MULTIPLY) { + ASM_MUL_REG_REG(emit->as, REG_ARG_2, reg_rhs); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); + } else if (op == MP_BINARY_OP_LESS + || op == MP_BINARY_OP_MORE + || op == MP_BINARY_OP_EQUAL + || op == MP_BINARY_OP_LESS_EQUAL + || op == MP_BINARY_OP_MORE_EQUAL + || op == MP_BINARY_OP_NOT_EQUAL) { + // comparison ops + + if (vtype_lhs != vtype_rhs) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, MP_ERROR_TEXT("comparison of int and uint")); + } + + size_t op_idx = op - MP_BINARY_OP_LESS + (vtype_lhs == VTYPE_UINT ? 0 : 6); + + need_reg_single(emit, REG_RET, 0); + #if N_X64 + asm_x64_xor_r64_r64(emit->as, REG_RET, REG_RET); + asm_x64_cmp_r64_with_r64(emit->as, reg_rhs, REG_ARG_2); + static byte ops[6 + 6] = { + // unsigned + ASM_X64_CC_JB, + ASM_X64_CC_JA, + ASM_X64_CC_JE, + ASM_X64_CC_JBE, + ASM_X64_CC_JAE, + ASM_X64_CC_JNE, + // signed + ASM_X64_CC_JL, + ASM_X64_CC_JG, + ASM_X64_CC_JE, + ASM_X64_CC_JLE, + ASM_X64_CC_JGE, + ASM_X64_CC_JNE, + }; + asm_x64_setcc_r8(emit->as, ops[op_idx], REG_RET); + #elif N_X86 + asm_x86_xor_r32_r32(emit->as, REG_RET, REG_RET); + asm_x86_cmp_r32_with_r32(emit->as, reg_rhs, REG_ARG_2); + static byte ops[6 + 6] = { + // unsigned + ASM_X86_CC_JB, + ASM_X86_CC_JA, + ASM_X86_CC_JE, + ASM_X86_CC_JBE, + ASM_X86_CC_JAE, + ASM_X86_CC_JNE, + // signed + ASM_X86_CC_JL, + ASM_X86_CC_JG, + ASM_X86_CC_JE, + ASM_X86_CC_JLE, + ASM_X86_CC_JGE, + ASM_X86_CC_JNE, + }; + asm_x86_setcc_r8(emit->as, ops[op_idx], REG_RET); + #elif N_THUMB + asm_thumb_cmp_rlo_rlo(emit->as, REG_ARG_2, reg_rhs); + if (asm_thumb_allow_armv7m(emit->as)) { + static uint16_t ops[6 + 6] = { + // unsigned + ASM_THUMB_OP_ITE_CC, + ASM_THUMB_OP_ITE_HI, + ASM_THUMB_OP_ITE_EQ, + ASM_THUMB_OP_ITE_LS, + ASM_THUMB_OP_ITE_CS, + ASM_THUMB_OP_ITE_NE, + // signed + ASM_THUMB_OP_ITE_LT, + ASM_THUMB_OP_ITE_GT, + ASM_THUMB_OP_ITE_EQ, + ASM_THUMB_OP_ITE_LE, + ASM_THUMB_OP_ITE_GE, + ASM_THUMB_OP_ITE_NE, + }; + asm_thumb_op16(emit->as, ops[op_idx]); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); + } else { + static uint16_t ops[6 + 6] = { + // unsigned + ASM_THUMB_CC_CC, + ASM_THUMB_CC_HI, + ASM_THUMB_CC_EQ, + ASM_THUMB_CC_LS, + ASM_THUMB_CC_CS, + ASM_THUMB_CC_NE, + // signed + ASM_THUMB_CC_LT, + ASM_THUMB_CC_GT, + ASM_THUMB_CC_EQ, + ASM_THUMB_CC_LE, + ASM_THUMB_CC_GE, + ASM_THUMB_CC_NE, + }; + asm_thumb_bcc_rel9(emit->as, ops[op_idx], 6); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); + asm_thumb_b_rel12(emit->as, 4); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); + } + #elif N_ARM + asm_arm_cmp_reg_reg(emit->as, REG_ARG_2, reg_rhs); + static uint ccs[6 + 6] = { + // unsigned + ASM_ARM_CC_CC, + ASM_ARM_CC_HI, + ASM_ARM_CC_EQ, + ASM_ARM_CC_LS, + ASM_ARM_CC_CS, + ASM_ARM_CC_NE, + // signed + ASM_ARM_CC_LT, + ASM_ARM_CC_GT, + ASM_ARM_CC_EQ, + ASM_ARM_CC_LE, + ASM_ARM_CC_GE, + ASM_ARM_CC_NE, + }; + asm_arm_setcc_reg(emit->as, REG_RET, ccs[op_idx]); + #elif N_XTENSA || N_XTENSAWIN + static uint8_t ccs[6 + 6] = { + // unsigned + ASM_XTENSA_CC_LTU, + 0x80 | ASM_XTENSA_CC_LTU, // for GTU we'll swap args + ASM_XTENSA_CC_EQ, + 0x80 | ASM_XTENSA_CC_GEU, // for LEU we'll swap args + ASM_XTENSA_CC_GEU, + ASM_XTENSA_CC_NE, + // signed + ASM_XTENSA_CC_LT, + 0x80 | ASM_XTENSA_CC_LT, // for GT we'll swap args + ASM_XTENSA_CC_EQ, + 0x80 | ASM_XTENSA_CC_GE, // for LE we'll swap args + ASM_XTENSA_CC_GE, + ASM_XTENSA_CC_NE, + }; + uint8_t cc = ccs[op_idx]; + if ((cc & 0x80) == 0) { + asm_xtensa_setcc_reg_reg_reg(emit->as, cc, REG_RET, REG_ARG_2, reg_rhs); + } else { + asm_xtensa_setcc_reg_reg_reg(emit->as, cc & ~0x80, REG_RET, reg_rhs, REG_ARG_2); + } + #else + #error not implemented + #endif + emit_post_push_reg(emit, VTYPE_BOOL, REG_RET); + } else { + // TODO other ops not yet implemented + adjust_stack(emit, 1); + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("binary op %q not implemented"), mp_binary_op_method_name[op]); + } + } else if (vtype_lhs == VTYPE_PYOBJ && vtype_rhs == VTYPE_PYOBJ) { + emit_pre_pop_reg_reg(emit, &vtype_rhs, REG_ARG_3, &vtype_lhs, REG_ARG_2); + bool invert = false; + if (op == MP_BINARY_OP_NOT_IN) { + invert = true; + op = MP_BINARY_OP_IN; + } else if (op == MP_BINARY_OP_IS_NOT) { + invert = true; + op = MP_BINARY_OP_IS; + } + emit_call_with_imm_arg(emit, MP_F_BINARY_OP, op, REG_ARG_1); + if (invert) { + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_RET); + emit_call_with_imm_arg(emit, MP_F_UNARY_OP, MP_UNARY_OP_NOT, REG_ARG_1); + } + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } else { + adjust_stack(emit, -1); + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("can't do binary op between '%q' and '%q'"), + vtype_to_qstr(vtype_lhs), vtype_to_qstr(vtype_rhs)); + } +} + +#if MICROPY_PY_BUILTINS_SLICE +static void emit_native_build_slice(emit_t *emit, mp_uint_t n_args); +#endif + +static void emit_native_build(emit_t *emit, mp_uint_t n_args, int kind) { + // for viper: call runtime, with types of args + // if wrapped in byte_array, or something, allocates memory and fills it + MP_STATIC_ASSERT(MP_F_BUILD_TUPLE + MP_EMIT_BUILD_TUPLE == MP_F_BUILD_TUPLE); + MP_STATIC_ASSERT(MP_F_BUILD_TUPLE + MP_EMIT_BUILD_LIST == MP_F_BUILD_LIST); + MP_STATIC_ASSERT(MP_F_BUILD_TUPLE + MP_EMIT_BUILD_MAP == MP_F_BUILD_MAP); + MP_STATIC_ASSERT(MP_F_BUILD_TUPLE + MP_EMIT_BUILD_SET == MP_F_BUILD_SET); + #if MICROPY_PY_BUILTINS_SLICE + if (kind == MP_EMIT_BUILD_SLICE) { + emit_native_build_slice(emit, n_args); + return; + } + #endif + emit_native_pre(emit); + if (kind == MP_EMIT_BUILD_TUPLE || kind == MP_EMIT_BUILD_LIST || kind == MP_EMIT_BUILD_SET) { + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_2, n_args); // pointer to items + } + emit_call_with_imm_arg(emit, MP_F_BUILD_TUPLE + kind, n_args, REG_ARG_1); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); // new tuple/list/map/set +} + +static void emit_native_store_map(emit_t *emit) { + vtype_kind_t vtype_key, vtype_value, vtype_map; + emit_pre_pop_reg_reg_reg(emit, &vtype_key, REG_ARG_2, &vtype_value, REG_ARG_3, &vtype_map, REG_ARG_1); // key, value, map + assert(vtype_key == VTYPE_PYOBJ); + assert(vtype_value == VTYPE_PYOBJ); + assert(vtype_map == VTYPE_PYOBJ); + emit_call(emit, MP_F_STORE_MAP); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); // map +} + +#if MICROPY_PY_BUILTINS_SLICE +static void emit_native_build_slice(emit_t *emit, mp_uint_t n_args) { + DEBUG_printf("build_slice %d\n", n_args); + if (n_args == 2) { + vtype_kind_t vtype_start, vtype_stop; + emit_pre_pop_reg_reg(emit, &vtype_stop, REG_ARG_2, &vtype_start, REG_ARG_1); // arg1 = start, arg2 = stop + assert(vtype_start == VTYPE_PYOBJ); + assert(vtype_stop == VTYPE_PYOBJ); + emit_native_mov_reg_const(emit, REG_ARG_3, MP_F_CONST_NONE_OBJ); // arg3 = step + } else { + assert(n_args == 3); + vtype_kind_t vtype_start, vtype_stop, vtype_step; + emit_pre_pop_reg_reg_reg(emit, &vtype_step, REG_ARG_3, &vtype_stop, REG_ARG_2, &vtype_start, REG_ARG_1); // arg1 = start, arg2 = stop, arg3 = step + assert(vtype_start == VTYPE_PYOBJ); + assert(vtype_stop == VTYPE_PYOBJ); + assert(vtype_step == VTYPE_PYOBJ); + } + emit_call(emit, MP_F_NEW_SLICE); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} +#endif + +static void emit_native_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection_index) { + mp_fun_kind_t f; + if (kind == SCOPE_LIST_COMP) { + vtype_kind_t vtype_item; + emit_pre_pop_reg(emit, &vtype_item, REG_ARG_2); + assert(vtype_item == VTYPE_PYOBJ); + f = MP_F_LIST_APPEND; + #if MICROPY_PY_BUILTINS_SET + } else if (kind == SCOPE_SET_COMP) { + vtype_kind_t vtype_item; + emit_pre_pop_reg(emit, &vtype_item, REG_ARG_2); + assert(vtype_item == VTYPE_PYOBJ); + f = MP_F_STORE_SET; + #endif + } else { + // SCOPE_DICT_COMP + vtype_kind_t vtype_key, vtype_value; + emit_pre_pop_reg_reg(emit, &vtype_key, REG_ARG_2, &vtype_value, REG_ARG_3); + assert(vtype_key == VTYPE_PYOBJ); + assert(vtype_value == VTYPE_PYOBJ); + f = MP_F_STORE_MAP; + } + vtype_kind_t vtype_collection; + emit_access_stack(emit, collection_index, &vtype_collection, REG_ARG_1); + assert(vtype_collection == VTYPE_PYOBJ); + emit_call(emit, f); + emit_post(emit); +} + +static void emit_native_unpack_sequence(emit_t *emit, mp_uint_t n_args) { + DEBUG_printf("unpack_sequence %d\n", n_args); + vtype_kind_t vtype_base; + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = seq + assert(vtype_base == VTYPE_PYOBJ); + emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, n_args); // arg3 = dest ptr + emit_call_with_imm_arg(emit, MP_F_UNPACK_SEQUENCE, n_args, REG_ARG_2); // arg2 = n_args +} + +static void emit_native_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right) { + DEBUG_printf("unpack_ex %d %d\n", n_left, n_right); + vtype_kind_t vtype_base; + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = seq + assert(vtype_base == VTYPE_PYOBJ); + emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, n_left + n_right + 1); // arg3 = dest ptr + emit_call_with_imm_arg(emit, MP_F_UNPACK_EX, n_left | (n_right << 8), REG_ARG_2); // arg2 = n_left + n_right +} + +static void emit_native_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { + // call runtime, with type info for args, or don't support dict/default params, or only support Python objects for them + emit_native_pre(emit); + emit_native_mov_reg_state(emit, REG_ARG_2, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_2, REG_ARG_2, OFFSETOF_OBJ_FUN_BC_CONTEXT); + if (n_pos_defaults == 0 && n_kw_defaults == 0) { + need_reg_all(emit); + ASM_MOV_REG_IMM(emit->as, REG_ARG_3, 0); + } else { + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 2); + need_reg_all(emit); + } + emit_load_reg_with_child(emit, REG_ARG_1, scope->raw_code); + ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_PROTO_FUN); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { + // make function + emit_native_pre(emit); + emit_native_mov_reg_state(emit, REG_ARG_2, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_2, REG_ARG_2, OFFSETOF_OBJ_FUN_BC_CONTEXT); + if (n_pos_defaults == 0 && n_kw_defaults == 0) { + need_reg_all(emit); + ASM_MOV_REG_IMM(emit->as, REG_ARG_3, 0); + } else { + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 2 + n_closed_over); + adjust_stack(emit, 2 + n_closed_over); + need_reg_all(emit); + } + emit_load_reg_with_child(emit, REG_ARG_1, scope->raw_code); + ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_PROTO_FUN); + + // make closure + #if REG_ARG_1 != REG_RET + ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_RET); + #endif + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, n_closed_over); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over); + if (n_pos_defaults != 0 || n_kw_defaults != 0) { + adjust_stack(emit, -2); + } + ASM_CALL_IND(emit->as, MP_F_NEW_CLOSURE); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); +} + +static void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { + DEBUG_printf("call_function(n_pos=" UINT_FMT ", n_kw=" UINT_FMT ", star_flags=" UINT_FMT ")\n", n_positional, n_keyword, star_flags); + + // TODO: in viper mode, call special runtime routine with type info for args, + // and wanted type info for return, to remove need for boxing/unboxing + + emit_native_pre(emit); + vtype_kind_t vtype_fun = peek_vtype(emit, n_positional + 2 * n_keyword); + if (vtype_fun == VTYPE_BUILTIN_CAST) { + // casting operator + assert(n_positional == 1 && n_keyword == 0); + assert(!star_flags); + DEBUG_printf(" cast to %d\n", vtype_fun); + vtype_kind_t vtype_cast = peek_stack(emit, 1)->data.u_imm; + switch (peek_vtype(emit, 0)) { + case VTYPE_PYOBJ: { + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, REG_ARG_1); + emit_pre_pop_discard(emit); + emit_call_with_imm_arg(emit, MP_F_CONVERT_OBJ_TO_NATIVE, vtype_cast, REG_ARG_2); // arg2 = type + emit_post_push_reg(emit, vtype_cast, REG_RET); + break; + } + case VTYPE_BOOL: + case VTYPE_INT: + case VTYPE_UINT: + case VTYPE_PTR: + case VTYPE_PTR8: + case VTYPE_PTR16: + case VTYPE_PTR32: + case VTYPE_PTR_NONE: + emit_fold_stack_top(emit, REG_ARG_1); + emit_post_top_set_vtype(emit, vtype_cast); + break; + default: + // this can happen when casting a cast: int(int) + mp_raise_NotImplementedError(MP_ERROR_TEXT("casting")); + } + } else { + assert(vtype_fun == VTYPE_PYOBJ); + if (star_flags) { + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 2); // pointer to args + emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 0, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } else { + if (n_positional != 0 || n_keyword != 0) { + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword); // pointer to args + } + emit_pre_pop_reg(emit, &vtype_fun, REG_ARG_1); // the function + emit_call_with_imm_arg(emit, MP_F_NATIVE_CALL_FUNCTION_N_KW, n_positional | (n_keyword << 8), REG_ARG_2); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } + } +} + +static void emit_native_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { + if (star_flags) { + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 3); // pointer to args + emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 1, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } else { + emit_native_pre(emit); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 2 + n_positional + 2 * n_keyword); // pointer to items, including meth and self + emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW, n_positional, REG_ARG_1, n_keyword, REG_ARG_2); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } +} + +static void emit_native_return_value(emit_t *emit) { + DEBUG_printf("return_value\n"); + + if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { + // Save pointer to current stack position for caller to access return value + emit_get_stack_pointer_to_reg_for_pop(emit, REG_TEMP0, 1); + emit_native_mov_state_reg(emit, OFFSETOF_CODE_STATE_SP, REG_TEMP0); + + // Put return type in return value slot + ASM_MOV_REG_IMM(emit->as, REG_TEMP0, MP_VM_RETURN_NORMAL); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_TEMP0); + + // Do the unwinding jump to get to the return handler + emit_native_unwind_jump(emit, emit->exit_label, emit->exc_stack_size); + return; + } + + if (emit->do_viper_types) { + vtype_kind_t return_vtype = emit->scope->scope_flags >> MP_SCOPE_FLAG_VIPERRET_POS; + if (peek_vtype(emit, 0) == VTYPE_PTR_NONE) { + emit_pre_pop_discard(emit); + if (return_vtype == VTYPE_PYOBJ) { + emit_native_mov_reg_const(emit, REG_PARENT_RET, MP_F_CONST_NONE_OBJ); + } else { + ASM_MOV_REG_IMM(emit->as, REG_ARG_1, 0); + } + } else { + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, return_vtype == VTYPE_PYOBJ ? REG_PARENT_RET : REG_ARG_1); + if (vtype != return_vtype) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("return expected '%q' but got '%q'"), + vtype_to_qstr(return_vtype), vtype_to_qstr(vtype)); + } + } + if (return_vtype != VTYPE_PYOBJ) { + emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, return_vtype, REG_ARG_2); + #if REG_RET != REG_PARENT_RET + ASM_MOV_REG_REG(emit->as, REG_PARENT_RET, REG_RET); + #endif + } + } else { + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, REG_PARENT_RET); + assert(vtype == VTYPE_PYOBJ); + } + if (NEED_GLOBAL_EXC_HANDLER(emit)) { + // Save return value for the global exception handler to use + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_PARENT_RET); + } + emit_native_unwind_jump(emit, emit->exit_label, emit->exc_stack_size); +} + +static void emit_native_raise_varargs(emit_t *emit, mp_uint_t n_args) { + (void)n_args; + assert(n_args == 1); + vtype_kind_t vtype_exc; + emit_pre_pop_reg(emit, &vtype_exc, REG_ARG_1); // arg1 = object to raise + if (vtype_exc != VTYPE_PYOBJ) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, MP_ERROR_TEXT("must raise an object")); + } + // TODO probably make this 1 call to the runtime (which could even call convert, native_raise(obj, type)) + emit_call(emit, MP_F_NATIVE_RAISE); + mp_asm_base_suppress_code(&emit->as->base); +} + +static void emit_native_yield(emit_t *emit, int kind) { + // Note: 1 (yield) or 3 (yield from) labels are reserved for this function, starting at *emit->label_slot + + if (emit->do_viper_types) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("native yield")); + } + emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR; + + need_stack_settled(emit); + + if (kind == MP_EMIT_YIELD_FROM) { + + // Top of yield-from loop, conceptually implementing: + // for item in generator: + // yield item + + // Jump to start of loop + emit_native_jump(emit, *emit->label_slot + 2); + + // Label for top of loop + emit_native_label_assign(emit, *emit->label_slot + 1); + } + + // Save pointer to current stack position for caller to access yielded value + emit_get_stack_pointer_to_reg_for_pop(emit, REG_TEMP0, 1); + emit_native_mov_state_reg(emit, OFFSETOF_CODE_STATE_SP, REG_TEMP0); + + // Put return type in return value slot + ASM_MOV_REG_IMM(emit->as, REG_TEMP0, MP_VM_RETURN_YIELD); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_TEMP0); + + // Save re-entry PC + ASM_MOV_REG_PCREL(emit->as, REG_TEMP0, *emit->label_slot); + emit_native_mov_state_reg(emit, LOCAL_IDX_GEN_PC(emit), REG_TEMP0); + + // Jump to exit handler + ASM_JUMP(emit->as, emit->exit_label); + + // Label re-entry point + mp_asm_base_label_assign(&emit->as->base, *emit->label_slot); + + // Re-open any active exception handler + if (emit->exc_stack_size > 0) { + // Find innermost active exception handler, to restore as current handler + exc_stack_entry_t *e = &emit->exc_stack[emit->exc_stack_size - 1]; + for (; e >= emit->exc_stack; --e) { + if (e->is_active) { + // Found active handler, get its PC + ASM_MOV_REG_PCREL(emit->as, REG_RET, e->label); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_RET); + break; + } + } + } + + emit_native_adjust_stack_size(emit, 1); // send_value + + if (kind == MP_EMIT_YIELD_VALUE) { + // Check LOCAL_IDX_EXC_VAL for any injected value + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit)); + emit_call(emit, MP_F_NATIVE_RAISE); + } else { + // Label loop entry + emit_native_label_assign(emit, *emit->label_slot + 2); + + // Get the next item from the delegate generator + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, REG_ARG_2); // send_value + emit_access_stack(emit, 1, &vtype, REG_ARG_1); // generator + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_3, LOCAL_IDX_EXC_VAL(emit)); // throw_value + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_3); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 1); // ret_value + emit_call(emit, MP_F_NATIVE_YIELD_FROM); + + // If returned non-zero then generator continues + ASM_JUMP_IF_REG_NONZERO(emit->as, REG_RET, *emit->label_slot + 1, true); + + // Pop exhausted gen, replace with ret_value + emit_native_adjust_stack_size(emit, 1); // ret_value + emit_fold_stack_top(emit, REG_ARG_1); + } +} + +static void emit_native_start_except_handler(emit_t *emit) { + // Protected block has finished so leave the current exception handler + emit_native_leave_exc_stack(emit, true); + + // Get and push nlr_buf.ret_val + ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, LOCAL_IDX_EXC_VAL(emit)); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_TEMP0); +} + +static void emit_native_end_except_handler(emit_t *emit) { + adjust_stack(emit, -1); // pop the exception (end_finally didn't use it) +} + +const emit_method_table_t EXPORT_FUN(method_table) = { + #if MICROPY_DYNAMIC_COMPILER + EXPORT_FUN(new), + EXPORT_FUN(free), + #endif + + emit_native_start_pass, + emit_native_end_pass, + emit_native_adjust_stack_size, + emit_native_set_source_line, + + { + emit_native_load_local, + emit_native_load_global, + }, + { + emit_native_store_local, + emit_native_store_global, + }, + { + emit_native_delete_local, + emit_native_delete_global, + }, + + emit_native_label_assign, + emit_native_import, + emit_native_load_const_tok, + emit_native_load_const_small_int, + emit_native_load_const_str, + emit_native_load_const_obj, + emit_native_load_null, + emit_native_load_method, + emit_native_load_build_class, + emit_native_subscr, + emit_native_attr, + emit_native_dup_top, + emit_native_dup_top_two, + emit_native_pop_top, + emit_native_rot_two, + emit_native_rot_three, + emit_native_jump, + emit_native_pop_jump_if, + emit_native_jump_if_or_pop, + emit_native_unwind_jump, + emit_native_setup_block, + emit_native_with_cleanup, + emit_native_end_finally, + emit_native_get_iter, + emit_native_for_iter, + emit_native_for_iter_end, + emit_native_pop_except_jump, + emit_native_unary_op, + emit_native_binary_op, + emit_native_build, + emit_native_store_map, + emit_native_store_comp, + emit_native_unpack_sequence, + emit_native_unpack_ex, + emit_native_make_function, + emit_native_make_closure, + emit_native_call_function, + emit_native_call_method, + emit_native_return_value, + emit_native_raise_varargs, + emit_native_yield, + + emit_native_start_except_handler, + emit_native_end_except_handler, +}; + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitnthumb.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnthumb.c new file mode 100644 index 00000000..844a73ff --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnthumb.c @@ -0,0 +1,18 @@ +// thumb specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_THUMB + +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmthumb.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (3) // r4 + +#define N_THUMB (1) +#define EXPORT_FUN(name) emit_native_thumb_##name +#include "py/emitnative.c" + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitnx64.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnx64.c new file mode 100644 index 00000000..1b32286d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnx64.c @@ -0,0 +1,18 @@ +// x64 specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_X64 + +// This is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmx64.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (5) // rbx + +#define N_X64 (1) +#define EXPORT_FUN(name) emit_native_x64_##name +#include "py/emitnative.c" + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitnx86.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnx86.c new file mode 100644 index 00000000..1d2aefa7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnx86.c @@ -0,0 +1,70 @@ +// x86 specific stuff + +#include "py/mpconfig.h" +#include "py/nativeglue.h" + +#if MICROPY_EMIT_X86 + +// This is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmx86.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (5) // ebx + +// x86 needs a table to know how many args a given function has +static byte mp_f_n_args[MP_F_NUMBER_OF] = { + [MP_F_CONVERT_OBJ_TO_NATIVE] = 2, + [MP_F_CONVERT_NATIVE_TO_OBJ] = 2, + [MP_F_NATIVE_SWAP_GLOBALS] = 1, + [MP_F_LOAD_NAME] = 1, + [MP_F_LOAD_GLOBAL] = 1, + [MP_F_LOAD_BUILD_CLASS] = 0, + [MP_F_LOAD_ATTR] = 2, + [MP_F_LOAD_METHOD] = 3, + [MP_F_LOAD_SUPER_METHOD] = 2, + [MP_F_STORE_NAME] = 2, + [MP_F_STORE_GLOBAL] = 2, + [MP_F_STORE_ATTR] = 3, + [MP_F_OBJ_SUBSCR] = 3, + [MP_F_OBJ_IS_TRUE] = 1, + [MP_F_UNARY_OP] = 2, + [MP_F_BINARY_OP] = 3, + [MP_F_BUILD_TUPLE] = 2, + [MP_F_BUILD_LIST] = 2, + [MP_F_BUILD_MAP] = 1, + [MP_F_BUILD_SET] = 2, + [MP_F_STORE_SET] = 2, + [MP_F_LIST_APPEND] = 2, + [MP_F_STORE_MAP] = 3, + [MP_F_MAKE_FUNCTION_FROM_PROTO_FUN] = 3, + [MP_F_NATIVE_CALL_FUNCTION_N_KW] = 3, + [MP_F_CALL_METHOD_N_KW] = 3, + [MP_F_CALL_METHOD_N_KW_VAR] = 3, + [MP_F_NATIVE_GETITER] = 2, + [MP_F_NATIVE_ITERNEXT] = 1, + [MP_F_NLR_PUSH] = 1, + [MP_F_NLR_POP] = 0, + [MP_F_NATIVE_RAISE] = 1, + [MP_F_IMPORT_NAME] = 3, + [MP_F_IMPORT_FROM] = 2, + [MP_F_IMPORT_ALL] = 1, + [MP_F_NEW_SLICE] = 3, + [MP_F_UNPACK_SEQUENCE] = 3, + [MP_F_UNPACK_EX] = 3, + [MP_F_DELETE_NAME] = 1, + [MP_F_DELETE_GLOBAL] = 1, + [MP_F_NEW_CLOSURE] = 3, + [MP_F_ARG_CHECK_NUM_SIG] = 3, + [MP_F_SETUP_CODE_STATE] = 4, + [MP_F_SMALL_INT_FLOOR_DIVIDE] = 2, + [MP_F_SMALL_INT_MODULO] = 2, + [MP_F_NATIVE_YIELD_FROM] = 3, + [MP_F_SETJMP] = 1, +}; + +#define N_X86 (1) +#define EXPORT_FUN(name) emit_native_x86_##name +#include "py/emitnative.c" + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitnxtensa.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnxtensa.c new file mode 100644 index 00000000..c89b0290 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnxtensa.c @@ -0,0 +1,18 @@ +// Xtensa specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_XTENSA + +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmxtensa.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (8) // a12 + +#define N_XTENSA (1) +#define EXPORT_FUN(name) emit_native_xtensa_##name +#include "py/emitnative.c" + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/emitnxtensawin.c b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnxtensawin.c new file mode 100644 index 00000000..f6eeff84 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/emitnxtensawin.c @@ -0,0 +1,20 @@ +// Xtensa-Windowed specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_XTENSAWIN + +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#define GENERIC_ASM_API_WIN (1) +#include "py/asmxtensa.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (2 + 4) // a4 + +#define N_NLR_SETJMP (1) +#define N_XTENSAWIN (1) +#define EXPORT_FUN(name) emit_native_xtensawin_##name +#include "py/emitnative.c" + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/formatfloat.c b/non_catalog_apps/mp_flipper/lib/micropython/py/formatfloat.c new file mode 100644 index 00000000..7cd47101 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/formatfloat.c @@ -0,0 +1,424 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpconfig.h" +#include "py/misc.h" +#if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE + +#include +#include +#include +#include +#include "py/formatfloat.h" + +/*********************************************************************** + + Routine for converting a arbitrary floating + point number into a string. + + The code in this function was inspired from Fred Bayer's pdouble.c. + Since pdouble.c was released as Public Domain, I'm releasing this + code as public domain as well. + + The original code can be found in https://github.com/dhylands/format-float + + Dave Hylands + +***********************************************************************/ + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +// 1 sign bit, 8 exponent bits, and 23 mantissa bits. +// exponent values 0 and 255 are reserved, exponent can be 1 to 254. +// exponent is stored with a bias of 127. +// The min and max floats are on the order of 1x10^37 and 1x10^-37 + +#define FPTYPE float +#define FPCONST(x) x##F +#define FPROUND_TO_ONE 0.9999995F +#define FPDECEXP 32 +#define FPMIN_BUF_SIZE 6 // +9e+99 + +#define FLT_SIGN_MASK 0x80000000 + +static inline int fp_signbit(float x) { + mp_float_union_t fb = {x}; + return fb.i & FLT_SIGN_MASK; +} +#define fp_isnan(x) isnan(x) +#define fp_isinf(x) isinf(x) +static inline int fp_iszero(float x) { + mp_float_union_t fb = {x}; + return fb.i == 0; +} +static inline int fp_isless1(float x) { + mp_float_union_t fb = {x}; + return fb.i < 0x3f800000; +} + +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + +#define FPTYPE double +#define FPCONST(x) x +#define FPROUND_TO_ONE 0.999999999995 +#define FPDECEXP 256 +#define FPMIN_BUF_SIZE 7 // +9e+199 +#define fp_signbit(x) signbit(x) +#define fp_isnan(x) isnan(x) +#define fp_isinf(x) isinf(x) +#define fp_iszero(x) (x == 0) +#define fp_isless1(x) (x < 1.0) + +#endif // MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT/DOUBLE + +static inline int fp_expval(FPTYPE x) { + mp_float_union_t fb = {x}; + return (int)((fb.i >> MP_FLOAT_FRAC_BITS) & (~(0xFFFFFFFF << MP_FLOAT_EXP_BITS))) - MP_FLOAT_EXP_OFFSET; +} + +int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) { + + char *s = buf; + + if (buf_size <= FPMIN_BUF_SIZE) { + // FPMIN_BUF_SIZE is the minimum size needed to store any FP number. + // If the buffer does not have enough room for this (plus null terminator) + // then don't try to format the float. + + if (buf_size >= 2) { + *s++ = '?'; + } + if (buf_size >= 1) { + *s = '\0'; + } + return buf_size >= 2; + } + if (fp_signbit(f) && !fp_isnan(f)) { + *s++ = '-'; + f = -f; + } else { + if (sign) { + *s++ = sign; + } + } + + // buf_remaining contains bytes available for digits and exponent. + // It is buf_size minus room for the sign and null byte. + int buf_remaining = buf_size - 1 - (s - buf); + + { + char uc = fmt & 0x20; + if (fp_isinf(f)) { + *s++ = 'I' ^ uc; + *s++ = 'N' ^ uc; + *s++ = 'F' ^ uc; + goto ret; + } else if (fp_isnan(f)) { + *s++ = 'N' ^ uc; + *s++ = 'A' ^ uc; + *s++ = 'N' ^ uc; + ret: + *s = '\0'; + return s - buf; + } + } + + if (prec < 0) { + prec = 6; + } + char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt + fmt |= 0x20; // Force fmt to be lowercase + char org_fmt = fmt; + if (fmt == 'g' && prec == 0) { + prec = 1; + } + int e; + int dec = 0; + char e_sign = '\0'; + int num_digits = 0; + int signed_e = 0; + + // Approximate power of 10 exponent from binary exponent. + // abs(e_guess) is lower bound on abs(power of 10 exponent). + int e_guess = (int)(fp_expval(f) * FPCONST(0.3010299956639812)); // 1/log2(10). + if (fp_iszero(f)) { + e = 0; + if (fmt == 'f') { + // Truncate precision to prevent buffer overflow + if (prec + 2 > buf_remaining) { + prec = buf_remaining - 2; + } + num_digits = prec + 1; + } else { + // Truncate precision to prevent buffer overflow + if (prec + 6 > buf_remaining) { + prec = buf_remaining - 6; + } + if (fmt == 'e') { + e_sign = '+'; + } + } + } else if (fp_isless1(f)) { + FPTYPE f_entry = f; // Save f in case we go to 'f' format. + // Build negative exponent + e = -e_guess; + FPTYPE u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); + while (u_base > f) { + ++e; + u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); + } + // Normalize out the inferred unit. Use divide because + // pow(10, e) * pow(10, -e) is slightly < 1 for some e in float32 + // (e.g. print("%.12f" % ((1e13) * (1e-13)))) + f /= u_base; + + // If the user specified 'g' format, and e is <= 4, then we'll switch + // to the fixed format ('f') + + if (fmt == 'f' || (fmt == 'g' && e <= 4)) { + fmt = 'f'; + dec = 0; + + if (org_fmt == 'g') { + prec += (e - 1); + } + + // truncate precision to prevent buffer overflow + if (prec + 2 > buf_remaining) { + prec = buf_remaining - 2; + } + + num_digits = prec; + signed_e = 0; + f = f_entry; + ++num_digits; + } else { + // For e & g formats, we'll be printing the exponent, so set the + // sign. + e_sign = '-'; + dec = 0; + + if (prec > (buf_remaining - FPMIN_BUF_SIZE)) { + prec = buf_remaining - FPMIN_BUF_SIZE; + if (fmt == 'g') { + prec++; + } + } + signed_e = -e; + } + } else { + // Build positive exponent. + // We don't modify f at this point to avoid inaccuracies from + // scaling it. Instead, we find the product of powers of 10 + // that is not greater than it, and use that to start the + // mantissa. + e = e_guess; + FPTYPE next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); + while (f >= next_u) { + ++e; + next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); + } + + // If the user specified fixed format (fmt == 'f') and e makes the + // number too big to fit into the available buffer, then we'll + // switch to the 'e' format. + + if (fmt == 'f') { + if (e >= buf_remaining) { + fmt = 'e'; + } else if ((e + prec + 2) > buf_remaining) { + prec = buf_remaining - e - 2; + if (prec < 0) { + // This means no decimal point, so we can add one back + // for the decimal. + prec++; + } + } + } + if (fmt == 'e' && prec > (buf_remaining - FPMIN_BUF_SIZE)) { + prec = buf_remaining - FPMIN_BUF_SIZE; + } + if (fmt == 'g') { + // Truncate precision to prevent buffer overflow + if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) { + prec = buf_remaining - (FPMIN_BUF_SIZE - 1); + } + } + // If the user specified 'g' format, and e is < prec, then we'll switch + // to the fixed format. + + if (fmt == 'g' && e < prec) { + fmt = 'f'; + prec -= (e + 1); + } + if (fmt == 'f') { + dec = e; + num_digits = prec + e + 1; + } else { + e_sign = '+'; + } + signed_e = e; + } + if (prec < 0) { + // This can happen when the prec is trimmed to prevent buffer overflow + prec = 0; + } + + // At this point e contains the absolute value of the power of 10 exponent. + // (dec + 1) == the number of dgits before the decimal. + + // For e, prec is # digits after the decimal + // For f, prec is # digits after the decimal + // For g, prec is the max number of significant digits + // + // For e & g there will be a single digit before the decimal + // for f there will be e digits before the decimal + + if (fmt == 'e') { + num_digits = prec + 1; + } else if (fmt == 'g') { + if (prec == 0) { + prec = 1; + } + num_digits = prec; + } + + int d = 0; + for (int digit_index = signed_e; num_digits >= 0; --digit_index) { + FPTYPE u_base = FPCONST(1.0); + if (digit_index > 0) { + // Generate 10^digit_index for positive digit_index. + u_base = MICROPY_FLOAT_C_FUN(pow)(10, digit_index); + } + for (d = 0; d < 9; ++d) { + if (f < u_base) { + break; + } + f -= u_base; + } + // We calculate one more digit than we display, to use in rounding + // below. So only emit the digit if it's one that we display. + if (num_digits > 0) { + // Emit this number (the leading digit). + *s++ = '0' + d; + if (dec == 0 && prec > 0) { + *s++ = '.'; + } + } + --dec; + --num_digits; + if (digit_index <= 0) { + // Once we get below 1.0, we scale up f instead of calculating + // negative powers of 10 in u_base. This provides better + // renditions of exact decimals like 1/16 etc. + f *= FPCONST(10.0); + } + } + // Rounding. If the next digit to print is >= 5, round up. + if (d >= 5) { + char *rs = s; + rs--; + while (1) { + if (*rs == '.') { + rs--; + continue; + } + if (*rs < '0' || *rs > '9') { + // + or - + rs++; // So we sit on the digit to the right of the sign + break; + } + if (*rs < '9') { + (*rs)++; + break; + } + *rs = '0'; + if (rs == buf) { + break; + } + rs--; + } + if (*rs == '0') { + // We need to insert a 1 + if (rs[1] == '.' && fmt != 'f') { + // We're going to round 9.99 to 10.00 + // Move the decimal point + rs[0] = '.'; + rs[1] = '0'; + if (e_sign == '-') { + e--; + if (e == 0) { + e_sign = '+'; + } + } else { + e++; + } + } else { + // Need at extra digit at the end to make room for the leading '1' + // but if we're at the buffer size limit, just drop the final digit. + if ((size_t)(s + 1 - buf) < buf_size) { + s++; + } + } + char *ss = s; + while (ss > rs) { + *ss = ss[-1]; + ss--; + } + *rs = '1'; + } + } + + // verify that we did not overrun the input buffer so far + assert((size_t)(s + 1 - buf) <= buf_size); + + if (org_fmt == 'g' && prec > 0) { + // Remove trailing zeros and a trailing decimal point + while (s[-1] == '0') { + s--; + } + if (s[-1] == '.') { + s--; + } + } + // Append the exponent + if (e_sign) { + *s++ = e_char; + *s++ = e_sign; + if (FPMIN_BUF_SIZE == 7 && e >= 100) { + *s++ = '0' + (e / 100); + } + *s++ = '0' + ((e / 10) % 10); + *s++ = '0' + (e % 10); + } + *s = '\0'; + + // verify that we did not overrun the input buffer + assert((size_t)(s + 1 - buf) <= buf_size); + + return s - buf; +} + +#endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/formatfloat.h b/non_catalog_apps/mp_flipper/lib/micropython/py/formatfloat.h new file mode 100644 index 00000000..9a1643b4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/formatfloat.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_FORMATFLOAT_H +#define MICROPY_INCLUDED_PY_FORMATFLOAT_H + +#include "py/mpconfig.h" + +#if MICROPY_PY_BUILTINS_FLOAT +int mp_format_float(mp_float_t f, char *buf, size_t bufSize, char fmt, int prec, char sign); +#endif + +#endif // MICROPY_INCLUDED_PY_FORMATFLOAT_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/frozenmod.c b/non_catalog_apps/mp_flipper/lib/micropython/py/frozenmod.c new file mode 100644 index 00000000..61c2f20a --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/frozenmod.c @@ -0,0 +1,135 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paul Sokolovsky + * Copyright (c) 2016 Damien P. George + * Copyright (c) 2021 Jim Mussared + * + * 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 "py/lexer.h" +#include "py/frozenmod.h" + +#if MICROPY_MODULE_FROZEN + +// Null-separated frozen file names. All string-type entries are listed first, +// followed by mpy-type entries. Use mp_frozen_str_sizes to determine how +// many string entries. +extern const char mp_frozen_names[]; + +#if MICROPY_MODULE_FROZEN_STR + +#ifndef MICROPY_MODULE_FROZEN_LEXER +#define MICROPY_MODULE_FROZEN_LEXER mp_lexer_new_from_str_len +#else +mp_lexer_t *MICROPY_MODULE_FROZEN_LEXER(qstr src_name, const char *str, mp_uint_t len, mp_uint_t free_len); +#endif + +// Size in bytes of each string entry, followed by a zero (terminator). +extern const uint32_t mp_frozen_str_sizes[]; +// Null-separated string content. +extern const char mp_frozen_str_content[]; +#endif // MICROPY_MODULE_FROZEN_STR + +#if MICROPY_MODULE_FROZEN_MPY + +#include "py/emitglue.h" + +extern const mp_frozen_module_t *const mp_frozen_mpy_content[]; + +#endif // MICROPY_MODULE_FROZEN_MPY + +// Search for "str" as a frozen entry, returning the stat result +// (no-exist/file/dir), as well as the type (none/str/mpy) and data. +// frozen_type can be NULL if its value isn't needed (and then data is assumed to be NULL). +mp_import_stat_t mp_find_frozen_module(const char *str, int *frozen_type, void **data) { + size_t len = strlen(str); + const char *name = mp_frozen_names; + + if (frozen_type != NULL) { + *frozen_type = MP_FROZEN_NONE; + } + + // Count the number of str lengths we have to find how many str entries. + size_t num_str = 0; + #if MICROPY_MODULE_FROZEN_STR && MICROPY_MODULE_FROZEN_MPY + for (const uint32_t *s = mp_frozen_str_sizes; *s != 0; ++s) { + ++num_str; + } + #endif + + for (size_t i = 0; *name != 0; i++) { + size_t entry_len = strlen(name); + if (entry_len >= len && memcmp(str, name, len) == 0) { + // Query is a prefix of the current entry. + if (entry_len == len) { + // Exact match --> file. + + if (frozen_type != NULL) { + #if MICROPY_MODULE_FROZEN_STR + if (i < num_str) { + *frozen_type = MP_FROZEN_STR; + // Use the size table to figure out where this index starts. + size_t offset = 0; + for (size_t j = 0; j < i; ++j) { + offset += mp_frozen_str_sizes[j] + 1; + } + size_t content_len = mp_frozen_str_sizes[i]; + const char *content = &mp_frozen_str_content[offset]; + + // Note: str & len have been updated by find_frozen_entry to strip + // the ".frozen/" prefix (to avoid this being a distinct qstr to + // the original path QSTR in frozen_content.c). + qstr source = qstr_from_strn(str, len); + mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, content, content_len, 0); + *data = lex; + } + #endif + + #if MICROPY_MODULE_FROZEN_MPY + if (i >= num_str) { + *frozen_type = MP_FROZEN_MPY; + // Load the corresponding index as a raw_code, taking + // into account any string entries to offset by. + *data = (void *)mp_frozen_mpy_content[i - num_str]; + } + #endif + } + + return MP_IMPORT_STAT_FILE; + } else if (name[len] == '/') { + // Matches up to directory separator, this is a valid + // directory path. + return MP_IMPORT_STAT_DIR; + } + } + // Skip null separator. + name += entry_len + 1; + } + + return MP_IMPORT_STAT_NO_EXIST; +} + +#endif // MICROPY_MODULE_FROZEN diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/frozenmod.h b/non_catalog_apps/mp_flipper/lib/micropython/py/frozenmod.h new file mode 100644 index 00000000..cff6c861 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/frozenmod.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paul Sokolovsky + * Copyright (c) 2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_FROZENMOD_H +#define MICROPY_INCLUDED_PY_FROZENMOD_H + +#include "py/builtin.h" + +enum { + MP_FROZEN_NONE, + MP_FROZEN_STR, + MP_FROZEN_MPY, +}; + +mp_import_stat_t mp_find_frozen_module(const char *str, int *frozen_type, void **data); + +#endif // MICROPY_INCLUDED_PY_FROZENMOD_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/gc.c b/non_catalog_apps/mp_flipper/lib/micropython/py/gc.c new file mode 100644 index 00000000..8a03ce52 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/gc.c @@ -0,0 +1,1354 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/gc.h" +#include "py/runtime.h" + +#if MICROPY_DEBUG_VALGRIND +#include +#endif + +#if MICROPY_ENABLE_GC + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + +// make this 1 to dump the heap each time it changes +#define EXTENSIVE_HEAP_PROFILING (0) + +// make this 1 to zero out swept memory to more eagerly +// detect untraced object still in use +#define CLEAR_ON_SWEEP (0) + +#define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / MP_BYTES_PER_OBJ_WORD) +#define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK) + +// ATB = allocation table byte +// 0b00 = FREE -- free block +// 0b01 = HEAD -- head of a chain of blocks +// 0b10 = TAIL -- in the tail of a chain of blocks +// 0b11 = MARK -- marked head block + +#define AT_FREE (0) +#define AT_HEAD (1) +#define AT_TAIL (2) +#define AT_MARK (3) + +#define BLOCKS_PER_ATB (4) +#define ATB_MASK_0 (0x03) +#define ATB_MASK_1 (0x0c) +#define ATB_MASK_2 (0x30) +#define ATB_MASK_3 (0xc0) + +#define ATB_0_IS_FREE(a) (((a) & ATB_MASK_0) == 0) +#define ATB_1_IS_FREE(a) (((a) & ATB_MASK_1) == 0) +#define ATB_2_IS_FREE(a) (((a) & ATB_MASK_2) == 0) +#define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0) + +#if MICROPY_GC_SPLIT_HEAP +#define NEXT_AREA(area) ((area)->next) +#else +#define NEXT_AREA(area) (NULL) +#endif + +#define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1))) +#define ATB_GET_KIND(area, block) (((area)->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3) +#define ATB_ANY_TO_FREE(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0) +#define ATB_FREE_TO_HEAD(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0) +#define ATB_FREE_TO_TAIL(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0) +#define ATB_HEAD_TO_MARK(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0) +#define ATB_MARK_TO_HEAD(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0) + +#define BLOCK_FROM_PTR(area, ptr) (((byte *)(ptr) - area->gc_pool_start) / BYTES_PER_BLOCK) +#define PTR_FROM_BLOCK(area, block) (((block) * BYTES_PER_BLOCK + (uintptr_t)area->gc_pool_start)) + +// After the ATB, there must be a byte filled with AT_FREE so that gc_mark_tree +// cannot erroneously conclude that a block extends past the end of the GC heap +// due to bit patterns in the FTB (or first block, if finalizers are disabled) +// being interpreted as AT_TAIL. +#define ALLOC_TABLE_GAP_BYTE (1) + +#if MICROPY_ENABLE_FINALISER +// FTB = finaliser table byte +// if set, then the corresponding block may have a finaliser + +#define BLOCKS_PER_FTB (8) + +#define FTB_GET(area, block) ((area->gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1) +#define FTB_SET(area, block) do { area->gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0) +#define FTB_CLEAR(area, block) do { area->gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0) +#endif + +#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL +#define GC_ENTER() mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1) +#define GC_EXIT() mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex)) +#else +#define GC_ENTER() +#define GC_EXIT() +#endif + +// TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool +static void gc_setup_area(mp_state_mem_area_t *area, void *start, void *end) { + // calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes): + // T = A + F + P + // F = A * BLOCKS_PER_ATB / BLOCKS_PER_FTB + // P = A * BLOCKS_PER_ATB * BYTES_PER_BLOCK + // => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK) + size_t total_byte_len = (byte *)end - (byte *)start; + #if MICROPY_ENABLE_FINALISER + area->gc_alloc_table_byte_len = (total_byte_len - ALLOC_TABLE_GAP_BYTE) + * MP_BITS_PER_BYTE + / ( + MP_BITS_PER_BYTE + + MP_BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + + MP_BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK + ); + #else + area->gc_alloc_table_byte_len = (total_byte_len - ALLOC_TABLE_GAP_BYTE) / (1 + MP_BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); + #endif + + area->gc_alloc_table_start = (byte *)start; + + #if MICROPY_ENABLE_FINALISER + size_t gc_finaliser_table_byte_len = (area->gc_alloc_table_byte_len * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB; + area->gc_finaliser_table_start = area->gc_alloc_table_start + area->gc_alloc_table_byte_len + ALLOC_TABLE_GAP_BYTE; + #endif + + size_t gc_pool_block_len = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; + area->gc_pool_start = (byte *)end - gc_pool_block_len * BYTES_PER_BLOCK; + area->gc_pool_end = end; + + #if MICROPY_ENABLE_FINALISER + assert(area->gc_pool_start >= area->gc_finaliser_table_start + gc_finaliser_table_byte_len); + #endif + + #if MICROPY_ENABLE_FINALISER + // clear ATB's and FTB's + memset(area->gc_alloc_table_start, 0, gc_finaliser_table_byte_len + area->gc_alloc_table_byte_len + ALLOC_TABLE_GAP_BYTE); + #else + // clear ATB's + memset(area->gc_alloc_table_start, 0, area->gc_alloc_table_byte_len + ALLOC_TABLE_GAP_BYTE); + #endif + + area->gc_last_free_atb_index = 0; + area->gc_last_used_block = 0; + + #if MICROPY_GC_SPLIT_HEAP + area->next = NULL; + #endif + + DEBUG_printf("GC layout:\n"); + DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " + UINT_FMT " blocks\n", + area->gc_alloc_table_start, area->gc_alloc_table_byte_len, + area->gc_alloc_table_byte_len * BLOCKS_PER_ATB); + #if MICROPY_ENABLE_FINALISER + DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " + UINT_FMT " blocks\n", area->gc_finaliser_table_start, + gc_finaliser_table_byte_len, + gc_finaliser_table_byte_len * BLOCKS_PER_FTB); + #endif + DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " + UINT_FMT " blocks\n", area->gc_pool_start, + gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len); +} + +void gc_init(void *start, void *end) { + // align end pointer on block boundary + end = (void *)((uintptr_t)end & (~(BYTES_PER_BLOCK - 1))); + DEBUG_printf("Initializing GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte *)end - (byte *)start); + + gc_setup_area(&MP_STATE_MEM(area), start, end); + + // set last free ATB index to start of heap + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + #endif + + // unlock the GC + MP_STATE_THREAD(gc_lock_depth) = 0; + + // allow auto collection + MP_STATE_MEM(gc_auto_collect_enabled) = 1; + + #if MICROPY_GC_ALLOC_THRESHOLD + // by default, maxuint for gc threshold, effectively turning gc-by-threshold off + MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1; + MP_STATE_MEM(gc_alloc_amount) = 0; + #endif + + #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL + mp_thread_mutex_init(&MP_STATE_MEM(gc_mutex)); + #endif +} + +#if MICROPY_GC_SPLIT_HEAP +void gc_add(void *start, void *end) { + // Place the area struct at the start of the area. + mp_state_mem_area_t *area = (mp_state_mem_area_t *)start; + start = (void *)((uintptr_t)start + sizeof(mp_state_mem_area_t)); + + end = (void *)((uintptr_t)end & (~(BYTES_PER_BLOCK - 1))); + DEBUG_printf("Adding GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte *)end - (byte *)start); + + // Init this area + gc_setup_area(area, start, end); + + // Find the last registered area in the linked list + mp_state_mem_area_t *prev_area = &MP_STATE_MEM(area); + while (prev_area->next != NULL) { + prev_area = prev_area->next; + } + + // Add this area to the linked list + prev_area->next = area; +} + +#if MICROPY_GC_SPLIT_HEAP_AUTO +// Try to automatically add a heap area large enough to fulfill 'failed_alloc'. +static bool gc_try_add_heap(size_t failed_alloc) { + // 'needed' is the size of a heap large enough to hold failed_alloc, with + // the additional metadata overheads as calculated in gc_setup_area(). + // + // Rather than reproduce all of that logic here, we approximate that adding + // (13/512) is enough overhead for sufficiently large heap areas (the + // overhead converges to 3/128, but there's some fixed overhead and some + // rounding up of partial block sizes). + size_t needed = failed_alloc + MAX(2048, failed_alloc * 13 / 512); + + size_t avail = gc_get_max_new_split(); + + DEBUG_printf("gc_try_add_heap failed_alloc " UINT_FMT ", " + "needed " UINT_FMT ", avail " UINT_FMT " bytes \n", + failed_alloc, + needed, + avail); + + if (avail < needed) { + // Can't fit this allocation, or system heap has nearly run out anyway + return false; + } + + // Deciding how much to grow the total heap by each time is tricky: + // + // - Grow by too small amounts, leads to heap fragmentation issues. + // + // - Grow by too large amounts, may lead to system heap running out of + // space. + // + // Currently, this implementation is: + // + // - At minimum, aim to double the total heap size each time we add a new + // heap. i.e. without any large single allocations, total size will be + // 64KB -> 128KB -> 256KB -> 512KB -> 1MB, etc + // + // - If the failed allocation is too large to fit in that size, the new + // heap is made exactly large enough for that allocation. Future growth + // will double the total heap size again. + // + // - If the new heap won't fit in the available free space, add the largest + // new heap that will fit (this may lead to failed system heap allocations + // elsewhere, but some allocation will likely fail in this circumstance!) + + // Compute total number of blocks in the current heap. + size_t total_blocks = 0; + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); + area != NULL; + area = NEXT_AREA(area)) { + total_blocks += area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; + } + + // Compute bytes needed to build a heap with total_blocks blocks. + size_t total_heap = + total_blocks / BLOCKS_PER_ATB + #if MICROPY_ENABLE_FINALISER + + total_blocks / BLOCKS_PER_FTB + #endif + + total_blocks * BYTES_PER_BLOCK + + ALLOC_TABLE_GAP_BYTE + + sizeof(mp_state_mem_area_t); + + // Round up size to the nearest multiple of BYTES_PER_BLOCK. + total_heap = (total_heap + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1)); + + DEBUG_printf("total_heap " UINT_FMT " bytes\n", total_heap); + + size_t to_alloc = MIN(avail, MAX(total_heap, needed)); + + mp_state_mem_area_t *new_heap = MP_PLAT_ALLOC_HEAP(to_alloc); + + DEBUG_printf("MP_PLAT_ALLOC_HEAP " UINT_FMT " = %p\n", + to_alloc, new_heap); + + if (new_heap == NULL) { + // This should only fail: + // - In a threaded environment if another thread has + // allocated while this function ran. + // - If there is a bug in gc_get_max_new_split(). + return false; + } + + gc_add(new_heap, (void *)new_heap + to_alloc); + + return true; +} +#endif + +#endif + +void gc_lock(void) { + // This does not need to be atomic or have the GC mutex because: + // - each thread has its own gc_lock_depth so there are no races between threads; + // - a hard interrupt will only change gc_lock_depth during its execution, and + // upon return will restore the value of gc_lock_depth. + MP_STATE_THREAD(gc_lock_depth)++; +} + +void gc_unlock(void) { + // This does not need to be atomic, See comment above in gc_lock. + MP_STATE_THREAD(gc_lock_depth)--; +} + +bool gc_is_locked(void) { + return MP_STATE_THREAD(gc_lock_depth) != 0; +} + +#if MICROPY_GC_SPLIT_HEAP +// Returns the area to which this pointer belongs, or NULL if it isn't +// allocated on the GC-managed heap. +static inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) { + if (((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) != 0) { // must be aligned on a block + return NULL; + } + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + if (ptr >= (void *)area->gc_pool_start // must be above start of pool + && ptr < (void *)area->gc_pool_end) { // must be below end of pool + return area; + } + } + return NULL; +} +#endif + +// ptr should be of type void* +#define VERIFY_PTR(ptr) ( \ + ((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \ + && ptr >= (void *)MP_STATE_MEM(area).gc_pool_start /* must be above start of pool */ \ + && ptr < (void *)MP_STATE_MEM(area).gc_pool_end /* must be below end of pool */ \ + ) + +#ifndef TRACE_MARK +#if DEBUG_PRINT +#define TRACE_MARK(block, ptr) DEBUG_printf("gc_mark(%p)\n", ptr) +#else +#define TRACE_MARK(block, ptr) +#endif +#endif + +// Take the given block as the topmost block on the stack. Check all it's +// children: mark the unmarked child blocks and put those newly marked +// blocks on the stack. When all children have been checked, pop off the +// topmost block on the stack and repeat with that one. +#if MICROPY_GC_SPLIT_HEAP +static void gc_mark_subtree(mp_state_mem_area_t *area, size_t block) +#else +static void gc_mark_subtree(size_t block) +#endif +{ + // Start with the block passed in the argument. + size_t sp = 0; + for (;;) { + #if !MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *area = &MP_STATE_MEM(area); + #endif + + // work out number of consecutive blocks in the chain starting with this one + size_t n_blocks = 0; + do { + n_blocks += 1; + } while (ATB_GET_KIND(area, block + n_blocks) == AT_TAIL); + + // check that the consecutive blocks didn't overflow past the end of the area + assert(area->gc_pool_start + (block + n_blocks) * BYTES_PER_BLOCK <= area->gc_pool_end); + + // check this block's children + void **ptrs = (void **)PTR_FROM_BLOCK(area, block); + for (size_t i = n_blocks * BYTES_PER_BLOCK / sizeof(void *); i > 0; i--, ptrs++) { + MICROPY_GC_HOOK_LOOP(i); + void *ptr = *ptrs; + // If this is a heap pointer that hasn't been marked, mark it and push + // it's children to the stack. + #if MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *ptr_area = gc_get_ptr_area(ptr); + if (!ptr_area) { + // Not a heap-allocated pointer (might even be random data). + continue; + } + #else + if (!VERIFY_PTR(ptr)) { + continue; + } + mp_state_mem_area_t *ptr_area = area; + #endif + size_t ptr_block = BLOCK_FROM_PTR(ptr_area, ptr); + if (ATB_GET_KIND(ptr_area, ptr_block) != AT_HEAD) { + // This block is already marked. + continue; + } + // An unmarked head. Mark it, and push it on gc stack. + TRACE_MARK(ptr_block, ptr); + ATB_HEAD_TO_MARK(ptr_area, ptr_block); + if (sp < MICROPY_ALLOC_GC_STACK_SIZE) { + MP_STATE_MEM(gc_block_stack)[sp] = ptr_block; + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_area_stack)[sp] = ptr_area; + #endif + sp += 1; + } else { + MP_STATE_MEM(gc_stack_overflow) = 1; + } + } + + // Are there any blocks on the stack? + if (sp == 0) { + break; // No, stack is empty, we're done. + } + + // pop the next block off the stack + sp -= 1; + block = MP_STATE_MEM(gc_block_stack)[sp]; + #if MICROPY_GC_SPLIT_HEAP + area = MP_STATE_MEM(gc_area_stack)[sp]; + #endif + } +} + +static void gc_deal_with_stack_overflow(void) { + while (MP_STATE_MEM(gc_stack_overflow)) { + MP_STATE_MEM(gc_stack_overflow) = 0; + + // scan entire memory looking for blocks which have been marked but not their children + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + for (size_t block = 0; block < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) { + MICROPY_GC_HOOK_LOOP(block); + // trace (again) if mark bit set + if (ATB_GET_KIND(area, block) == AT_MARK) { + #if MICROPY_GC_SPLIT_HEAP + gc_mark_subtree(area, block); + #else + gc_mark_subtree(block); + #endif + } + } + } + } +} + +static void gc_sweep(void) { + #if MICROPY_PY_GC_COLLECT_RETVAL + MP_STATE_MEM(gc_collected) = 0; + #endif + // free unmarked heads and their tails + int free_tail = 0; + #if MICROPY_GC_SPLIT_HEAP_AUTO + mp_state_mem_area_t *prev_area = NULL; + #endif + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + size_t end_block = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; + if (area->gc_last_used_block < end_block) { + end_block = area->gc_last_used_block + 1; + } + + size_t last_used_block = 0; + + for (size_t block = 0; block < end_block; block++) { + MICROPY_GC_HOOK_LOOP(block); + switch (ATB_GET_KIND(area, block)) { + case AT_HEAD: + #if MICROPY_ENABLE_FINALISER + if (FTB_GET(area, block)) { + mp_obj_base_t *obj = (mp_obj_base_t *)PTR_FROM_BLOCK(area, block); + if (obj->type != NULL) { + // if the object has a type then see if it has a __del__ method + mp_obj_t dest[2]; + mp_load_method_maybe(MP_OBJ_FROM_PTR(obj), MP_QSTR___del__, dest); + if (dest[0] != MP_OBJ_NULL) { + // load_method returned a method, execute it in a protected environment + #if MICROPY_ENABLE_SCHEDULER + mp_sched_lock(); + #endif + mp_call_function_1_protected(dest[0], dest[1]); + #if MICROPY_ENABLE_SCHEDULER + mp_sched_unlock(); + #endif + } + } + // clear finaliser flag + FTB_CLEAR(area, block); + } + #endif + free_tail = 1; + DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(area, block)); + #if MICROPY_PY_GC_COLLECT_RETVAL + MP_STATE_MEM(gc_collected)++; + #endif + // fall through to free the head + MP_FALLTHROUGH + + case AT_TAIL: + if (free_tail) { + ATB_ANY_TO_FREE(area, block); + #if CLEAR_ON_SWEEP + memset((void *)PTR_FROM_BLOCK(area, block), 0, BYTES_PER_BLOCK); + #endif + } else { + last_used_block = block; + } + break; + + case AT_MARK: + ATB_MARK_TO_HEAD(area, block); + free_tail = 0; + last_used_block = block; + break; + } + } + + area->gc_last_used_block = last_used_block; + + #if MICROPY_GC_SPLIT_HEAP_AUTO + // Free any empty area, aside from the first one + if (last_used_block == 0 && prev_area != NULL) { + DEBUG_printf("gc_sweep free empty area %p\n", area); + NEXT_AREA(prev_area) = NEXT_AREA(area); + MP_PLAT_FREE_HEAP(area); + area = prev_area; + } + prev_area = area; + #endif + } +} + +void gc_collect_start(void) { + GC_ENTER(); + MP_STATE_THREAD(gc_lock_depth)++; + #if MICROPY_GC_ALLOC_THRESHOLD + MP_STATE_MEM(gc_alloc_amount) = 0; + #endif + MP_STATE_MEM(gc_stack_overflow) = 0; + + // Trace root pointers. This relies on the root pointers being organised + // correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals, + // dict_globals, then the root pointer section of mp_state_vm. + void **ptrs = (void **)(void *)&mp_state_ctx; + size_t root_start = offsetof(mp_state_ctx_t, thread.dict_locals); + size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk); + gc_collect_root(ptrs + root_start / sizeof(void *), (root_end - root_start) / sizeof(void *)); + + #if MICROPY_ENABLE_PYSTACK + // Trace root pointers from the Python stack. + ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start); + gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *)); + #endif +} + +// Address sanitizer needs to know that the access to ptrs[i] must always be +// considered OK, even if it's a load from an address that would normally be +// prohibited (due to being undefined, in a red zone, etc). +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +__attribute__((no_sanitize_address)) +#endif +static void *gc_get_ptr(void **ptrs, int i) { + #if MICROPY_DEBUG_VALGRIND + if (!VALGRIND_CHECK_MEM_IS_ADDRESSABLE(&ptrs[i], sizeof(*ptrs))) { + return NULL; + } + #endif + return ptrs[i]; +} + +void gc_collect_root(void **ptrs, size_t len) { + #if !MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *area = &MP_STATE_MEM(area); + #endif + for (size_t i = 0; i < len; i++) { + MICROPY_GC_HOOK_LOOP(i); + void *ptr = gc_get_ptr(ptrs, i); + #if MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *area = gc_get_ptr_area(ptr); + if (!area) { + continue; + } + #else + if (!VERIFY_PTR(ptr)) { + continue; + } + #endif + size_t block = BLOCK_FROM_PTR(area, ptr); + if (ATB_GET_KIND(area, block) == AT_HEAD) { + // An unmarked head: mark it, and mark all its children + ATB_HEAD_TO_MARK(area, block); + #if MICROPY_GC_SPLIT_HEAP + gc_mark_subtree(area, block); + #else + gc_mark_subtree(block); + #endif + } + } +} + +void gc_collect_end(void) { + gc_deal_with_stack_overflow(); + gc_sweep(); + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + #endif + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + area->gc_last_free_atb_index = 0; + } + MP_STATE_THREAD(gc_lock_depth)--; + GC_EXIT(); +} + +void gc_sweep_all(void) { + GC_ENTER(); + MP_STATE_THREAD(gc_lock_depth)++; + MP_STATE_MEM(gc_stack_overflow) = 0; + gc_collect_end(); +} + +void gc_info(gc_info_t *info) { + GC_ENTER(); + info->total = 0; + info->used = 0; + info->free = 0; + info->max_free = 0; + info->num_1block = 0; + info->num_2block = 0; + info->max_block = 0; + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + bool finish = false; + info->total += area->gc_pool_end - area->gc_pool_start; + for (size_t block = 0, len = 0, len_free = 0; !finish;) { + MICROPY_GC_HOOK_LOOP(block); + size_t kind = ATB_GET_KIND(area, block); + switch (kind) { + case AT_FREE: + info->free += 1; + len_free += 1; + len = 0; + break; + + case AT_HEAD: + info->used += 1; + len = 1; + break; + + case AT_TAIL: + info->used += 1; + len += 1; + break; + + case AT_MARK: + // shouldn't happen + break; + } + + block++; + finish = (block == area->gc_alloc_table_byte_len * BLOCKS_PER_ATB); + // Get next block type if possible + if (!finish) { + kind = ATB_GET_KIND(area, block); + } + + if (finish || kind == AT_FREE || kind == AT_HEAD) { + if (len == 1) { + info->num_1block += 1; + } else if (len == 2) { + info->num_2block += 1; + } + if (len > info->max_block) { + info->max_block = len; + } + if (finish || kind == AT_HEAD) { + if (len_free > info->max_free) { + info->max_free = len_free; + } + len_free = 0; + } + } + } + } + + info->used *= BYTES_PER_BLOCK; + info->free *= BYTES_PER_BLOCK; + + #if MICROPY_GC_SPLIT_HEAP_AUTO + info->max_new_split = gc_get_max_new_split(); + #endif + + GC_EXIT(); +} + +void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { + bool has_finaliser = alloc_flags & GC_ALLOC_FLAG_HAS_FINALISER; + size_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK; + DEBUG_printf("gc_alloc(" UINT_FMT " bytes -> " UINT_FMT " blocks)\n", n_bytes, n_blocks); + + // check for 0 allocation + if (n_blocks == 0) { + return NULL; + } + + // check if GC is locked + if (MP_STATE_THREAD(gc_lock_depth) > 0) { + return NULL; + } + + GC_ENTER(); + + mp_state_mem_area_t *area; + size_t i; + size_t end_block; + size_t start_block; + size_t n_free; + int collected = !MP_STATE_MEM(gc_auto_collect_enabled); + #if MICROPY_GC_SPLIT_HEAP_AUTO + bool added = false; + #endif + + #if MICROPY_GC_ALLOC_THRESHOLD + if (!collected && MP_STATE_MEM(gc_alloc_amount) >= MP_STATE_MEM(gc_alloc_threshold)) { + GC_EXIT(); + gc_collect(); + collected = 1; + GC_ENTER(); + } + #endif + + for (;;) { + + #if MICROPY_GC_SPLIT_HEAP + area = MP_STATE_MEM(gc_last_free_area); + #else + area = &MP_STATE_MEM(area); + #endif + + // look for a run of n_blocks available blocks + for (; area != NULL; area = NEXT_AREA(area), i = 0) { + n_free = 0; + for (i = area->gc_last_free_atb_index; i < area->gc_alloc_table_byte_len; i++) { + MICROPY_GC_HOOK_LOOP(i); + byte a = area->gc_alloc_table_start[i]; + // *FORMAT-OFF* + if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; } + if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; } + if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; } + if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; } + // *FORMAT-ON* + } + + // No free blocks found on this heap. Mark this heap as + // filled, so we won't try to find free space here again until + // space is freed. + #if MICROPY_GC_SPLIT_HEAP + if (n_blocks == 1) { + area->gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB; // or (size_t)-1 + } + #endif + } + + GC_EXIT(); + // nothing found! + if (collected) { + #if MICROPY_GC_SPLIT_HEAP_AUTO + if (!added && gc_try_add_heap(n_bytes)) { + added = true; + continue; + } + #endif + return NULL; + } + DEBUG_printf("gc_alloc(" UINT_FMT "): no free mem, triggering GC\n", n_bytes); + gc_collect(); + collected = 1; + GC_ENTER(); + } + + // found, ending at block i inclusive +found: + // get starting and end blocks, both inclusive + end_block = i; + start_block = i - n_free + 1; + + // Set last free ATB index to block after last block we found, for start of + // next scan. To reduce fragmentation, we only do this if we were looking + // for a single free block, which guarantees that there are no free blocks + // before this one. Also, whenever we free or shink a block we must check + // if this index needs adjusting (see gc_realloc and gc_free). + if (n_free == 1) { + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_last_free_area) = area; + #endif + area->gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB; + } + + area->gc_last_used_block = MAX(area->gc_last_used_block, end_block); + + // mark first block as used head + ATB_FREE_TO_HEAD(area, start_block); + + // mark rest of blocks as used tail + // TODO for a run of many blocks can make this more efficient + for (size_t bl = start_block + 1; bl <= end_block; bl++) { + ATB_FREE_TO_TAIL(area, bl); + } + + // get pointer to first block + // we must create this pointer before unlocking the GC so a collection can find it + void *ret_ptr = (void *)(area->gc_pool_start + start_block * BYTES_PER_BLOCK); + DEBUG_printf("gc_alloc(%p)\n", ret_ptr); + + #if MICROPY_GC_ALLOC_THRESHOLD + MP_STATE_MEM(gc_alloc_amount) += n_blocks; + #endif + + GC_EXIT(); + + #if MICROPY_GC_CONSERVATIVE_CLEAR + // be conservative and zero out all the newly allocated blocks + memset((byte *)ret_ptr, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK); + #else + // zero out the additional bytes of the newly allocated blocks + // This is needed because the blocks may have previously held pointers + // to the heap and will not be set to something else if the caller + // doesn't actually use the entire block. As such they will continue + // to point to the heap and may prevent other blocks from being reclaimed. + memset((byte *)ret_ptr + n_bytes, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK - n_bytes); + #endif + + #if MICROPY_ENABLE_FINALISER + if (has_finaliser) { + // clear type pointer in case it is never set + ((mp_obj_base_t *)ret_ptr)->type = NULL; + // set mp_obj flag only if it has a finaliser + GC_ENTER(); + FTB_SET(area, start_block); + GC_EXIT(); + } + #else + (void)has_finaliser; + #endif + + #if EXTENSIVE_HEAP_PROFILING + gc_dump_alloc_table(&mp_plat_print); + #endif + + return ret_ptr; +} + +/* +void *gc_alloc(mp_uint_t n_bytes) { + return _gc_alloc(n_bytes, false); +} + +void *gc_alloc_with_finaliser(mp_uint_t n_bytes) { + return _gc_alloc(n_bytes, true); +} +*/ + +// force the freeing of a piece of memory +// TODO: freeing here does not call finaliser +void gc_free(void *ptr) { + if (MP_STATE_THREAD(gc_lock_depth) > 0) { + // Cannot free while the GC is locked. However free is an optimisation + // to reclaim the memory immediately, this means it will now be left + // until the next collection. + return; + } + + GC_ENTER(); + + DEBUG_printf("gc_free(%p)\n", ptr); + + if (ptr == NULL) { + // free(NULL) is a no-op + GC_EXIT(); + return; + } + + // get the GC block number corresponding to this pointer + mp_state_mem_area_t *area; + #if MICROPY_GC_SPLIT_HEAP + area = gc_get_ptr_area(ptr); + assert(area); + #else + assert(VERIFY_PTR(ptr)); + area = &MP_STATE_MEM(area); + #endif + + size_t block = BLOCK_FROM_PTR(area, ptr); + assert(ATB_GET_KIND(area, block) == AT_HEAD); + + #if MICROPY_ENABLE_FINALISER + FTB_CLEAR(area, block); + #endif + + #if MICROPY_GC_SPLIT_HEAP + if (MP_STATE_MEM(gc_last_free_area) != area) { + // We freed something but it isn't the current area. Reset the + // last free area to the start for a rescan. Note that this won't + // give much of a performance hit, since areas that are completely + // filled will likely be skipped (the gc_last_free_atb_index + // points to the last block). + // The reason why this is necessary is because it is not possible + // to see which area came first (like it is possible to adjust + // gc_last_free_atb_index based on whether the freed block is + // before the last free block). + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + } + #endif + + // set the last_free pointer to this block if it's earlier in the heap + if (block / BLOCKS_PER_ATB < area->gc_last_free_atb_index) { + area->gc_last_free_atb_index = block / BLOCKS_PER_ATB; + } + + // free head and all of its tail blocks + do { + ATB_ANY_TO_FREE(area, block); + block += 1; + } while (ATB_GET_KIND(area, block) == AT_TAIL); + + GC_EXIT(); + + #if EXTENSIVE_HEAP_PROFILING + gc_dump_alloc_table(&mp_plat_print); + #endif +} + +size_t gc_nbytes(const void *ptr) { + GC_ENTER(); + + mp_state_mem_area_t *area; + #if MICROPY_GC_SPLIT_HEAP + area = gc_get_ptr_area(ptr); + #else + if (VERIFY_PTR(ptr)) { + area = &MP_STATE_MEM(area); + } else { + area = NULL; + } + #endif + + if (area) { + size_t block = BLOCK_FROM_PTR(area, ptr); + if (ATB_GET_KIND(area, block) == AT_HEAD) { + // work out number of consecutive blocks in the chain starting with this on + size_t n_blocks = 0; + do { + n_blocks += 1; + } while (ATB_GET_KIND(area, block + n_blocks) == AT_TAIL); + GC_EXIT(); + return n_blocks * BYTES_PER_BLOCK; + } + } + + // invalid pointer + GC_EXIT(); + return 0; +} + +#if 0 +// old, simple realloc that didn't expand memory in place +void *gc_realloc(void *ptr, mp_uint_t n_bytes) { + mp_uint_t n_existing = gc_nbytes(ptr); + if (n_bytes <= n_existing) { + return ptr; + } else { + bool has_finaliser; + if (ptr == NULL) { + has_finaliser = false; + } else { + #if MICROPY_ENABLE_FINALISER + has_finaliser = FTB_GET(BLOCK_FROM_PTR((mp_uint_t)ptr)); + #else + has_finaliser = false; + #endif + } + void *ptr2 = gc_alloc(n_bytes, has_finaliser); + if (ptr2 == NULL) { + return ptr2; + } + memcpy(ptr2, ptr, n_existing); + gc_free(ptr); + return ptr2; + } +} + +#else // Alternative gc_realloc impl + +void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { + // check for pure allocation + if (ptr_in == NULL) { + return gc_alloc(n_bytes, false); + } + + // check for pure free + if (n_bytes == 0) { + gc_free(ptr_in); + return NULL; + } + + if (MP_STATE_THREAD(gc_lock_depth) > 0) { + return NULL; + } + + void *ptr = ptr_in; + + GC_ENTER(); + + // get the GC block number corresponding to this pointer + mp_state_mem_area_t *area; + #if MICROPY_GC_SPLIT_HEAP + area = gc_get_ptr_area(ptr); + assert(area); + #else + assert(VERIFY_PTR(ptr)); + area = &MP_STATE_MEM(area); + #endif + size_t block = BLOCK_FROM_PTR(area, ptr); + assert(ATB_GET_KIND(area, block) == AT_HEAD); + + // compute number of new blocks that are requested + size_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK; + + // Get the total number of consecutive blocks that are already allocated to + // this chunk of memory, and then count the number of free blocks following + // it. Stop if we reach the end of the heap, or if we find enough extra + // free blocks to satisfy the realloc. Note that we need to compute the + // total size of the existing memory chunk so we can correctly and + // efficiently shrink it (see below for shrinking code). + size_t n_free = 0; + size_t n_blocks = 1; // counting HEAD block + size_t max_block = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; + for (size_t bl = block + n_blocks; bl < max_block; bl++) { + byte block_type = ATB_GET_KIND(area, bl); + if (block_type == AT_TAIL) { + n_blocks++; + continue; + } + if (block_type == AT_FREE) { + n_free++; + if (n_blocks + n_free >= new_blocks) { + // stop as soon as we find enough blocks for n_bytes + break; + } + continue; + } + break; + } + + // return original ptr if it already has the requested number of blocks + if (new_blocks == n_blocks) { + GC_EXIT(); + return ptr_in; + } + + // check if we can shrink the allocated area + if (new_blocks < n_blocks) { + // free unneeded tail blocks + for (size_t bl = block + new_blocks, count = n_blocks - new_blocks; count > 0; bl++, count--) { + ATB_ANY_TO_FREE(area, bl); + } + + #if MICROPY_GC_SPLIT_HEAP + if (MP_STATE_MEM(gc_last_free_area) != area) { + // See comment in gc_free. + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + } + #endif + + // set the last_free pointer to end of this block if it's earlier in the heap + if ((block + new_blocks) / BLOCKS_PER_ATB < area->gc_last_free_atb_index) { + area->gc_last_free_atb_index = (block + new_blocks) / BLOCKS_PER_ATB; + } + + GC_EXIT(); + + #if EXTENSIVE_HEAP_PROFILING + gc_dump_alloc_table(&mp_plat_print); + #endif + + return ptr_in; + } + + // check if we can expand in place + if (new_blocks <= n_blocks + n_free) { + // mark few more blocks as used tail + size_t end_block = block + new_blocks; + for (size_t bl = block + n_blocks; bl < end_block; bl++) { + assert(ATB_GET_KIND(area, bl) == AT_FREE); + ATB_FREE_TO_TAIL(area, bl); + } + + area->gc_last_used_block = MAX(area->gc_last_used_block, end_block); + + GC_EXIT(); + + #if MICROPY_GC_CONSERVATIVE_CLEAR + // be conservative and zero out all the newly allocated blocks + memset((byte *)ptr_in + n_blocks * BYTES_PER_BLOCK, 0, (new_blocks - n_blocks) * BYTES_PER_BLOCK); + #else + // zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc) + memset((byte *)ptr_in + n_bytes, 0, new_blocks * BYTES_PER_BLOCK - n_bytes); + #endif + + #if EXTENSIVE_HEAP_PROFILING + gc_dump_alloc_table(&mp_plat_print); + #endif + + return ptr_in; + } + + #if MICROPY_ENABLE_FINALISER + bool ftb_state = FTB_GET(area, block); + #else + bool ftb_state = false; + #endif + + GC_EXIT(); + + if (!allow_move) { + // not allowed to move memory block so return failure + return NULL; + } + + // can't resize inplace; try to find a new contiguous chain + void *ptr_out = gc_alloc(n_bytes, ftb_state); + + // check that the alloc succeeded + if (ptr_out == NULL) { + return NULL; + } + + DEBUG_printf("gc_realloc(%p -> %p)\n", ptr_in, ptr_out); + memcpy(ptr_out, ptr_in, n_blocks * BYTES_PER_BLOCK); + gc_free(ptr_in); + return ptr_out; +} +#endif // Alternative gc_realloc impl + +void gc_dump_info(const mp_print_t *print) { + gc_info_t info; + gc_info(&info); + mp_printf(print, "GC: total: %u, used: %u, free: %u", + (uint)info.total, (uint)info.used, (uint)info.free); + #if MICROPY_GC_SPLIT_HEAP_AUTO + mp_printf(print, ", max new split: %u", (uint)info.max_new_split); + #endif + mp_printf(print, "\n No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n", + (uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free); +} + +void gc_dump_alloc_table(const mp_print_t *print) { + GC_ENTER(); + static const size_t DUMP_BYTES_PER_LINE = 64; + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + #if !EXTENSIVE_HEAP_PROFILING + // When comparing heap output we don't want to print the starting + // pointer of the heap because it changes from run to run. + mp_printf(print, "GC memory layout; from %p:", area->gc_pool_start); + #endif + for (size_t bl = 0; bl < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) { + if (bl % DUMP_BYTES_PER_LINE == 0) { + // a new line of blocks + { + // check if this line contains only free blocks + size_t bl2 = bl; + while (bl2 < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB && ATB_GET_KIND(area, bl2) == AT_FREE) { + bl2++; + } + if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) { + // there are at least 2 lines containing only free blocks, so abbreviate their printing + mp_printf(print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); + bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); + if (bl >= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB) { + // got to end of heap + break; + } + } + } + // print header for new line of blocks + // (the cast to uint32_t is for 16-bit ports) + mp_printf(print, "\n%08x: ", (uint)(bl * BYTES_PER_BLOCK)); + } + int c = ' '; + switch (ATB_GET_KIND(area, bl)) { + case AT_FREE: + c = '.'; + break; + /* this prints out if the object is reachable from BSS or STACK (for unix only) + case AT_HEAD: { + c = 'h'; + void **ptrs = (void**)(void*)&mp_state_ctx; + mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t); + for (mp_uint_t i = 0; i < len; i++) { + mp_uint_t ptr = (mp_uint_t)ptrs[i]; + if (gc_get_ptr_area(ptr) && BLOCK_FROM_PTR(ptr) == bl) { + c = 'B'; + break; + } + } + if (c == 'h') { + ptrs = (void**)&c; + len = ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)&c) / sizeof(mp_uint_t); + for (mp_uint_t i = 0; i < len; i++) { + mp_uint_t ptr = (mp_uint_t)ptrs[i]; + if (gc_get_ptr_area(ptr) && BLOCK_FROM_PTR(ptr) == bl) { + c = 'S'; + break; + } + } + } + break; + } + */ + /* this prints the uPy object type of the head block */ + case AT_HEAD: { + void **ptr = (void **)(area->gc_pool_start + bl * BYTES_PER_BLOCK); + if (*ptr == &mp_type_tuple) { + c = 'T'; + } else if (*ptr == &mp_type_list) { + c = 'L'; + } else if (*ptr == &mp_type_dict) { + c = 'D'; + } else if (*ptr == &mp_type_str || *ptr == &mp_type_bytes) { + c = 'S'; + } + #if MICROPY_PY_BUILTINS_BYTEARRAY + else if (*ptr == &mp_type_bytearray) { + c = 'A'; + } + #endif + #if MICROPY_PY_ARRAY + else if (*ptr == &mp_type_array) { + c = 'A'; + } + #endif + #if MICROPY_PY_BUILTINS_FLOAT + else if (*ptr == &mp_type_float) { + c = 'F'; + } + #endif + else if (*ptr == &mp_type_fun_bc) { + c = 'B'; + } else if (*ptr == &mp_type_module) { + c = 'M'; + } else { + c = 'h'; + #if 0 + // This code prints "Q" for qstr-pool data, and "q" for qstr-str + // data. It can be useful to see how qstrs are being allocated, + // but is disabled by default because it is very slow. + for (qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) { + if ((qstr_pool_t *)ptr == pool) { + c = 'Q'; + break; + } + for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { + if ((const byte *)ptr == *q) { + c = 'q'; + break; + } + } + } + #endif + } + break; + } + case AT_TAIL: + c = '='; + break; + case AT_MARK: + c = 'm'; + break; + } + mp_printf(print, "%c", c); + } + mp_print_str(print, "\n"); + } + GC_EXIT(); +} + +#if 0 +// For testing the GC functions +void gc_test(void) { + mp_uint_t len = 500; + mp_uint_t *heap = malloc(len); + gc_init(heap, heap + len / sizeof(mp_uint_t)); + void *ptrs[100]; + { + mp_uint_t **p = gc_alloc(16, false); + p[0] = gc_alloc(64, false); + p[1] = gc_alloc(1, false); + p[2] = gc_alloc(1, false); + p[3] = gc_alloc(1, false); + mp_uint_t ***p2 = gc_alloc(16, false); + p2[0] = p; + p2[1] = p; + ptrs[0] = p2; + } + for (int i = 0; i < 25; i += 2) { + mp_uint_t *p = gc_alloc(i, false); + printf("p=%p\n", p); + if (i & 3) { + // ptrs[i] = p; + } + } + + printf("Before GC:\n"); + gc_dump_alloc_table(&mp_plat_print); + printf("Starting GC...\n"); + gc_collect_start(); + gc_collect_root(ptrs, sizeof(ptrs) / sizeof(void *)); + gc_collect_end(); + printf("After GC:\n"); + gc_dump_alloc_table(&mp_plat_print); +} +#endif + +#endif // MICROPY_ENABLE_GC diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/gc.h b/non_catalog_apps/mp_flipper/lib/micropython/py/gc.h new file mode 100644 index 00000000..36177633 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/gc.h @@ -0,0 +1,87 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_GC_H +#define MICROPY_INCLUDED_PY_GC_H + +#include +#include +#include "py/mpprint.h" + +void gc_init(void *start, void *end); + +#if MICROPY_GC_SPLIT_HEAP +// Used to add additional memory areas to the heap. +void gc_add(void *start, void *end); + +#if MICROPY_GC_SPLIT_HEAP_AUTO +// Port must implement this function to return the maximum available block of +// RAM to allocate a new heap area into using MP_PLAT_ALLOC_HEAP. +size_t gc_get_max_new_split(void); +#endif // MICROPY_GC_SPLIT_HEAP_AUTO +#endif // MICROPY_GC_SPLIT_HEAP + +// These lock/unlock functions can be nested. +// They can be used to prevent the GC from allocating/freeing. +void gc_lock(void); +void gc_unlock(void); +bool gc_is_locked(void); + +// A given port must implement gc_collect by using the other collect functions. +void gc_collect(void); +void gc_collect_start(void); +void gc_collect_root(void **ptrs, size_t len); +void gc_collect_end(void); + +// Use this function to sweep the whole heap and run all finalisers +void gc_sweep_all(void); + +enum { + GC_ALLOC_FLAG_HAS_FINALISER = 1, +}; + +void *gc_alloc(size_t n_bytes, unsigned int alloc_flags); +void gc_free(void *ptr); // does not call finaliser +size_t gc_nbytes(const void *ptr); +void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move); + +typedef struct _gc_info_t { + size_t total; + size_t used; + size_t free; + size_t max_free; + size_t num_1block; + size_t num_2block; + size_t max_block; + #if MICROPY_GC_SPLIT_HEAP_AUTO + size_t max_new_split; + #endif +} gc_info_t; + +void gc_info(gc_info_t *info); +void gc_dump_info(const mp_print_t *print); +void gc_dump_alloc_table(const mp_print_t *print); + +#endif // MICROPY_INCLUDED_PY_GC_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/grammar.h b/non_catalog_apps/mp_flipper/lib/micropython/py/grammar.h new file mode 100644 index 00000000..285fbded --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/grammar.h @@ -0,0 +1,372 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Damien P. George + * + * 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. + */ + +// *FORMAT-OFF* + +// rules for writing rules: +// - zero_or_more is implemented using opt_rule around a one_or_more rule +// - don't put opt_rule in arguments of or rule; instead, wrap the call to this or rule in opt_rule + +// Generic sub-rules used by multiple rules below. + +DEF_RULE_NC(generic_colon_test, and_ident(2), tok(DEL_COLON), rule(test)) +DEF_RULE_NC(generic_equal_test, and_ident(2), tok(DEL_EQUAL), rule(test)) + +// # Start symbols for the grammar: +// # single_input is a single interactive statement; +// # file_input is a module or sequence of commands read from an input file; +// # eval_input is the input for the eval() functions. +// # NB: compound_stmt in single_input is followed by extra NEWLINE! --> not in MicroPython +// single_input: NEWLINE | simple_stmt | compound_stmt +// file_input: (NEWLINE | stmt)* ENDMARKER +// eval_input: testlist NEWLINE* ENDMARKER + +DEF_RULE_NC(single_input, or(3), tok(NEWLINE), rule(simple_stmt), rule(compound_stmt)) +DEF_RULE(file_input, c(generic_all_nodes), and_ident(1), opt_rule(file_input_2)) +DEF_RULE(file_input_2, c(generic_all_nodes), one_or_more, rule(file_input_3)) +DEF_RULE_NC(file_input_3, or(2), tok(NEWLINE), rule(stmt)) +DEF_RULE_NC(eval_input, and_ident(2), rule(testlist), opt_rule(eval_input_2)) +DEF_RULE_NC(eval_input_2, and(1), tok(NEWLINE)) + +// decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE +// decorators: decorator+ +// decorated: decorators (classdef | funcdef | async_funcdef) +// funcdef: 'def' NAME parameters ['->' test] ':' suite +// async_funcdef: 'async' funcdef +// parameters: '(' [typedargslist] ')' +// typedargslist: tfpdef ['=' test] (',' tfpdef ['=' test])* [',' ['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]] | '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef +// tfpdef: NAME [':' test] +// varargslist: vfpdef ['=' test] (',' vfpdef ['=' test])* [',' ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef +// vfpdef: NAME + +DEF_RULE_NC(decorator, and(4), tok(OP_AT), rule(dotted_name), opt_rule(trailer_paren), tok(NEWLINE)) +DEF_RULE_NC(decorators, one_or_more, rule(decorator)) +DEF_RULE(decorated, c(decorated), and_ident(2), rule(decorators), rule(decorated_body)) +#if MICROPY_PY_ASYNC_AWAIT +DEF_RULE_NC(decorated_body, or(3), rule(classdef), rule(funcdef), rule(async_funcdef)) +DEF_RULE_NC(async_funcdef, and(2), tok(KW_ASYNC), rule(funcdef)) +#else +DEF_RULE_NC(decorated_body, or(2), rule(classdef), rule(funcdef)) +#endif +DEF_RULE(funcdef, c(funcdef), and_blank(8), tok(KW_DEF), tok(NAME), tok(DEL_PAREN_OPEN), opt_rule(typedargslist), tok(DEL_PAREN_CLOSE), opt_rule(funcdefrettype), tok(DEL_COLON), rule(suite)) +DEF_RULE_NC(funcdefrettype, and_ident(2), tok(DEL_MINUS_MORE), rule(test)) +// note: typedargslist lets through more than is allowed, compiler does further checks +DEF_RULE_NC(typedargslist, list_with_end, rule(typedargslist_item), tok(DEL_COMMA)) +DEF_RULE_NC(typedargslist_item, or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star)) +DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(generic_colon_test), opt_rule(generic_equal_test)) +DEF_RULE_NC(typedargslist_star, and(2), tok(OP_STAR), opt_rule(tfpdef)) +DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(generic_colon_test)) +DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(generic_colon_test)) +// note: varargslist lets through more than is allowed, compiler does further checks +DEF_RULE_NC(varargslist, list_with_end, rule(varargslist_item), tok(DEL_COMMA)) +DEF_RULE_NC(varargslist_item, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star)) +DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(generic_equal_test)) +DEF_RULE_NC(varargslist_star, and(2), tok(OP_STAR), opt_rule(vfpdef)) +DEF_RULE_NC(varargslist_dbl_star, and(2), tok(OP_DBL_STAR), tok(NAME)) +DEF_RULE_NC(vfpdef, and_ident(1), tok(NAME)) + +// stmt: compound_stmt | simple_stmt + +DEF_RULE_NC(stmt, or(2), rule(compound_stmt), rule(simple_stmt)) + +// simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE + +DEF_RULE_NC(simple_stmt, and_ident(2), rule(simple_stmt_2), tok(NEWLINE)) +DEF_RULE(simple_stmt_2, c(generic_all_nodes), list_with_end, rule(small_stmt), tok(DEL_SEMICOLON)) + +// small_stmt: expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt +// expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*) +// testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +// annassign: ':' test ['=' (yield_expr|testlist_star_expr)] +// augassign: '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' +// # For normal and annotated assignments, additional restrictions enforced by the interpreter + +DEF_RULE_NC(small_stmt, or(8), rule(del_stmt), rule(pass_stmt), rule(flow_stmt), rule(import_stmt), rule(global_stmt), rule(nonlocal_stmt), rule(assert_stmt), rule(expr_stmt)) +DEF_RULE(expr_stmt, c(expr_stmt), and(2), rule(testlist_star_expr), opt_rule(expr_stmt_2)) +DEF_RULE_NC(expr_stmt_2, or(3), rule(annassign), rule(expr_stmt_augassign), rule(expr_stmt_assign_list)) +DEF_RULE_NC(expr_stmt_augassign, and_ident(2), rule(augassign), rule(expr_stmt_6)) +DEF_RULE_NC(expr_stmt_assign_list, one_or_more, rule(expr_stmt_assign)) +DEF_RULE_NC(expr_stmt_assign, and_ident(2), tok(DEL_EQUAL), rule(expr_stmt_6)) +DEF_RULE_NC(expr_stmt_6, or(2), rule(yield_expr), rule(testlist_star_expr)) +DEF_RULE(testlist_star_expr, c(generic_tuple), list_with_end, rule(testlist_star_expr_2), tok(DEL_COMMA)) +DEF_RULE_NC(testlist_star_expr_2, or(2), rule(star_expr), rule(test)) +DEF_RULE_NC(annassign, and(3), tok(DEL_COLON), rule(test), opt_rule(expr_stmt_assign)) +DEF_RULE_NC(augassign, or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_AT_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL)) + +// del_stmt: 'del' exprlist +// pass_stmt: 'pass' +// flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +// break_stmt: 'break' +// continue_stmt: 'continue' +// return_stmt: 'return' [testlist] +// yield_stmt: yield_expr +// raise_stmt: 'raise' [test ['from' test]] + +DEF_RULE(del_stmt, c(del_stmt), and(2), tok(KW_DEL), rule(exprlist)) +DEF_RULE(pass_stmt, c(generic_all_nodes), and(1), tok(KW_PASS)) +DEF_RULE_NC(flow_stmt, or(5), rule(break_stmt), rule(continue_stmt), rule(return_stmt), rule(raise_stmt), rule(yield_stmt)) +DEF_RULE(break_stmt, c(break_cont_stmt), and(1), tok(KW_BREAK)) +DEF_RULE(continue_stmt, c(break_cont_stmt), and(1), tok(KW_CONTINUE)) +DEF_RULE(return_stmt, c(return_stmt), and(2), tok(KW_RETURN), opt_rule(testlist)) +DEF_RULE(yield_stmt, c(yield_stmt), and(1), rule(yield_expr)) +DEF_RULE(raise_stmt, c(raise_stmt), and(2), tok(KW_RAISE), opt_rule(raise_stmt_arg)) +DEF_RULE_NC(raise_stmt_arg, and_ident(2), rule(test), opt_rule(raise_stmt_from)) +DEF_RULE_NC(raise_stmt_from, and_ident(2), tok(KW_FROM), rule(test)) + +// import_stmt: import_name | import_from +// import_name: 'import' dotted_as_names +// import_from: 'from' (('.' | '...')* dotted_name | ('.' | '...')+) 'import' ('*' | '(' import_as_names ')' | import_as_names) +// import_as_name: NAME ['as' NAME] +// dotted_as_name: dotted_name ['as' NAME] +// import_as_names: import_as_name (',' import_as_name)* [','] +// dotted_as_names: dotted_as_name (',' dotted_as_name)* +// dotted_name: NAME ('.' NAME)* +// global_stmt: 'global' NAME (',' NAME)* +// nonlocal_stmt: 'nonlocal' NAME (',' NAME)* +// assert_stmt: 'assert' test [',' test] + +DEF_RULE_NC(import_stmt, or(2), rule(import_name), rule(import_from)) +DEF_RULE(import_name, c(import_name), and(2), tok(KW_IMPORT), rule(dotted_as_names)) +DEF_RULE(import_from, c(import_from), and(4), tok(KW_FROM), rule(import_from_2), tok(KW_IMPORT), rule(import_from_3)) +DEF_RULE_NC(import_from_2, or(2), rule(dotted_name), rule(import_from_2b)) +DEF_RULE_NC(import_from_2b, and_ident(2), rule(one_or_more_period_or_ellipsis), opt_rule(dotted_name)) +DEF_RULE_NC(import_from_3, or(3), tok(OP_STAR), rule(import_as_names_paren), rule(import_as_names)) +DEF_RULE_NC(import_as_names_paren, and_ident(3), tok(DEL_PAREN_OPEN), rule(import_as_names), tok(DEL_PAREN_CLOSE)) +DEF_RULE_NC(one_or_more_period_or_ellipsis, one_or_more, rule(period_or_ellipsis)) +DEF_RULE_NC(period_or_ellipsis, or(2), tok(DEL_PERIOD), tok(ELLIPSIS)) +DEF_RULE_NC(import_as_name, and(2), tok(NAME), opt_rule(as_name)) +DEF_RULE_NC(dotted_as_name, and_ident(2), rule(dotted_name), opt_rule(as_name)) +DEF_RULE_NC(as_name, and_ident(2), tok(KW_AS), tok(NAME)) +DEF_RULE_NC(import_as_names, list_with_end, rule(import_as_name), tok(DEL_COMMA)) +DEF_RULE_NC(dotted_as_names, list, rule(dotted_as_name), tok(DEL_COMMA)) +DEF_RULE_NC(dotted_name, list, tok(NAME), tok(DEL_PERIOD)) +DEF_RULE(global_stmt, c(global_nonlocal_stmt), and(2), tok(KW_GLOBAL), rule(name_list)) +DEF_RULE(nonlocal_stmt, c(global_nonlocal_stmt), and(2), tok(KW_NONLOCAL), rule(name_list)) +DEF_RULE_NC(name_list, list, tok(NAME), tok(DEL_COMMA)) +DEF_RULE(assert_stmt, c(assert_stmt), and(3), tok(KW_ASSERT), rule(test), opt_rule(assert_stmt_extra)) +DEF_RULE_NC(assert_stmt_extra, and_ident(2), tok(DEL_COMMA), rule(test)) + +// compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt +// if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] +// while_stmt: 'while' test ':' suite ['else' ':' suite] +// for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +// try_stmt: 'try' ':' suite ((except_clause ':' suite)+ ['else' ':' suite] ['finally' ':' suite] | 'finally' ':' suite) +// # NB compile.c makes sure that the default except clause is last +// except_clause: 'except' [test ['as' NAME]] +// with_stmt: 'with' with_item (',' with_item)* ':' suite +// with_item: test ['as' expr] +// suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT +// async_stmt: 'async' (funcdef | with_stmt | for_stmt) + +#if MICROPY_PY_ASYNC_AWAIT +DEF_RULE_NC(compound_stmt, or(9), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated), rule(async_stmt)) +DEF_RULE(async_stmt, c(async_stmt), and(2), tok(KW_ASYNC), rule(async_stmt_2)) +DEF_RULE_NC(async_stmt_2, or(3), rule(funcdef), rule(with_stmt), rule(for_stmt)) +#else +DEF_RULE_NC(compound_stmt, or(8), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated)) +#endif +DEF_RULE(if_stmt, c(if_stmt), and(6), tok(KW_IF), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(if_stmt_elif_list), opt_rule(else_stmt)) +DEF_RULE_NC(if_stmt_elif_list, one_or_more, rule(if_stmt_elif)) +DEF_RULE_NC(if_stmt_elif, and(4), tok(KW_ELIF), rule(namedexpr_test), tok(DEL_COLON), rule(suite)) +DEF_RULE(while_stmt, c(while_stmt), and(5), tok(KW_WHILE), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) +DEF_RULE(for_stmt, c(for_stmt), and(7), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(testlist), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) +DEF_RULE(try_stmt, c(try_stmt), and(4), tok(KW_TRY), tok(DEL_COLON), rule(suite), rule(try_stmt_2)) +DEF_RULE_NC(try_stmt_2, or(2), rule(try_stmt_except_and_more), rule(try_stmt_finally)) +DEF_RULE_NC(try_stmt_except_and_more, and_ident(3), rule(try_stmt_except_list), opt_rule(else_stmt), opt_rule(try_stmt_finally)) +DEF_RULE_NC(try_stmt_except, and(4), tok(KW_EXCEPT), opt_rule(try_stmt_as_name), tok(DEL_COLON), rule(suite)) +DEF_RULE_NC(try_stmt_as_name, and_ident(2), rule(test), opt_rule(as_name)) +DEF_RULE_NC(try_stmt_except_list, one_or_more, rule(try_stmt_except)) +DEF_RULE_NC(try_stmt_finally, and(3), tok(KW_FINALLY), tok(DEL_COLON), rule(suite)) +DEF_RULE_NC(else_stmt, and_ident(3), tok(KW_ELSE), tok(DEL_COLON), rule(suite)) +DEF_RULE(with_stmt, c(with_stmt), and(4), tok(KW_WITH), rule(with_stmt_list), tok(DEL_COLON), rule(suite)) +DEF_RULE_NC(with_stmt_list, list, rule(with_item), tok(DEL_COMMA)) +DEF_RULE_NC(with_item, and_ident(2), rule(test), opt_rule(with_item_as)) +DEF_RULE_NC(with_item_as, and_ident(2), tok(KW_AS), rule(expr)) +DEF_RULE_NC(suite, or(2), rule(suite_block), rule(simple_stmt)) +DEF_RULE_NC(suite_block, and_ident(4), tok(NEWLINE), tok(INDENT), rule(suite_block_stmts), tok(DEDENT)) +DEF_RULE(suite_block_stmts, c(generic_all_nodes), one_or_more, rule(stmt)) + +// test: or_test ['if' or_test 'else' test] | lambdef +// test_nocond: or_test | lambdef_nocond +// lambdef: 'lambda' [varargslist] ':' test +// lambdef_nocond: 'lambda' [varargslist] ':' test_nocond + +#if MICROPY_PY_ASSIGN_EXPR +DEF_RULE(namedexpr_test, c(namedexpr), and_ident(2), rule(test), opt_rule(namedexpr_test_2)) +DEF_RULE_NC(namedexpr_test_2, and_ident(2), tok(OP_ASSIGN), rule(test)) +#else +DEF_RULE_NC(namedexpr_test, or(1), rule(test)) +#endif +DEF_RULE_NC(test, or(2), rule(lambdef), rule(test_if_expr)) +DEF_RULE(test_if_expr, c(test_if_expr), and_ident(2), rule(or_test), opt_rule(test_if_else)) +DEF_RULE_NC(test_if_else, and(4), tok(KW_IF), rule(or_test), tok(KW_ELSE), rule(test)) +DEF_RULE_NC(test_nocond, or(2), rule(lambdef_nocond), rule(or_test)) +DEF_RULE(lambdef, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(varargslist), tok(DEL_COLON), rule(test)) +DEF_RULE(lambdef_nocond, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(varargslist), tok(DEL_COLON), rule(test_nocond)) + +// or_test: and_test ('or' and_test)* +// and_test: not_test ('and' not_test)* +// not_test: 'not' not_test | comparison +// comparison: expr (comp_op expr)* +// comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is'|'is' 'not' +// star_expr: '*' expr +// expr: xor_expr ('|' xor_expr)* +// xor_expr: and_expr ('^' and_expr)* +// and_expr: shift_expr ('&' shift_expr)* +// shift_expr: arith_expr (('<<'|'>>') arith_expr)* +// arith_expr: term (('+'|'-') term)* +// term: factor (('*'|'@'|'/'|'%'|'//') factor)* +// factor: ('+'|'-'|'~') factor | power +// power: atom_expr ['**' factor] +// atom_expr: 'await' atom trailer* | atom trailer* + +DEF_RULE(or_test, c(or_and_test), list, rule(and_test), tok(KW_OR)) +DEF_RULE(and_test, c(or_and_test), list, rule(not_test), tok(KW_AND)) +DEF_RULE_NC(not_test, or(2), rule(not_test_2), rule(comparison)) +DEF_RULE(not_test_2, c(not_test_2), and(2), tok(KW_NOT), rule(not_test)) +DEF_RULE(comparison, c(comparison), list, rule(expr), rule(comp_op)) +DEF_RULE_NC(comp_op, or(9), tok(OP_LESS), tok(OP_MORE), tok(OP_DBL_EQUAL), tok(OP_LESS_EQUAL), tok(OP_MORE_EQUAL), tok(OP_NOT_EQUAL), tok(KW_IN), rule(comp_op_not_in), rule(comp_op_is)) +DEF_RULE_NC(comp_op_not_in, and(2), tok(KW_NOT), tok(KW_IN)) +DEF_RULE_NC(comp_op_is, and(2), tok(KW_IS), opt_rule(comp_op_is_not)) +DEF_RULE_NC(comp_op_is_not, and(1), tok(KW_NOT)) +DEF_RULE(star_expr, c(star_expr), and(2), tok(OP_STAR), rule(expr)) +DEF_RULE(expr, c(binary_op), list, rule(xor_expr), tok(OP_PIPE)) +DEF_RULE(xor_expr, c(binary_op), list, rule(and_expr), tok(OP_CARET)) +DEF_RULE(and_expr, c(binary_op), list, rule(shift_expr), tok(OP_AMPERSAND)) +DEF_RULE(shift_expr, c(term), list, rule(arith_expr), rule(shift_op)) +DEF_RULE_NC(shift_op, or(2), tok(OP_DBL_LESS), tok(OP_DBL_MORE)) +DEF_RULE(arith_expr, c(term), list, rule(term), rule(arith_op)) +DEF_RULE_NC(arith_op, or(2), tok(OP_PLUS), tok(OP_MINUS)) +DEF_RULE(term, c(term), list, rule(factor), rule(term_op)) +DEF_RULE_NC(term_op, or(5), tok(OP_STAR), tok(OP_AT), tok(OP_SLASH), tok(OP_PERCENT), tok(OP_DBL_SLASH)) +DEF_RULE_NC(factor, or(2), rule(factor_2), rule(power)) +DEF_RULE(factor_2, c(factor_2), and_ident(2), rule(factor_op), rule(factor)) +DEF_RULE_NC(factor_op, or(3), tok(OP_PLUS), tok(OP_MINUS), tok(OP_TILDE)) +DEF_RULE(power, c(power), and_ident(2), rule(atom_expr), opt_rule(power_dbl_star)) +#if MICROPY_PY_ASYNC_AWAIT +DEF_RULE_NC(atom_expr, or(2), rule(atom_expr_await), rule(atom_expr_normal)) +DEF_RULE(atom_expr_await, c(atom_expr_await), and(3), tok(KW_AWAIT), rule(atom), opt_rule(atom_expr_trailers)) +#else +DEF_RULE_NC(atom_expr, or(1), rule(atom_expr_normal)) +#endif +DEF_RULE(atom_expr_normal, c(atom_expr_normal), and_ident(2), rule(atom), opt_rule(atom_expr_trailers)) +DEF_RULE_NC(atom_expr_trailers, one_or_more, rule(trailer)) +DEF_RULE_NC(power_dbl_star, and_ident(2), tok(OP_DBL_STAR), rule(factor)) + +// atom: '(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' +// testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) +// trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME + +DEF_RULE_NC(atom, or(12), tok(NAME), tok(INTEGER), tok(FLOAT_OR_IMAG), tok(STRING), tok(BYTES), tok(ELLIPSIS), tok(KW_NONE), tok(KW_TRUE), tok(KW_FALSE), rule(atom_paren), rule(atom_bracket), rule(atom_brace)) +DEF_RULE(atom_paren, c(atom_paren), and(3), tok(DEL_PAREN_OPEN), opt_rule(atom_2b), tok(DEL_PAREN_CLOSE)) +DEF_RULE_NC(atom_2b, or(2), rule(yield_expr), rule(testlist_comp)) +DEF_RULE(atom_bracket, c(atom_bracket), and(3), tok(DEL_BRACKET_OPEN), opt_rule(testlist_comp), tok(DEL_BRACKET_CLOSE)) +DEF_RULE(atom_brace, c(atom_brace), and(3), tok(DEL_BRACE_OPEN), opt_rule(dictorsetmaker), tok(DEL_BRACE_CLOSE)) +DEF_RULE_NC(testlist_comp, and_ident(2), rule(testlist_comp_2), opt_rule(testlist_comp_3)) +DEF_RULE_NC(testlist_comp_2, or(2), rule(star_expr), rule(namedexpr_test)) +DEF_RULE_NC(testlist_comp_3, or(2), rule(comp_for), rule(testlist_comp_3b)) +DEF_RULE_NC(testlist_comp_3b, and_ident(2), tok(DEL_COMMA), opt_rule(testlist_comp_3c)) +DEF_RULE_NC(testlist_comp_3c, list_with_end, rule(testlist_comp_2), tok(DEL_COMMA)) +DEF_RULE_NC(trailer, or(3), rule(trailer_paren), rule(trailer_bracket), rule(trailer_period)) +DEF_RULE(trailer_paren, c(trailer_paren), and(3), tok(DEL_PAREN_OPEN), opt_rule(arglist), tok(DEL_PAREN_CLOSE)) +DEF_RULE(trailer_bracket, c(trailer_bracket), and(3), tok(DEL_BRACKET_OPEN), rule(subscriptlist), tok(DEL_BRACKET_CLOSE)) +DEF_RULE(trailer_period, c(trailer_period), and(2), tok(DEL_PERIOD), tok(NAME)) + +// subscriptlist: subscript (',' subscript)* [','] +// subscript: test | [test] ':' [test] [sliceop] +// sliceop: ':' [test] + +#if MICROPY_PY_BUILTINS_SLICE +DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(subscript), tok(DEL_COMMA)) +DEF_RULE_NC(subscript, or(2), rule(subscript_3), rule(subscript_2)) +DEF_RULE(subscript_2, c(subscript), and_ident(2), rule(test), opt_rule(subscript_3)) +DEF_RULE(subscript_3, c(subscript), and(2), tok(DEL_COLON), opt_rule(subscript_3b)) +DEF_RULE_NC(subscript_3b, or(2), rule(subscript_3c), rule(subscript_3d)) +DEF_RULE_NC(subscript_3c, and(2), tok(DEL_COLON), opt_rule(test)) +DEF_RULE_NC(subscript_3d, and_ident(2), rule(test), opt_rule(sliceop)) +DEF_RULE_NC(sliceop, and(2), tok(DEL_COLON), opt_rule(test)) +#else +DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA)) +#endif + +// exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] +// testlist: test (',' test)* [','] +// dictorsetmaker: (test ':' test (comp_for | (',' test ':' test)* [','])) | (test (comp_for | (',' test)* [','])) + +DEF_RULE_NC(exprlist, list_with_end, rule(exprlist_2), tok(DEL_COMMA)) +DEF_RULE_NC(exprlist_2, or(2), rule(star_expr), rule(expr)) +DEF_RULE(testlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA)) +// TODO dictorsetmaker lets through more than is allowed +DEF_RULE_NC(dictorsetmaker, and_ident(2), rule(dictorsetmaker_item), opt_rule(dictorsetmaker_tail)) +#if MICROPY_PY_BUILTINS_SET +DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and_ident(2), rule(test), opt_rule(generic_colon_test)) +#else +DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and(3), rule(test), tok(DEL_COLON), rule(test)) +#endif +DEF_RULE_NC(dictorsetmaker_tail, or(2), rule(comp_for), rule(dictorsetmaker_list)) +DEF_RULE_NC(dictorsetmaker_list, and(2), tok(DEL_COMMA), opt_rule(dictorsetmaker_list2)) +DEF_RULE_NC(dictorsetmaker_list2, list_with_end, rule(dictorsetmaker_item), tok(DEL_COMMA)) + +// classdef: 'class' NAME ['(' [arglist] ')'] ':' suite + +DEF_RULE(classdef, c(classdef), and_blank(5), tok(KW_CLASS), tok(NAME), opt_rule(classdef_2), tok(DEL_COLON), rule(suite)) +DEF_RULE_NC(classdef_2, and_ident(3), tok(DEL_PAREN_OPEN), opt_rule(arglist), tok(DEL_PAREN_CLOSE)) + +// arglist: (argument ',')* (argument [','] | '*' test (',' argument)* [',' '**' test] | '**' test) + +// TODO arglist lets through more than is allowed, compiler needs to do further verification +DEF_RULE_NC(arglist, list_with_end, rule(arglist_2), tok(DEL_COMMA)) +DEF_RULE_NC(arglist_2, or(3), rule(arglist_star), rule(arglist_dbl_star), rule(argument)) +DEF_RULE_NC(arglist_star, and(2), tok(OP_STAR), rule(test)) +DEF_RULE_NC(arglist_dbl_star, and(2), tok(OP_DBL_STAR), rule(test)) + +// # The reason that keywords are test nodes instead of NAME is that using NAME +// # results in an ambiguity. ast.c makes sure it's a NAME. +// argument: test [comp_for] | test '=' test # Really [keyword '='] test +// comp_iter: comp_for | comp_if +// comp_for: 'for' exprlist 'in' or_test [comp_iter] +// comp_if: 'if' test_nocond [comp_iter] + +DEF_RULE_NC(argument, and_ident(2), rule(test), opt_rule(argument_2)) +#if MICROPY_PY_ASSIGN_EXPR +DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(generic_equal_test), rule(argument_3)) +DEF_RULE_NC(argument_3, and(2), tok(OP_ASSIGN), rule(test)) +#else +DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(generic_equal_test)) +#endif +DEF_RULE_NC(comp_iter, or(2), rule(comp_for), rule(comp_if)) +DEF_RULE_NC(comp_for, and_blank(5), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(or_test), opt_rule(comp_iter)) +DEF_RULE_NC(comp_if, and(3), tok(KW_IF), rule(test_nocond), opt_rule(comp_iter)) + +// # not used in grammar, but may appear in "node" passed from Parser to Compiler +// encoding_decl: NAME + +// yield_expr: 'yield' [yield_arg] +// yield_arg: 'from' test | testlist + +DEF_RULE(yield_expr, c(yield_expr), and(2), tok(KW_YIELD), opt_rule(yield_arg)) +DEF_RULE_NC(yield_arg, or(2), rule(yield_arg_from), rule(testlist)) +DEF_RULE_NC(yield_arg_from, and(2), tok(KW_FROM), rule(test)) diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/lexer.c b/non_catalog_apps/mp_flipper/lib/micropython/py/lexer.c new file mode 100644 index 00000000..bff8e637 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/lexer.c @@ -0,0 +1,944 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/reader.h" +#include "py/lexer.h" +#include "py/runtime.h" + +#if MICROPY_ENABLE_COMPILER + +#define TAB_SIZE (8) + +// TODO seems that CPython allows NULL byte in the input stream +// don't know if that's intentional or not, but we don't allow it + +#define MP_LEXER_EOF ((unichar)MP_READER_EOF) +#define CUR_CHAR(lex) ((lex)->chr0) + +static bool is_end(mp_lexer_t *lex) { + return lex->chr0 == MP_LEXER_EOF; +} + +static bool is_physical_newline(mp_lexer_t *lex) { + return lex->chr0 == '\n'; +} + +static bool is_char(mp_lexer_t *lex, byte c) { + return lex->chr0 == c; +} + +static bool is_char_or(mp_lexer_t *lex, byte c1, byte c2) { + return lex->chr0 == c1 || lex->chr0 == c2; +} + +static bool is_char_or3(mp_lexer_t *lex, byte c1, byte c2, byte c3) { + return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3; +} + +#if MICROPY_PY_FSTRINGS +static bool is_char_or4(mp_lexer_t *lex, byte c1, byte c2, byte c3, byte c4) { + return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3 || lex->chr0 == c4; +} +#endif + +static bool is_char_following(mp_lexer_t *lex, byte c) { + return lex->chr1 == c; +} + +static bool is_char_following_or(mp_lexer_t *lex, byte c1, byte c2) { + return lex->chr1 == c1 || lex->chr1 == c2; +} + +static bool is_char_following_following_or(mp_lexer_t *lex, byte c1, byte c2) { + return lex->chr2 == c1 || lex->chr2 == c2; +} + +static bool is_char_and(mp_lexer_t *lex, byte c1, byte c2) { + return lex->chr0 == c1 && lex->chr1 == c2; +} + +static bool is_whitespace(mp_lexer_t *lex) { + return unichar_isspace(lex->chr0); +} + +static bool is_letter(mp_lexer_t *lex) { + return unichar_isalpha(lex->chr0); +} + +static bool is_digit(mp_lexer_t *lex) { + return unichar_isdigit(lex->chr0); +} + +static bool is_following_digit(mp_lexer_t *lex) { + return unichar_isdigit(lex->chr1); +} + +static bool is_following_base_char(mp_lexer_t *lex) { + const unichar chr1 = lex->chr1 | 0x20; + return chr1 == 'b' || chr1 == 'o' || chr1 == 'x'; +} + +static bool is_following_odigit(mp_lexer_t *lex) { + return lex->chr1 >= '0' && lex->chr1 <= '7'; +} + +static bool is_string_or_bytes(mp_lexer_t *lex) { + return is_char_or(lex, '\'', '\"') + #if MICROPY_PY_FSTRINGS + || (is_char_or4(lex, 'r', 'u', 'b', 'f') && is_char_following_or(lex, '\'', '\"')) + || (((is_char_and(lex, 'r', 'f') || is_char_and(lex, 'f', 'r')) + && is_char_following_following_or(lex, '\'', '\"'))) + #else + || (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"')) + #endif + || ((is_char_and(lex, 'r', 'b') || is_char_and(lex, 'b', 'r')) + && is_char_following_following_or(lex, '\'', '\"')); +} + +// to easily parse utf-8 identifiers we allow any raw byte with high bit set +static bool is_head_of_identifier(mp_lexer_t *lex) { + return is_letter(lex) || lex->chr0 == '_' || lex->chr0 >= 0x80; +} + +static bool is_tail_of_identifier(mp_lexer_t *lex) { + return is_head_of_identifier(lex) || is_digit(lex); +} + +static void next_char(mp_lexer_t *lex) { + if (lex->chr0 == '\n') { + // a new line + ++lex->line; + lex->column = 1; + } else if (lex->chr0 == '\t') { + // a tab + lex->column = (((lex->column - 1 + TAB_SIZE) / TAB_SIZE) * TAB_SIZE) + 1; + } else { + // a character worth one column + ++lex->column; + } + + // shift the input queue forward + lex->chr0 = lex->chr1; + lex->chr1 = lex->chr2; + + // and add the next byte from either the fstring args or the reader + #if MICROPY_PY_FSTRINGS + if (lex->fstring_args_idx) { + // if there are saved chars, then we're currently injecting fstring args + if (lex->fstring_args_idx < lex->fstring_args.len) { + lex->chr2 = lex->fstring_args.buf[lex->fstring_args_idx++]; + } else { + // no more fstring arg bytes + lex->chr2 = '\0'; + } + + if (lex->chr0 == '\0') { + // consumed all fstring data, restore saved input queue + lex->chr0 = lex->chr0_saved; + lex->chr1 = lex->chr1_saved; + lex->chr2 = lex->chr2_saved; + // stop consuming fstring arg data + vstr_reset(&lex->fstring_args); + lex->fstring_args_idx = 0; + } + } else + #endif + { + lex->chr2 = lex->reader.readbyte(lex->reader.data); + } + + if (lex->chr1 == '\r') { + // CR is a new line, converted to LF + lex->chr1 = '\n'; + if (lex->chr2 == '\n') { + // CR LF is a single new line, throw out the extra LF + lex->chr2 = lex->reader.readbyte(lex->reader.data); + } + } + + // check if we need to insert a newline at end of file + if (lex->chr2 == MP_LEXER_EOF && lex->chr1 != MP_LEXER_EOF && lex->chr1 != '\n') { + lex->chr2 = '\n'; + } +} + +static void indent_push(mp_lexer_t *lex, size_t indent) { + if (lex->num_indent_level >= lex->alloc_indent_level) { + lex->indent_level = m_renew(uint16_t, lex->indent_level, lex->alloc_indent_level, lex->alloc_indent_level + MICROPY_ALLOC_LEXEL_INDENT_INC); + lex->alloc_indent_level += MICROPY_ALLOC_LEXEL_INDENT_INC; + } + lex->indent_level[lex->num_indent_level++] = indent; +} + +static size_t indent_top(mp_lexer_t *lex) { + return lex->indent_level[lex->num_indent_level - 1]; +} + +static void indent_pop(mp_lexer_t *lex) { + lex->num_indent_level -= 1; +} + +// some tricky operator encoding: +// = begin with , if this opchar matches then begin here +// e = end with , if this opchar matches then end +// c = continue with , if this opchar matches then continue matching +// this means if the start of two ops are the same then they are equal til the last char + +static const char *const tok_enc = + "()[]{},;~" // singles + ":e=" // : := + "e=c>e=" // > >= >> >>= + "*e=c*e=" // * *= ** **= + "+e=" // + += + "-e=e>" // - -= -> + "&e=" // & &= + "|e=" // | |= + "/e=c/e=" // / /= // //= + "%e=" // % %= + "^e=" // ^ ^= + "@e=" // @ @= + "=e=" // = == + "!."; // start of special cases: != . ... + +// TODO static assert that number of tokens is less than 256 so we can safely make this table with byte sized entries +static const uint8_t tok_enc_kind[] = { + MP_TOKEN_DEL_PAREN_OPEN, MP_TOKEN_DEL_PAREN_CLOSE, + MP_TOKEN_DEL_BRACKET_OPEN, MP_TOKEN_DEL_BRACKET_CLOSE, + MP_TOKEN_DEL_BRACE_OPEN, MP_TOKEN_DEL_BRACE_CLOSE, + MP_TOKEN_DEL_COMMA, MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_OP_TILDE, + + MP_TOKEN_DEL_COLON, MP_TOKEN_OP_ASSIGN, + MP_TOKEN_OP_LESS, MP_TOKEN_OP_LESS_EQUAL, MP_TOKEN_OP_DBL_LESS, MP_TOKEN_DEL_DBL_LESS_EQUAL, + MP_TOKEN_OP_MORE, MP_TOKEN_OP_MORE_EQUAL, MP_TOKEN_OP_DBL_MORE, MP_TOKEN_DEL_DBL_MORE_EQUAL, + MP_TOKEN_OP_STAR, MP_TOKEN_DEL_STAR_EQUAL, MP_TOKEN_OP_DBL_STAR, MP_TOKEN_DEL_DBL_STAR_EQUAL, + MP_TOKEN_OP_PLUS, MP_TOKEN_DEL_PLUS_EQUAL, + MP_TOKEN_OP_MINUS, MP_TOKEN_DEL_MINUS_EQUAL, MP_TOKEN_DEL_MINUS_MORE, + MP_TOKEN_OP_AMPERSAND, MP_TOKEN_DEL_AMPERSAND_EQUAL, + MP_TOKEN_OP_PIPE, MP_TOKEN_DEL_PIPE_EQUAL, + MP_TOKEN_OP_SLASH, MP_TOKEN_DEL_SLASH_EQUAL, MP_TOKEN_OP_DBL_SLASH, MP_TOKEN_DEL_DBL_SLASH_EQUAL, + MP_TOKEN_OP_PERCENT, MP_TOKEN_DEL_PERCENT_EQUAL, + MP_TOKEN_OP_CARET, MP_TOKEN_DEL_CARET_EQUAL, + MP_TOKEN_OP_AT, MP_TOKEN_DEL_AT_EQUAL, + MP_TOKEN_DEL_EQUAL, MP_TOKEN_OP_DBL_EQUAL, +}; + +// must have the same order as enum in lexer.h +// must be sorted according to strcmp +static const char *const tok_kw[] = { + "False", + "None", + "True", + "__debug__", + "and", + "as", + "assert", + #if MICROPY_PY_ASYNC_AWAIT + "async", + "await", + #endif + "break", + "class", + "continue", + "def", + "del", + "elif", + "else", + "except", + "finally", + "for", + "from", + "global", + "if", + "import", + "in", + "is", + "lambda", + "nonlocal", + "not", + "or", + "pass", + "raise", + "return", + "try", + "while", + "with", + "yield", +}; + +// This is called with CUR_CHAR() before first hex digit, and should return with +// it pointing to last hex digit +// num_digits must be greater than zero +static bool get_hex(mp_lexer_t *lex, size_t num_digits, mp_uint_t *result) { + mp_uint_t num = 0; + while (num_digits-- != 0) { + next_char(lex); + unichar c = CUR_CHAR(lex); + if (!unichar_isxdigit(c)) { + return false; + } + num = (num << 4) + unichar_xdigit_value(c); + } + *result = num; + return true; +} + +static void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) { + // get first quoting character + char quote_char = '\''; + if (is_char(lex, '\"')) { + quote_char = '\"'; + } + next_char(lex); + + // work out if it's a single or triple quoted literal + size_t num_quotes; + if (is_char_and(lex, quote_char, quote_char)) { + // triple quotes + next_char(lex); + next_char(lex); + num_quotes = 3; + } else { + // single quotes + num_quotes = 1; + } + + size_t n_closing = 0; + #if MICROPY_PY_FSTRINGS + if (is_fstring) { + // assume there's going to be interpolation, so prep the injection data + // fstring_args_idx==0 && len(fstring_args)>0 means we're extracting the args. + // only when fstring_args_idx>0 will we consume the arg data + // note: lex->fstring_args will be empty already (it's reset when finished) + vstr_add_str(&lex->fstring_args, ".format("); + } + #endif + + while (!is_end(lex) && (num_quotes > 1 || !is_char(lex, '\n')) && n_closing < num_quotes) { + if (is_char(lex, quote_char)) { + n_closing += 1; + vstr_add_char(&lex->vstr, CUR_CHAR(lex)); + } else { + n_closing = 0; + + #if MICROPY_PY_FSTRINGS + while (is_fstring && is_char(lex, '{')) { + next_char(lex); + if (is_char(lex, '{')) { + // "{{" is passed through unchanged to be handled by str.format + vstr_add_byte(&lex->vstr, '{'); + next_char(lex); + } else { + // wrap each argument in (), e.g. + // f"{a,b,}, {c}" --> "{}".format((a,b), (c),) + vstr_add_byte(&lex->fstring_args, '('); + // remember the start of this argument (if we need it for f'{a=}'). + size_t i = lex->fstring_args.len; + // Extract characters inside the { until the bracket level + // is zero and we reach the conversion specifier '!', + // format specifier ':', or closing '}'. The conversion + // and format specifiers are left unchanged in the format + // string to be handled by str.format. + // (MicroPython limitation) note: this is completely + // unaware of Python syntax and will not handle any + // expression containing '}' or ':'. e.g. f'{"}"}' or f' + // {foo({})}'. However, detection of the '!' will + // specifically ensure that it's followed by [rs] and + // then either the format specifier or the closing + // brace. This allows the use of e.g. != in expressions. + unsigned int nested_bracket_level = 0; + while (!is_end(lex) && (nested_bracket_level != 0 + || !(is_char_or(lex, ':', '}') + || (is_char(lex, '!') + && is_char_following_or(lex, 'r', 's') + && is_char_following_following_or(lex, ':', '}')))) + ) { + unichar c = CUR_CHAR(lex); + if (c == '[' || c == '{') { + nested_bracket_level += 1; + } else if (c == ']' || c == '}') { + nested_bracket_level -= 1; + } + // like the default case at the end of this function, stay 8-bit clean + vstr_add_byte(&lex->fstring_args, c); + next_char(lex); + } + if (lex->fstring_args.buf[lex->fstring_args.len - 1] == '=') { + // if the last character of the arg was '=', then inject "arg=" before the '{'. + // f'{a=}' --> 'a={}'.format(a) + vstr_add_strn(&lex->vstr, lex->fstring_args.buf + i, lex->fstring_args.len - i); + // remove the trailing '=' + lex->fstring_args.len--; + } + // close the paren-wrapped arg to .format(). + vstr_add_byte(&lex->fstring_args, ')'); + // comma-separate args to .format(). + vstr_add_byte(&lex->fstring_args, ','); + } + vstr_add_byte(&lex->vstr, '{'); + } + #endif + + if (is_char(lex, '\\')) { + next_char(lex); + unichar c = CUR_CHAR(lex); + if (is_raw) { + // raw strings allow escaping of quotes, but the backslash is also emitted + vstr_add_char(&lex->vstr, '\\'); + } else { + switch (c) { + // note: "c" can never be MP_LEXER_EOF because next_char + // always inserts a newline at the end of the input stream + case '\n': + c = MP_LEXER_EOF; + break; // backslash escape the newline, just ignore it + case '\\': + break; + case '\'': + break; + case '"': + break; + case 'a': + c = 0x07; + break; + case 'b': + c = 0x08; + break; + case 't': + c = 0x09; + break; + case 'n': + c = 0x0a; + break; + case 'v': + c = 0x0b; + break; + case 'f': + c = 0x0c; + break; + case 'r': + c = 0x0d; + break; + case 'u': + case 'U': + if (lex->tok_kind == MP_TOKEN_BYTES) { + // b'\u1234' == b'\\u1234' + vstr_add_char(&lex->vstr, '\\'); + break; + } + // Otherwise fall through. + MP_FALLTHROUGH + case 'x': { + mp_uint_t num = 0; + if (!get_hex(lex, (c == 'x' ? 2 : c == 'u' ? 4 : 8), &num)) { + // not enough hex chars for escape sequence + lex->tok_kind = MP_TOKEN_INVALID; + } + c = num; + break; + } + case 'N': + // Supporting '\N{LATIN SMALL LETTER A}' == 'a' would require keeping the + // entire Unicode name table in the core. As of Unicode 6.3.0, that's nearly + // 3MB of text; even gzip-compressed and with minimal structure, it'll take + // roughly half a meg of storage. This form of Unicode escape may be added + // later on, but it's definitely not a priority right now. -- CJA 20140607 + mp_raise_NotImplementedError(MP_ERROR_TEXT("unicode name escapes")); + break; + default: + if (c >= '0' && c <= '7') { + // Octal sequence, 1-3 chars + size_t digits = 3; + mp_uint_t num = c - '0'; + while (is_following_odigit(lex) && --digits != 0) { + next_char(lex); + num = num * 8 + (CUR_CHAR(lex) - '0'); + } + c = num; + } else { + // unrecognised escape character; CPython lets this through verbatim as '\' and then the character + vstr_add_char(&lex->vstr, '\\'); + } + break; + } + } + if (c != MP_LEXER_EOF) { + #if MICROPY_PY_BUILTINS_STR_UNICODE + if (c < 0x110000 && lex->tok_kind == MP_TOKEN_STRING) { + // Valid unicode character in a str object. + vstr_add_char(&lex->vstr, c); + } else if (c < 0x100 && lex->tok_kind == MP_TOKEN_BYTES) { + // Valid byte in a bytes object. + vstr_add_byte(&lex->vstr, c); + } + #else + if (c < 0x100) { + // Without unicode everything is just added as an 8-bit byte. + vstr_add_byte(&lex->vstr, c); + } + #endif + else { + // Character out of range; this raises a generic SyntaxError. + lex->tok_kind = MP_TOKEN_INVALID; + } + } + } else { + // Add the "character" as a byte so that we remain 8-bit clean. + // This way, strings are parsed correctly whether or not they contain utf-8 chars. + vstr_add_byte(&lex->vstr, CUR_CHAR(lex)); + } + } + next_char(lex); + } + + // check we got the required end quotes + if (n_closing < num_quotes) { + lex->tok_kind = MP_TOKEN_LONELY_STRING_OPEN; + } + + // cut off the end quotes from the token text + vstr_cut_tail_bytes(&lex->vstr, n_closing); +} + +// This function returns whether it has crossed a newline or not. +// It therefore always return true if stop_at_newline is true +static bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) { + while (!is_end(lex)) { + if (is_physical_newline(lex)) { + if (stop_at_newline && lex->nested_bracket_level == 0) { + return true; + } + next_char(lex); + } else if (is_whitespace(lex)) { + next_char(lex); + } else if (is_char(lex, '#')) { + next_char(lex); + while (!is_end(lex) && !is_physical_newline(lex)) { + next_char(lex); + } + // will return true on next loop + } else if (is_char_and(lex, '\\', '\n')) { + // line-continuation, so don't return true + next_char(lex); + next_char(lex); + } else { + break; + } + } + return false; +} + +void mp_lexer_to_next(mp_lexer_t *lex) { + #if MICROPY_PY_FSTRINGS + if (lex->fstring_args.len && lex->fstring_args_idx == 0) { + // moving onto the next token means the literal string is complete. + // switch into injecting the format args. + vstr_add_byte(&lex->fstring_args, ')'); + lex->chr0_saved = lex->chr0; + lex->chr1_saved = lex->chr1; + lex->chr2_saved = lex->chr2; + lex->chr0 = lex->fstring_args.buf[0]; + lex->chr1 = lex->fstring_args.buf[1]; + lex->chr2 = lex->fstring_args.buf[2]; + // we've already extracted 3 chars, but setting this non-zero also + // means we'll start consuming the fstring data + lex->fstring_args_idx = 3; + } + #endif + + // start new token text + vstr_reset(&lex->vstr); + + // skip white space and comments + // set the newline tokens at the line and column of the preceding line: + // only advance on the pointer until a new line is crossed, save the + // line and column, and then readvance it + bool had_physical_newline = skip_whitespace(lex, true); + + // set token source information + lex->tok_line = lex->line; + lex->tok_column = lex->column; + + if (lex->emit_dent < 0) { + lex->tok_kind = MP_TOKEN_DEDENT; + lex->emit_dent += 1; + + } else if (lex->emit_dent > 0) { + lex->tok_kind = MP_TOKEN_INDENT; + lex->emit_dent -= 1; + + } else if (had_physical_newline) { + // The cursor is at the end of the previous line, pointing to a + // physical newline. Skip any remaining whitespace, comments, and + // newlines. + skip_whitespace(lex, false); + + lex->tok_kind = MP_TOKEN_NEWLINE; + + size_t num_spaces = lex->column - 1; + if (num_spaces == indent_top(lex)) { + } else if (num_spaces > indent_top(lex)) { + indent_push(lex, num_spaces); + lex->emit_dent += 1; + } else { + while (num_spaces < indent_top(lex)) { + indent_pop(lex); + lex->emit_dent -= 1; + } + if (num_spaces != indent_top(lex)) { + lex->tok_kind = MP_TOKEN_DEDENT_MISMATCH; + } + } + + } else if (is_end(lex)) { + lex->tok_kind = MP_TOKEN_END; + + } else if (is_string_or_bytes(lex)) { + // a string or bytes literal + + // Python requires adjacent string/bytes literals to be automatically + // concatenated. We do it here in the tokeniser to make efficient use of RAM, + // because then the lexer's vstr can be used to accumulate the string literal, + // in contrast to creating a parse tree of strings and then joining them later + // in the compiler. It's also more compact in code size to do it here. + + // MP_TOKEN_END is used to indicate that this is the first string token + lex->tok_kind = MP_TOKEN_END; + + // Loop to accumulate string/bytes literals + do { + // parse type codes + bool is_raw = false; + bool is_fstring = false; + mp_token_kind_t kind = MP_TOKEN_STRING; + int n_char = 0; + if (is_char(lex, 'u')) { + n_char = 1; + } else if (is_char(lex, 'b')) { + kind = MP_TOKEN_BYTES; + n_char = 1; + if (is_char_following(lex, 'r')) { + is_raw = true; + n_char = 2; + } + } else if (is_char(lex, 'r')) { + is_raw = true; + n_char = 1; + if (is_char_following(lex, 'b')) { + kind = MP_TOKEN_BYTES; + n_char = 2; + } + #if MICROPY_PY_FSTRINGS + if (is_char_following(lex, 'f')) { + // raw-f-strings unsupported, immediately return (invalid) token. + lex->tok_kind = MP_TOKEN_FSTRING_RAW; + break; + } + #endif + } + #if MICROPY_PY_FSTRINGS + else if (is_char(lex, 'f')) { + if (is_char_following(lex, 'r')) { + // raw-f-strings unsupported, immediately return (invalid) token. + lex->tok_kind = MP_TOKEN_FSTRING_RAW; + break; + } + n_char = 1; + is_fstring = true; + } + #endif + + // Set or check token kind + if (lex->tok_kind == MP_TOKEN_END) { + lex->tok_kind = kind; + } else if (lex->tok_kind != kind) { + // Can't concatenate string with bytes + break; + } + + // Skip any type code characters + if (n_char != 0) { + next_char(lex); + if (n_char == 2) { + next_char(lex); + } + } + + // Parse the literal + parse_string_literal(lex, is_raw, is_fstring); + + // Skip whitespace so we can check if there's another string following + skip_whitespace(lex, true); + + } while (is_string_or_bytes(lex)); + + } else if (is_head_of_identifier(lex)) { + lex->tok_kind = MP_TOKEN_NAME; + + // get first char (add as byte to remain 8-bit clean and support utf-8) + vstr_add_byte(&lex->vstr, CUR_CHAR(lex)); + next_char(lex); + + // get tail chars + while (!is_end(lex) && is_tail_of_identifier(lex)) { + vstr_add_byte(&lex->vstr, CUR_CHAR(lex)); + next_char(lex); + } + + // Check if the name is a keyword. + // We also check for __debug__ here and convert it to its value. This is + // so the parser gives a syntax error on, eg, x.__debug__. Otherwise, we + // need to check for this special token in many places in the compiler. + const char *s = vstr_null_terminated_str(&lex->vstr); + for (size_t i = 0; i < MP_ARRAY_SIZE(tok_kw); i++) { + int cmp = strcmp(s, tok_kw[i]); + if (cmp == 0) { + lex->tok_kind = MP_TOKEN_KW_FALSE + i; + if (lex->tok_kind == MP_TOKEN_KW___DEBUG__) { + lex->tok_kind = (MP_STATE_VM(mp_optimise_value) == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE); + } + break; + } else if (cmp < 0) { + // Table is sorted and comparison was less-than, so stop searching + break; + } + } + + } else if (is_digit(lex) || (is_char(lex, '.') && is_following_digit(lex))) { + bool forced_integer = false; + if (is_char(lex, '.')) { + lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG; + } else { + lex->tok_kind = MP_TOKEN_INTEGER; + if (is_char(lex, '0') && is_following_base_char(lex)) { + forced_integer = true; + } + } + + // get first char + vstr_add_char(&lex->vstr, CUR_CHAR(lex)); + next_char(lex); + + // get tail chars + while (!is_end(lex)) { + if (!forced_integer && is_char_or(lex, 'e', 'E')) { + lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG; + vstr_add_char(&lex->vstr, 'e'); + next_char(lex); + if (is_char(lex, '+') || is_char(lex, '-')) { + vstr_add_char(&lex->vstr, CUR_CHAR(lex)); + next_char(lex); + } + } else if (is_letter(lex) || is_digit(lex) || is_char(lex, '.')) { + if (is_char_or3(lex, '.', 'j', 'J')) { + lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG; + } + vstr_add_char(&lex->vstr, CUR_CHAR(lex)); + next_char(lex); + } else if (is_char(lex, '_')) { + next_char(lex); + } else { + break; + } + } + + } else { + // search for encoded delimiter or operator + + const char *t = tok_enc; + size_t tok_enc_index = 0; + for (; *t != 0 && !is_char(lex, *t); t += 1) { + if (*t == 'e' || *t == 'c') { + t += 1; + } + tok_enc_index += 1; + } + + next_char(lex); + + if (*t == 0) { + // didn't match any delimiter or operator characters + lex->tok_kind = MP_TOKEN_INVALID; + + } else if (*t == '!') { + // "!=" is a special case because "!" is not a valid operator + if (is_char(lex, '=')) { + next_char(lex); + lex->tok_kind = MP_TOKEN_OP_NOT_EQUAL; + } else { + lex->tok_kind = MP_TOKEN_INVALID; + } + + } else if (*t == '.') { + // "." and "..." are special cases because ".." is not a valid operator + if (is_char_and(lex, '.', '.')) { + next_char(lex); + next_char(lex); + lex->tok_kind = MP_TOKEN_ELLIPSIS; + } else { + lex->tok_kind = MP_TOKEN_DEL_PERIOD; + } + + } else { + // matched a delimiter or operator character + + // get the maximum characters for a valid token + t += 1; + size_t t_index = tok_enc_index; + while (*t == 'c' || *t == 'e') { + t_index += 1; + if (is_char(lex, t[1])) { + next_char(lex); + tok_enc_index = t_index; + if (*t == 'e') { + break; + } + } else if (*t == 'c') { + break; + } + t += 2; + } + + // set token kind + lex->tok_kind = tok_enc_kind[tok_enc_index]; + + // compute bracket level for implicit line joining + if (lex->tok_kind == MP_TOKEN_DEL_PAREN_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACKET_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACE_OPEN) { + lex->nested_bracket_level += 1; + } else if (lex->tok_kind == MP_TOKEN_DEL_PAREN_CLOSE || lex->tok_kind == MP_TOKEN_DEL_BRACKET_CLOSE || lex->tok_kind == MP_TOKEN_DEL_BRACE_CLOSE) { + lex->nested_bracket_level -= 1; + } + } + } +} + +mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { + mp_lexer_t *lex = m_new_obj(mp_lexer_t); + + lex->source_name = src_name; + lex->reader = reader; + lex->line = 1; + lex->column = (size_t)-2; // account for 3 dummy bytes + lex->emit_dent = 0; + lex->nested_bracket_level = 0; + lex->alloc_indent_level = MICROPY_ALLOC_LEXER_INDENT_INIT; + lex->num_indent_level = 1; + lex->indent_level = m_new(uint16_t, lex->alloc_indent_level); + vstr_init(&lex->vstr, 32); + #if MICROPY_PY_FSTRINGS + vstr_init(&lex->fstring_args, 0); + lex->fstring_args_idx = 0; + #endif + + // store sentinel for first indentation level + lex->indent_level[0] = 0; + + // load lexer with start of file, advancing lex->column to 1 + // start with dummy bytes and use next_char() for proper EOL/EOF handling + lex->chr0 = lex->chr1 = lex->chr2 = 0; + next_char(lex); + next_char(lex); + next_char(lex); + + // preload first token + mp_lexer_to_next(lex); + + // Check that the first token is in the first column unless it is a + // newline. Otherwise we convert the token kind to INDENT so that + // the parser gives a syntax error. + if (lex->tok_column != 1 && lex->tok_kind != MP_TOKEN_NEWLINE) { + lex->tok_kind = MP_TOKEN_INDENT; + } + + return lex; +} + +mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len, size_t free_len) { + mp_reader_t reader; + mp_reader_new_mem(&reader, (const byte *)str, len, free_len); + return mp_lexer_new(src_name, reader); +} + +#if MICROPY_READER_POSIX || MICROPY_READER_VFS + +mp_lexer_t *mp_lexer_new_from_file(qstr filename) { + mp_reader_t reader; + mp_reader_new_file(&reader, filename); + return mp_lexer_new(filename, reader); +} + +#if MICROPY_HELPER_LEXER_UNIX + +mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd) { + mp_reader_t reader; + mp_reader_new_file_from_fd(&reader, fd, close_fd); + return mp_lexer_new(filename, reader); +} + +#endif + +#endif + +void mp_lexer_free(mp_lexer_t *lex) { + if (lex) { + lex->reader.close(lex->reader.data); + vstr_clear(&lex->vstr); + #if MICROPY_PY_FSTRINGS + vstr_clear(&lex->fstring_args); + #endif + m_del(uint16_t, lex->indent_level, lex->alloc_indent_level); + m_del_obj(mp_lexer_t, lex); + } +} + +#if 0 +// This function is used to print the current token and should only be +// needed to debug the lexer, so it's not available via a config option. +void mp_lexer_show_token(const mp_lexer_t *lex) { + printf("(" UINT_FMT ":" UINT_FMT ") kind:%u str:%p len:%zu", lex->tok_line, lex->tok_column, lex->tok_kind, lex->vstr.buf, lex->vstr.len); + if (lex->vstr.len > 0) { + const byte *i = (const byte *)lex->vstr.buf; + const byte *j = (const byte *)i + lex->vstr.len; + printf(" "); + while (i < j) { + unichar c = utf8_get_char(i); + i = utf8_next_char(i); + if (unichar_isprint(c)) { + printf("%c", (int)c); + } else { + printf("?"); + } + } + } + printf("\n"); +} +#endif + +#endif // MICROPY_ENABLE_COMPILER diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/lexer.h b/non_catalog_apps/mp_flipper/lib/micropython/py/lexer.h new file mode 100644 index 00000000..2d9d0447 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/lexer.h @@ -0,0 +1,203 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_LEXER_H +#define MICROPY_INCLUDED_PY_LEXER_H + +#include + +#include "py/mpconfig.h" +#include "py/qstr.h" +#include "py/reader.h" + +/* lexer.h -- simple tokeniser for MicroPython + * + * Uses (byte) length instead of null termination. + * Tokens are the same - UTF-8 with (byte) length. + */ + +typedef enum _mp_token_kind_t { + MP_TOKEN_END, + + MP_TOKEN_INVALID, + MP_TOKEN_DEDENT_MISMATCH, + MP_TOKEN_LONELY_STRING_OPEN, + #if MICROPY_PY_FSTRINGS + MP_TOKEN_MALFORMED_FSTRING, + MP_TOKEN_FSTRING_RAW, + #endif + + MP_TOKEN_NEWLINE, + MP_TOKEN_INDENT, + MP_TOKEN_DEDENT, + + MP_TOKEN_NAME, + MP_TOKEN_INTEGER, + MP_TOKEN_FLOAT_OR_IMAG, + MP_TOKEN_STRING, + MP_TOKEN_BYTES, + + MP_TOKEN_ELLIPSIS, + + MP_TOKEN_KW_FALSE, + MP_TOKEN_KW_NONE, + MP_TOKEN_KW_TRUE, + MP_TOKEN_KW___DEBUG__, + MP_TOKEN_KW_AND, + MP_TOKEN_KW_AS, + MP_TOKEN_KW_ASSERT, + #if MICROPY_PY_ASYNC_AWAIT + MP_TOKEN_KW_ASYNC, + MP_TOKEN_KW_AWAIT, + #endif + MP_TOKEN_KW_BREAK, + MP_TOKEN_KW_CLASS, + MP_TOKEN_KW_CONTINUE, + MP_TOKEN_KW_DEF, + MP_TOKEN_KW_DEL, + MP_TOKEN_KW_ELIF, + MP_TOKEN_KW_ELSE, + MP_TOKEN_KW_EXCEPT, + MP_TOKEN_KW_FINALLY, + MP_TOKEN_KW_FOR, + MP_TOKEN_KW_FROM, + MP_TOKEN_KW_GLOBAL, + MP_TOKEN_KW_IF, + MP_TOKEN_KW_IMPORT, + MP_TOKEN_KW_IN, + MP_TOKEN_KW_IS, + MP_TOKEN_KW_LAMBDA, + MP_TOKEN_KW_NONLOCAL, + MP_TOKEN_KW_NOT, + MP_TOKEN_KW_OR, + MP_TOKEN_KW_PASS, + MP_TOKEN_KW_RAISE, + MP_TOKEN_KW_RETURN, + MP_TOKEN_KW_TRY, + MP_TOKEN_KW_WHILE, + MP_TOKEN_KW_WITH, + MP_TOKEN_KW_YIELD, + + MP_TOKEN_OP_ASSIGN, + MP_TOKEN_OP_TILDE, + + // Order of these 6 matches corresponding mp_binary_op_t operator + MP_TOKEN_OP_LESS, + MP_TOKEN_OP_MORE, + MP_TOKEN_OP_DBL_EQUAL, + MP_TOKEN_OP_LESS_EQUAL, + MP_TOKEN_OP_MORE_EQUAL, + MP_TOKEN_OP_NOT_EQUAL, + + // Order of these 13 matches corresponding mp_binary_op_t operator + MP_TOKEN_OP_PIPE, + MP_TOKEN_OP_CARET, + MP_TOKEN_OP_AMPERSAND, + MP_TOKEN_OP_DBL_LESS, + MP_TOKEN_OP_DBL_MORE, + MP_TOKEN_OP_PLUS, + MP_TOKEN_OP_MINUS, + MP_TOKEN_OP_STAR, + MP_TOKEN_OP_AT, + MP_TOKEN_OP_DBL_SLASH, + MP_TOKEN_OP_SLASH, + MP_TOKEN_OP_PERCENT, + MP_TOKEN_OP_DBL_STAR, + + // Order of these 13 matches corresponding mp_binary_op_t operator + MP_TOKEN_DEL_PIPE_EQUAL, + MP_TOKEN_DEL_CARET_EQUAL, + MP_TOKEN_DEL_AMPERSAND_EQUAL, + MP_TOKEN_DEL_DBL_LESS_EQUAL, + MP_TOKEN_DEL_DBL_MORE_EQUAL, + MP_TOKEN_DEL_PLUS_EQUAL, + MP_TOKEN_DEL_MINUS_EQUAL, + MP_TOKEN_DEL_STAR_EQUAL, + MP_TOKEN_DEL_AT_EQUAL, + MP_TOKEN_DEL_DBL_SLASH_EQUAL, + MP_TOKEN_DEL_SLASH_EQUAL, + MP_TOKEN_DEL_PERCENT_EQUAL, + MP_TOKEN_DEL_DBL_STAR_EQUAL, + + MP_TOKEN_DEL_PAREN_OPEN, + MP_TOKEN_DEL_PAREN_CLOSE, + MP_TOKEN_DEL_BRACKET_OPEN, + MP_TOKEN_DEL_BRACKET_CLOSE, + MP_TOKEN_DEL_BRACE_OPEN, + MP_TOKEN_DEL_BRACE_CLOSE, + MP_TOKEN_DEL_COMMA, + MP_TOKEN_DEL_COLON, + MP_TOKEN_DEL_PERIOD, + MP_TOKEN_DEL_SEMICOLON, + MP_TOKEN_DEL_EQUAL, + MP_TOKEN_DEL_MINUS_MORE, +} mp_token_kind_t; + +// this data structure is exposed for efficiency +// public members are: source_name, tok_line, tok_column, tok_kind, vstr +typedef struct _mp_lexer_t { + qstr source_name; // name of source + mp_reader_t reader; // stream source + + unichar chr0, chr1, chr2; // current cached characters from source + #if MICROPY_PY_FSTRINGS + unichar chr0_saved, chr1_saved, chr2_saved; // current cached characters from alt source + #endif + + size_t line; // current source line + size_t column; // current source column + + mp_int_t emit_dent; // non-zero when there are INDENT/DEDENT tokens to emit + mp_int_t nested_bracket_level; // >0 when there are nested brackets over multiple lines + + size_t alloc_indent_level; + size_t num_indent_level; + uint16_t *indent_level; + + size_t tok_line; // token source line + size_t tok_column; // token source column + mp_token_kind_t tok_kind; // token kind + vstr_t vstr; // token data + #if MICROPY_PY_FSTRINGS + vstr_t fstring_args; // extracted arguments to pass to .format() + size_t fstring_args_idx; // how many bytes of fstring_args have been read + #endif +} mp_lexer_t; + +mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader); +mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len, size_t free_len); + +// If MICROPY_READER_POSIX or MICROPY_READER_VFS aren't enabled then +// this function must be implemented by the port. +mp_lexer_t *mp_lexer_new_from_file(qstr filename); + +#if MICROPY_HELPER_LEXER_UNIX +mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd); +#endif + +void mp_lexer_free(mp_lexer_t *lex); +void mp_lexer_to_next(mp_lexer_t *lex); + +#endif // MICROPY_INCLUDED_PY_LEXER_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/malloc.c b/non_catalog_apps/mp_flipper/lib/micropython/py/malloc.c new file mode 100644 index 00000000..f557ade4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/malloc.c @@ -0,0 +1,315 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpconfig.h" +#include "py/misc.h" +#include "py/mpstate.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_printf(...) (void)0 +#endif + +#if MICROPY_MEM_STATS +#if !MICROPY_MALLOC_USES_ALLOCATED_SIZE +#error MICROPY_MEM_STATS requires MICROPY_MALLOC_USES_ALLOCATED_SIZE +#endif +#define UPDATE_PEAK() { if (MP_STATE_MEM(current_bytes_allocated) > MP_STATE_MEM(peak_bytes_allocated)) MP_STATE_MEM(peak_bytes_allocated) = MP_STATE_MEM(current_bytes_allocated); } +#endif + +#if MICROPY_ENABLE_GC +#include "py/gc.h" + +// We redirect standard alloc functions to GC heap - just for the rest of +// this module. In the rest of MicroPython source, system malloc can be +// freely accessed - for interfacing with system and 3rd-party libs for +// example. On the other hand, some (e.g. bare-metal) ports may use GC +// heap as system heap, so, to avoid warnings, we do undef's first. +#undef malloc +#undef free +#undef realloc +#define malloc(b) gc_alloc((b), false) +#define malloc_with_finaliser(b) gc_alloc((b), true) +#define free gc_free +#define realloc(ptr, n) gc_realloc(ptr, n, true) +#define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv) +#else + +// GC is disabled. Use system malloc/realloc/free. + +#if MICROPY_ENABLE_FINALISER +#error MICROPY_ENABLE_FINALISER requires MICROPY_ENABLE_GC +#endif + +static void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) { + if (allow_move) { + return realloc(ptr, n_bytes); + } else { + // We are asked to resize, but without moving the memory region pointed to + // by ptr. Unless the underlying memory manager has special provision for + // this behaviour there is nothing we can do except fail to resize. + return NULL; + } +} + +#endif // MICROPY_ENABLE_GC + +void *m_malloc(size_t num_bytes) { + void *ptr = malloc(num_bytes); + if (ptr == NULL && num_bytes != 0) { + m_malloc_fail(num_bytes); + } + #if MICROPY_MEM_STATS + MP_STATE_MEM(total_bytes_allocated) += num_bytes; + MP_STATE_MEM(current_bytes_allocated) += num_bytes; + UPDATE_PEAK(); + #endif + DEBUG_printf("malloc %d : %p\n", num_bytes, ptr); + return ptr; +} + +void *m_malloc_maybe(size_t num_bytes) { + void *ptr = malloc(num_bytes); + #if MICROPY_MEM_STATS + MP_STATE_MEM(total_bytes_allocated) += num_bytes; + MP_STATE_MEM(current_bytes_allocated) += num_bytes; + UPDATE_PEAK(); + #endif + DEBUG_printf("malloc %d : %p\n", num_bytes, ptr); + return ptr; +} + +#if MICROPY_ENABLE_FINALISER +void *m_malloc_with_finaliser(size_t num_bytes) { + void *ptr = malloc_with_finaliser(num_bytes); + if (ptr == NULL && num_bytes != 0) { + m_malloc_fail(num_bytes); + } + #if MICROPY_MEM_STATS + MP_STATE_MEM(total_bytes_allocated) += num_bytes; + MP_STATE_MEM(current_bytes_allocated) += num_bytes; + UPDATE_PEAK(); + #endif + DEBUG_printf("malloc %d : %p\n", num_bytes, ptr); + return ptr; +} +#endif + +void *m_malloc0(size_t num_bytes) { + void *ptr = m_malloc(num_bytes); + // If this config is set then the GC clears all memory, so we don't need to. + #if !MICROPY_GC_CONSERVATIVE_CLEAR + memset(ptr, 0, num_bytes); + #endif + return ptr; +} + +#if MICROPY_MALLOC_USES_ALLOCATED_SIZE +void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes) +#else +void *m_realloc(void *ptr, size_t new_num_bytes) +#endif +{ + void *new_ptr = realloc(ptr, new_num_bytes); + if (new_ptr == NULL && new_num_bytes != 0) { + m_malloc_fail(new_num_bytes); + } + #if MICROPY_MEM_STATS + // At first thought, "Total bytes allocated" should only grow, + // after all, it's *total*. But consider for example 2K block + // shrunk to 1K and then grown to 2K again. It's still 2K + // allocated total. If we process only positive increments, + // we'll count 3K. + size_t diff = new_num_bytes - old_num_bytes; + MP_STATE_MEM(total_bytes_allocated) += diff; + MP_STATE_MEM(current_bytes_allocated) += diff; + UPDATE_PEAK(); + #endif + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE + DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr); + #else + DEBUG_printf("realloc %p, %d : %p\n", ptr, new_num_bytes, new_ptr); + #endif + return new_ptr; +} + +#if MICROPY_MALLOC_USES_ALLOCATED_SIZE +void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes, bool allow_move) +#else +void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move) +#endif +{ + void *new_ptr = realloc_ext(ptr, new_num_bytes, allow_move); + #if MICROPY_MEM_STATS + // At first thought, "Total bytes allocated" should only grow, + // after all, it's *total*. But consider for example 2K block + // shrunk to 1K and then grown to 2K again. It's still 2K + // allocated total. If we process only positive increments, + // we'll count 3K. + // Also, don't count failed reallocs. + if (!(new_ptr == NULL && new_num_bytes != 0)) { + size_t diff = new_num_bytes - old_num_bytes; + MP_STATE_MEM(total_bytes_allocated) += diff; + MP_STATE_MEM(current_bytes_allocated) += diff; + UPDATE_PEAK(); + } + #endif + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE + DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr); + #else + DEBUG_printf("realloc %p, %d : %p\n", ptr, new_num_bytes, new_ptr); + #endif + return new_ptr; +} + +#if MICROPY_MALLOC_USES_ALLOCATED_SIZE +void m_free(void *ptr, size_t num_bytes) +#else +void m_free(void *ptr) +#endif +{ + free(ptr); + #if MICROPY_MEM_STATS + MP_STATE_MEM(current_bytes_allocated) -= num_bytes; + #endif + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE + DEBUG_printf("free %p, %d\n", ptr, num_bytes); + #else + DEBUG_printf("free %p\n", ptr); + #endif +} + +#if MICROPY_TRACKED_ALLOC + +#define MICROPY_TRACKED_ALLOC_STORE_SIZE (!MICROPY_ENABLE_GC) + +typedef struct _m_tracked_node_t { + struct _m_tracked_node_t *prev; + struct _m_tracked_node_t *next; + #if MICROPY_TRACKED_ALLOC_STORE_SIZE + uintptr_t size; + #endif + uint8_t data[]; +} m_tracked_node_t; + +#if MICROPY_DEBUG_VERBOSE +static size_t m_tracked_count_links(size_t *nb) { + m_tracked_node_t *node = MP_STATE_VM(m_tracked_head); + size_t n = 0; + *nb = 0; + while (node != NULL) { + ++n; + #if MICROPY_TRACKED_ALLOC_STORE_SIZE + *nb += node->size; + #else + *nb += gc_nbytes(node); + #endif + node = node->next; + } + return n; +} +#endif + +void *m_tracked_calloc(size_t nmemb, size_t size) { + m_tracked_node_t *node = m_malloc_maybe(sizeof(m_tracked_node_t) + nmemb * size); + if (node == NULL) { + return NULL; + } + #if MICROPY_DEBUG_VERBOSE + size_t nb; + size_t n = m_tracked_count_links(&nb); + DEBUG_printf("m_tracked_calloc(%u, %u) -> (%u;%u) %p\n", (int)nmemb, (int)size, (int)n, (int)nb, node); + #endif + if (MP_STATE_VM(m_tracked_head) != NULL) { + MP_STATE_VM(m_tracked_head)->prev = node; + } + node->prev = NULL; + node->next = MP_STATE_VM(m_tracked_head); + MP_STATE_VM(m_tracked_head) = node; + #if MICROPY_TRACKED_ALLOC_STORE_SIZE + node->size = nmemb * size; + #endif + #if !MICROPY_GC_CONSERVATIVE_CLEAR + memset(&node->data[0], 0, nmemb * size); + #endif + return &node->data[0]; +} + +void m_tracked_free(void *ptr_in) { + if (ptr_in == NULL) { + return; + } + m_tracked_node_t *node = (m_tracked_node_t *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t)); + #if MICROPY_DEBUG_VERBOSE + size_t data_bytes; + #if MICROPY_TRACKED_ALLOC_STORE_SIZE + data_bytes = node->size; + #else + data_bytes = gc_nbytes(node); + #endif + size_t nb; + size_t n = m_tracked_count_links(&nb); + DEBUG_printf("m_tracked_free(%p, [%p, %p], nbytes=%u, links=%u;%u)\n", node, node->prev, node->next, (int)data_bytes, (int)n, (int)nb); + #endif + if (node->next != NULL) { + node->next->prev = node->prev; + } + if (node->prev != NULL) { + node->prev->next = node->next; + } else { + MP_STATE_VM(m_tracked_head) = node->next; + } + m_free(node + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE + #if MICROPY_TRACKED_ALLOC_STORE_SIZE + , node->size + #else + , gc_nbytes(node) + #endif + #endif + ); +} + +#endif // MICROPY_TRACKED_ALLOC + +#if MICROPY_MEM_STATS +size_t m_get_total_bytes_allocated(void) { + return MP_STATE_MEM(total_bytes_allocated); +} + +size_t m_get_current_bytes_allocated(void) { + return MP_STATE_MEM(current_bytes_allocated); +} + +size_t m_get_peak_bytes_allocated(void) { + return MP_STATE_MEM(peak_bytes_allocated); +} +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/map.c b/non_catalog_apps/mp_flipper/lib/micropython/py/map.c new file mode 100644 index 00000000..d40e3dc4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/map.c @@ -0,0 +1,461 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpconfig.h" +#include "py/misc.h" +#include "py/runtime.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + +#if MICROPY_OPT_MAP_LOOKUP_CACHE +// MP_STATE_VM(map_lookup_cache) provides a cache of index to the last known +// position of that index in any map. On a cache hit, this allows +// short-circuiting the full linear search in the case of an ordered map +// (i.e. all builtin modules and objects' locals dicts), and computation of +// the hash (and potentially some linear probing) in the case of a regular +// map. Note the same cache is shared across all maps. + +// Gets the index into the cache for this index. Shift down by two to remove +// mp_obj_t tag bits. +#define MAP_CACHE_OFFSET(index) ((((uintptr_t)(index)) >> 2) % MICROPY_OPT_MAP_LOOKUP_CACHE_SIZE) +// Gets the map cache entry for the corresponding index. +#define MAP_CACHE_ENTRY(index) (MP_STATE_VM(map_lookup_cache)[MAP_CACHE_OFFSET(index)]) +// Retrieve the mp_obj_t at the location suggested by the cache. +#define MAP_CACHE_GET(map, index) (&(map)->table[MAP_CACHE_ENTRY(index) % (map)->alloc]) +// Update the cache for this index. +#define MAP_CACHE_SET(index, pos) MAP_CACHE_ENTRY(index) = (pos) & 0xff; +#else +#define MAP_CACHE_SET(index, pos) +#endif + +// This table of sizes is used to control the growth of hash tables. +// The first set of sizes are chosen so the allocation fits exactly in a +// 4-word GC block, and it's not so important for these small values to be +// prime. The latter sizes are prime and increase at an increasing rate. +static const uint16_t hash_allocation_sizes[] = { + 0, 2, 4, 6, 8, 10, 12, // +2 + 17, 23, 29, 37, 47, 59, 73, // *1.25 + 97, 127, 167, 223, 293, 389, 521, 691, 919, 1223, 1627, 2161, // *1.33 + 3229, 4831, 7243, 10861, 16273, 24407, 36607, 54907, // *1.5 +}; + +static size_t get_hash_alloc_greater_or_equal_to(size_t x) { + for (size_t i = 0; i < MP_ARRAY_SIZE(hash_allocation_sizes); i++) { + if (hash_allocation_sizes[i] >= x) { + return hash_allocation_sizes[i]; + } + } + // ran out of primes in the table! + // return something sensible, at least make it odd + return (x + x / 2) | 1; +} + +/******************************************************************************/ +/* map */ + +void mp_map_init(mp_map_t *map, size_t n) { + if (n == 0) { + map->alloc = 0; + map->table = NULL; + } else { + map->alloc = n; + map->table = m_new0(mp_map_elem_t, map->alloc); + } + map->used = 0; + map->all_keys_are_qstrs = 1; + map->is_fixed = 0; + map->is_ordered = 0; +} + +void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table) { + map->alloc = n; + map->used = n; + map->all_keys_are_qstrs = 1; + map->is_fixed = 1; + map->is_ordered = 1; + map->table = (mp_map_elem_t *)table; +} + +// Differentiate from mp_map_clear() - semantics is different +void mp_map_deinit(mp_map_t *map) { + if (!map->is_fixed) { + m_del(mp_map_elem_t, map->table, map->alloc); + } + map->used = map->alloc = 0; +} + +void mp_map_clear(mp_map_t *map) { + if (!map->is_fixed) { + m_del(mp_map_elem_t, map->table, map->alloc); + } + map->alloc = 0; + map->used = 0; + map->all_keys_are_qstrs = 1; + map->is_fixed = 0; + map->table = NULL; +} + +static void mp_map_rehash(mp_map_t *map) { + size_t old_alloc = map->alloc; + size_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1); + DEBUG_printf("mp_map_rehash(%p): " UINT_FMT " -> " UINT_FMT "\n", map, old_alloc, new_alloc); + mp_map_elem_t *old_table = map->table; + mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc); + // If we reach this point, table resizing succeeded, now we can edit the old map. + map->alloc = new_alloc; + map->used = 0; + map->all_keys_are_qstrs = 1; + map->table = new_table; + for (size_t i = 0; i < old_alloc; i++) { + if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) { + mp_map_lookup(map, old_table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_table[i].value; + } + } + m_del(mp_map_elem_t, old_table, old_alloc); +} + +// MP_MAP_LOOKUP behaviour: +// - returns NULL if not found, else the slot it was found in with key,value non-null +// MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour: +// - returns slot, with key non-null and value=MP_OBJ_NULL if it was added +// MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour: +// - returns NULL if not found, else the slot if was found in with key null and value non-null +mp_map_elem_t *MICROPY_WRAP_MP_MAP_LOOKUP(mp_map_lookup)(mp_map_t * map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { + // If the map is a fixed array then we must only be called for a lookup + assert(!map->is_fixed || lookup_kind == MP_MAP_LOOKUP); + + #if MICROPY_OPT_MAP_LOOKUP_CACHE + // Try the cache for lookup or add-if-not-found. + if (lookup_kind != MP_MAP_LOOKUP_REMOVE_IF_FOUND && map->alloc) { + mp_map_elem_t *slot = MAP_CACHE_GET(map, index); + // Note: Just comparing key for value equality will have false negatives, but + // these will be handled by the regular path below. + if (slot->key == index) { + return slot; + } + } + #endif + + // Work out if we can compare just pointers + bool compare_only_ptrs = map->all_keys_are_qstrs; + if (compare_only_ptrs) { + if (mp_obj_is_qstr(index)) { + // Index is a qstr, so can just do ptr comparison. + } else if (mp_obj_is_exact_type(index, &mp_type_str)) { + // Index is a non-interned string. + // We can either intern the string, or force a full equality comparison. + // We chose the latter, since interning costs time and potentially RAM, + // and it won't necessarily benefit subsequent calls because these calls + // most likely won't pass the newly-interned string. + compare_only_ptrs = false; + } else if (lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + // If we are not adding, then we can return straight away a failed + // lookup because we know that the index will never be found. + return NULL; + } + } + + // if the map is an ordered array then we must do a brute force linear search + if (map->is_ordered) { + for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) { + if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) { + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT + if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) { + // remove the found element by moving the rest of the array down + mp_obj_t value = elem->value; + --map->used; + memmove(elem, elem + 1, (top - elem - 1) * sizeof(*elem)); + // put the found element after the end so the caller can access it if needed + // note: caller must NULL the value so the GC can clean up (e.g. see dict_get_helper). + elem = &map->table[map->used]; + elem->key = MP_OBJ_NULL; + elem->value = value; + } + #endif + MAP_CACHE_SET(index, elem - map->table); + return elem; + } + } + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT + if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) { + return NULL; + } + if (map->used == map->alloc) { + // TODO: Alloc policy + map->alloc += 4; + map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc); + mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table)); + } + mp_map_elem_t *elem = map->table + map->used++; + elem->key = index; + elem->value = MP_OBJ_NULL; + if (!mp_obj_is_qstr(index)) { + map->all_keys_are_qstrs = 0; + } + return elem; + #else + return NULL; + #endif + } + + // map is a hash table (not an ordered array), so do a hash lookup + + if (map->alloc == 0) { + if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + mp_map_rehash(map); + } else { + return NULL; + } + } + + // get hash of index, with fast path for common case of qstr + mp_uint_t hash; + if (mp_obj_is_qstr(index)) { + hash = qstr_hash(MP_OBJ_QSTR_VALUE(index)); + } else { + hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index)); + } + + size_t pos = hash % map->alloc; + size_t start_pos = pos; + mp_map_elem_t *avail_slot = NULL; + for (;;) { + mp_map_elem_t *slot = &map->table[pos]; + if (slot->key == MP_OBJ_NULL) { + // found NULL slot, so index is not in table + if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + map->used += 1; + if (avail_slot == NULL) { + avail_slot = slot; + } + avail_slot->key = index; + avail_slot->value = MP_OBJ_NULL; + if (!mp_obj_is_qstr(index)) { + map->all_keys_are_qstrs = 0; + } + return avail_slot; + } else { + return NULL; + } + } else if (slot->key == MP_OBJ_SENTINEL) { + // found deleted slot, remember for later + if (avail_slot == NULL) { + avail_slot = slot; + } + } else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) { + // found index + // Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x + if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) { + // delete element in this slot + map->used--; + if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) { + // optimisation if next slot is empty + slot->key = MP_OBJ_NULL; + } else { + slot->key = MP_OBJ_SENTINEL; + } + // keep slot->value so that caller can access it if needed + } + MAP_CACHE_SET(index, pos); + return slot; + } + + // not yet found, keep searching in this table + pos = (pos + 1) % map->alloc; + + if (pos == start_pos) { + // search got back to starting position, so index is not in table + if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot != NULL) { + // there was an available slot, so use that + map->used++; + avail_slot->key = index; + avail_slot->value = MP_OBJ_NULL; + if (!mp_obj_is_qstr(index)) { + map->all_keys_are_qstrs = 0; + } + return avail_slot; + } else { + // not enough room in table, rehash it + mp_map_rehash(map); + // restart the search for the new element + start_pos = pos = hash % map->alloc; + } + } else { + return NULL; + } + } + } +} + +/******************************************************************************/ +/* set */ + +#if MICROPY_PY_BUILTINS_SET + +void mp_set_init(mp_set_t *set, size_t n) { + set->alloc = n; + set->used = 0; + set->table = m_new0(mp_obj_t, set->alloc); +} + +static void mp_set_rehash(mp_set_t *set) { + size_t old_alloc = set->alloc; + mp_obj_t *old_table = set->table; + set->alloc = get_hash_alloc_greater_or_equal_to(set->alloc + 1); + set->used = 0; + set->table = m_new0(mp_obj_t, set->alloc); + for (size_t i = 0; i < old_alloc; i++) { + if (old_table[i] != MP_OBJ_NULL && old_table[i] != MP_OBJ_SENTINEL) { + mp_set_lookup(set, old_table[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + } + } + m_del(mp_obj_t, old_table, old_alloc); +} + +mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { + // Note: lookup_kind can be MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND which + // is handled by using bitwise operations. + + if (set->alloc == 0) { + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + mp_set_rehash(set); + } else { + return MP_OBJ_NULL; + } + } + mp_uint_t hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index)); + size_t pos = hash % set->alloc; + size_t start_pos = pos; + mp_obj_t *avail_slot = NULL; + for (;;) { + mp_obj_t elem = set->table[pos]; + if (elem == MP_OBJ_NULL) { + // found NULL slot, so index is not in table + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot == NULL) { + avail_slot = &set->table[pos]; + } + set->used++; + *avail_slot = index; + return index; + } else { + return MP_OBJ_NULL; + } + } else if (elem == MP_OBJ_SENTINEL) { + // found deleted slot, remember for later + if (avail_slot == NULL) { + avail_slot = &set->table[pos]; + } + } else if (mp_obj_equal(elem, index)) { + // found index + if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) { + // delete element + set->used--; + if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) { + // optimisation if next slot is empty + set->table[pos] = MP_OBJ_NULL; + } else { + set->table[pos] = MP_OBJ_SENTINEL; + } + } + return elem; + } + + // not yet found, keep searching in this table + pos = (pos + 1) % set->alloc; + + if (pos == start_pos) { + // search got back to starting position, so index is not in table + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot != NULL) { + // there was an available slot, so use that + set->used++; + *avail_slot = index; + return index; + } else { + // not enough room in table, rehash it + mp_set_rehash(set); + // restart the search for the new element + start_pos = pos = hash % set->alloc; + } + } else { + return MP_OBJ_NULL; + } + } + } +} + +mp_obj_t mp_set_remove_first(mp_set_t *set) { + for (size_t pos = 0; pos < set->alloc; pos++) { + if (mp_set_slot_is_filled(set, pos)) { + mp_obj_t elem = set->table[pos]; + // delete element + set->used--; + if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) { + // optimisation if next slot is empty + set->table[pos] = MP_OBJ_NULL; + } else { + set->table[pos] = MP_OBJ_SENTINEL; + } + return elem; + } + } + return MP_OBJ_NULL; +} + +void mp_set_clear(mp_set_t *set) { + m_del(mp_obj_t, set->table, set->alloc); + set->alloc = 0; + set->used = 0; + set->table = NULL; +} + +#endif // MICROPY_PY_BUILTINS_SET + +#if defined(DEBUG_PRINT) && DEBUG_PRINT +void mp_map_dump(mp_map_t *map) { + for (size_t i = 0; i < map->alloc; i++) { + if (map->table[i].key != MP_OBJ_NULL) { + mp_obj_print(map->table[i].key, PRINT_REPR); + } else { + DEBUG_printf("(nil)"); + } + DEBUG_printf(": %p\n", map->table[i].value); + } + DEBUG_printf("---\n"); +} +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/misc.h b/non_catalog_apps/mp_flipper/lib/micropython/py/misc.h new file mode 100644 index 00000000..eea3e8b0 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/misc.h @@ -0,0 +1,337 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MISC_H +#define MICROPY_INCLUDED_PY_MISC_H + +// a mini library of useful types and functions + +/** types *******************************************************/ + +#include +#include +#include + +typedef unsigned char byte; +typedef unsigned int uint; + +/** generic ops *************************************************/ + +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +// Classical double-indirection stringification of preprocessor macro's value +#define MP_STRINGIFY_HELPER(x) #x +#define MP_STRINGIFY(x) MP_STRINGIFY_HELPER(x) + +// Static assertion macro +#define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)])) +// In C++ things like comparing extern const pointers are not constant-expressions so cannot be used +// in MP_STATIC_ASSERT. Note that not all possible compiler versions will reject this. Some gcc versions +// do, others only with -Werror=vla, msvc always does. +// The (void) is needed to avoid "left operand of comma operator has no effect [-Werror=unused-value]" +// when using this macro on the left-hand side of a comma. +#if defined(_MSC_VER) || defined(__cplusplus) +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) +#else +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) MP_STATIC_ASSERT(cond) +#endif + +// Round-up integer division +#define MP_CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b)) +#define MP_ROUND_DIVIDE(a, b) (((a) + (b) / 2) / (b)) + +/** memory allocation ******************************************/ + +// TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element) + +#define m_new(type, num) ((type *)(m_malloc(sizeof(type) * (num)))) +#define m_new_maybe(type, num) ((type *)(m_malloc_maybe(sizeof(type) * (num)))) +#define m_new0(type, num) ((type *)(m_malloc0(sizeof(type) * (num)))) +#define m_new_obj(type) (m_new(type, 1)) +#define m_new_obj_maybe(type) (m_new_maybe(type, 1)) +#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc0(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_maybe(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#if MICROPY_MALLOC_USES_ALLOCATED_SIZE +#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num)))) +#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num), (allow_move)))) +#define m_del(type, ptr, num) m_free(ptr, sizeof(type) * (num)) +#define m_del_var(obj_type, var_field, var_type, var_num, ptr) (m_free(ptr, offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#else +#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (new_num)))) +#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (new_num), (allow_move)))) +#define m_del(type, ptr, num) ((void)(num), m_free(ptr)) +#define m_del_var(obj_type, var_field, var_type, var_num, ptr) ((void)(var_num), m_free(ptr)) +#endif +#define m_del_obj(type, ptr) (m_del(type, ptr, 1)) + +void *m_malloc(size_t num_bytes); +void *m_malloc_maybe(size_t num_bytes); +void *m_malloc_with_finaliser(size_t num_bytes); +void *m_malloc0(size_t num_bytes); +#if MICROPY_MALLOC_USES_ALLOCATED_SIZE +void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes); +void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes, bool allow_move); +void m_free(void *ptr, size_t num_bytes); +#else +void *m_realloc(void *ptr, size_t new_num_bytes); +void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move); +void m_free(void *ptr); +#endif +NORETURN void m_malloc_fail(size_t num_bytes); + +#if MICROPY_TRACKED_ALLOC +// These alloc/free functions track the pointers in a linked list so the GC does not reclaim +// them. They can be used by code that requires traditional C malloc/free semantics. +void *m_tracked_calloc(size_t nmemb, size_t size); +void m_tracked_free(void *ptr_in); +#endif + +#if MICROPY_MEM_STATS +size_t m_get_total_bytes_allocated(void); +size_t m_get_current_bytes_allocated(void); +size_t m_get_peak_bytes_allocated(void); +#endif + +/** array helpers ***********************************************/ + +// get the number of elements in a fixed-size array +#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +// align ptr to the nearest multiple of "alignment" +#define MP_ALIGN(ptr, alignment) (void *)(((uintptr_t)(ptr) + ((alignment) - 1)) & ~((alignment) - 1)) + +/** unichar / UTF-8 *********************************************/ + +#if MICROPY_PY_BUILTINS_STR_UNICODE +// with unicode enabled we need a type which can fit chars up to 0x10ffff +typedef uint32_t unichar; +#else +// without unicode enabled we can only need to fit chars up to 0xff +// (on 16-bit archs uint is 16-bits and more efficient than uint32_t) +typedef uint unichar; +#endif + +#if MICROPY_PY_BUILTINS_STR_UNICODE +unichar utf8_get_char(const byte *s); +const byte *utf8_next_char(const byte *s); +size_t utf8_charlen(const byte *str, size_t len); +#else +static inline unichar utf8_get_char(const byte *s) { + return *s; +} +static inline const byte *utf8_next_char(const byte *s) { + return s + 1; +} +static inline size_t utf8_charlen(const byte *str, size_t len) { + (void)str; + return len; +} +#endif + +bool unichar_isspace(unichar c); +bool unichar_isalpha(unichar c); +bool unichar_isprint(unichar c); +bool unichar_isdigit(unichar c); +bool unichar_isxdigit(unichar c); +bool unichar_isident(unichar c); +bool unichar_isalnum(unichar c); +bool unichar_isupper(unichar c); +bool unichar_islower(unichar c); +unichar unichar_tolower(unichar c); +unichar unichar_toupper(unichar c); +mp_uint_t unichar_xdigit_value(unichar c); +#define UTF8_IS_NONASCII(ch) ((ch) & 0x80) +#define UTF8_IS_CONT(ch) (((ch) & 0xC0) == 0x80) + +/** variable string *********************************************/ + +typedef struct _vstr_t { + size_t alloc; + size_t len; + char *buf; + bool fixed_buf; +} vstr_t; + +// convenience macro to declare a vstr with a fixed size buffer on the stack +#define VSTR_FIXED(vstr, alloc) vstr_t vstr; char vstr##_buf[(alloc)]; vstr_init_fixed_buf(&vstr, (alloc), vstr##_buf); + +void vstr_init(vstr_t *vstr, size_t alloc); +void vstr_init_len(vstr_t *vstr, size_t len); +void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf); +struct _mp_print_t; +void vstr_init_print(vstr_t *vstr, size_t alloc, struct _mp_print_t *print); +void vstr_clear(vstr_t *vstr); +vstr_t *vstr_new(size_t alloc); +void vstr_free(vstr_t *vstr); +static inline void vstr_reset(vstr_t *vstr) { + vstr->len = 0; +} +static inline char *vstr_str(vstr_t *vstr) { + return vstr->buf; +} +static inline size_t vstr_len(vstr_t *vstr) { + return vstr->len; +} +void vstr_hint_size(vstr_t *vstr, size_t size); +char *vstr_extend(vstr_t *vstr, size_t size); +char *vstr_add_len(vstr_t *vstr, size_t len); +char *vstr_null_terminated_str(vstr_t *vstr); +void vstr_add_byte(vstr_t *vstr, byte v); +void vstr_add_char(vstr_t *vstr, unichar chr); +void vstr_add_str(vstr_t *vstr, const char *str); +void vstr_add_strn(vstr_t *vstr, const char *str, size_t len); +void vstr_ins_byte(vstr_t *vstr, size_t byte_pos, byte b); +void vstr_ins_char(vstr_t *vstr, size_t char_pos, unichar chr); +void vstr_cut_head_bytes(vstr_t *vstr, size_t bytes_to_cut); +void vstr_cut_tail_bytes(vstr_t *vstr, size_t bytes_to_cut); +void vstr_cut_out_bytes(vstr_t *vstr, size_t byte_pos, size_t bytes_to_cut); +void vstr_printf(vstr_t *vstr, const char *fmt, ...); + +/** non-dynamic size-bounded variable buffer/string *************/ + +#define CHECKBUF(buf, max_size) char buf[max_size + 1]; size_t buf##_len = max_size; char *buf##_p = buf; +#define CHECKBUF_RESET(buf, max_size) buf##_len = max_size; buf##_p = buf; +#define CHECKBUF_APPEND(buf, src, src_len) \ + { size_t l = MIN(src_len, buf##_len); \ + memcpy(buf##_p, src, l); \ + buf##_len -= l; \ + buf##_p += l; } +#define CHECKBUF_APPEND_0(buf) { *buf##_p = 0; } +#define CHECKBUF_LEN(buf) (buf##_p - buf) + +#ifdef va_start +void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap); +#endif + +// Debugging helpers +int DEBUG_printf(const char *fmt, ...); + +extern mp_uint_t mp_verbose_flag; + +/** float internals *************/ + +#if MICROPY_PY_BUILTINS_FLOAT + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define MP_FLOAT_EXP_BITS (11) +#define MP_FLOAT_EXP_OFFSET (1023) +#define MP_FLOAT_FRAC_BITS (52) +typedef uint64_t mp_float_uint_t; +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define MP_FLOAT_EXP_BITS (8) +#define MP_FLOAT_EXP_OFFSET (127) +#define MP_FLOAT_FRAC_BITS (23) +typedef uint32_t mp_float_uint_t; +#endif + +#define MP_FLOAT_EXP_BIAS ((1 << (MP_FLOAT_EXP_BITS - 1)) - 1) + +typedef union _mp_float_union_t { + mp_float_t f; + #if MP_ENDIANNESS_LITTLE + struct { + mp_float_uint_t frc : MP_FLOAT_FRAC_BITS; + mp_float_uint_t exp : MP_FLOAT_EXP_BITS; + mp_float_uint_t sgn : 1; + } p; + #else + struct { + mp_float_uint_t sgn : 1; + mp_float_uint_t exp : MP_FLOAT_EXP_BITS; + mp_float_uint_t frc : MP_FLOAT_FRAC_BITS; + } p; + #endif + mp_float_uint_t i; +} mp_float_union_t; + +#endif // MICROPY_PY_BUILTINS_FLOAT + +/** ROM string compression *************/ + +#if MICROPY_ROM_TEXT_COMPRESSION + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE +#error "MICROPY_ERROR_REPORTING_NONE requires MICROPY_ROM_TEXT_COMPRESSION disabled" +#endif + +#ifdef NO_QSTR + +// Compression enabled but doing QSTR extraction. +// So leave MP_COMPRESSED_ROM_TEXT in place for makeqstrdefs.py / makecompresseddata.py to find them. + +#else + +// Compression enabled and doing a regular build. +// Map MP_COMPRESSED_ROM_TEXT to the compressed strings. + +// Force usage of the MP_ERROR_TEXT macro by requiring an opaque type. +typedef struct { + #if defined(__clang__) || defined(_MSC_VER) + // Fix "error: empty struct has size 0 in C, size 1 in C++", and the msvc counterpart + // "C requires that a struct or union have at least one member" + char dummy; + #endif +} *mp_rom_error_text_t; + +#include + +inline MP_ALWAYSINLINE const char *MP_COMPRESSED_ROM_TEXT(const char *msg) { + // "genhdr/compressed.data.h" contains an invocation of the MP_MATCH_COMPRESSED macro for each compressed string. + // The giant if(strcmp) tree is optimized by the compiler, which turns this into a direct return of the compressed data. + #define MP_MATCH_COMPRESSED(a, b) if (strcmp(msg, a) == 0) { return b; } else + + // It also contains a single invocation of the MP_COMPRESSED_DATA macro, we don't need that here. + #define MP_COMPRESSED_DATA(x) + + #include "genhdr/compressed.data.h" + +#undef MP_COMPRESSED_DATA +#undef MP_MATCH_COMPRESSED + + return msg; +} + +#endif + +#else + +// Compression not enabled, just make it a no-op. + +typedef const char *mp_rom_error_text_t; +#define MP_COMPRESSED_ROM_TEXT(x) x + +#endif // MICROPY_ROM_TEXT_COMPRESSION + +// Might add more types of compressed text in the future. +// For now, forward directly to MP_COMPRESSED_ROM_TEXT. +#define MP_ERROR_TEXT(x) (mp_rom_error_text_t)MP_COMPRESSED_ROM_TEXT(x) + +#endif // MICROPY_INCLUDED_PY_MISC_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modarray.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modarray.c new file mode 100644 index 00000000..116c844e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modarray.c @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/builtin.h" + +#if MICROPY_PY_ARRAY + +static const mp_rom_map_elem_t mp_module_array_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_array) }, + { MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&mp_type_array) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_array_globals, mp_module_array_globals_table); + +const mp_obj_module_t mp_module_array = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_array_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_array, mp_module_array); + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modbuiltins.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modbuiltins.c new file mode 100644 index 00000000..51cf3137 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modbuiltins.c @@ -0,0 +1,773 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/smallint.h" +#include "py/objint.h" +#include "py/objstr.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/stream.h" + +#if MICROPY_PY_BUILTINS_FLOAT +#include +#endif + +#if MICROPY_PY_IO +extern struct _mp_dummy_t mp_sys_stdout_obj; // type is irrelevant, just need pointer +#endif + +// args[0] is function from class body +// args[1] is class name +// args[2:] are base objects +static mp_obj_t mp_builtin___build_class__(size_t n_args, const mp_obj_t *args) { + assert(2 <= n_args); + + // set the new classes __locals__ object + mp_obj_dict_t *old_locals = mp_locals_get(); + mp_obj_t class_locals = mp_obj_new_dict(0); + mp_locals_set(MP_OBJ_TO_PTR(class_locals)); + + // call the class code + mp_obj_t cell = mp_call_function_0(args[0]); + + // restore old __locals__ object + mp_locals_set(old_locals); + + // get the class type (meta object) from the base objects + mp_obj_t meta; + if (n_args == 2) { + // no explicit bases, so use 'type' + meta = MP_OBJ_FROM_PTR(&mp_type_type); + } else { + // use type of first base object + meta = MP_OBJ_FROM_PTR(mp_obj_get_type(args[2])); + } + + // TODO do proper metaclass resolution for multiple base objects + + // create the new class using a call to the meta object + mp_obj_t meta_args[3]; + meta_args[0] = args[1]; // class name + meta_args[1] = mp_obj_new_tuple(n_args - 2, args + 2); // tuple of bases + meta_args[2] = class_locals; // dict of members + mp_obj_t new_class = mp_call_function_n_kw(meta, 3, 0, meta_args); + + // store into cell if needed + if (cell != mp_const_none) { + mp_obj_cell_set(cell, new_class); + } + + return new_class; +} +MP_DEFINE_CONST_FUN_OBJ_VAR(mp_builtin___build_class___obj, 2, mp_builtin___build_class__); + +static mp_obj_t mp_builtin_abs(mp_obj_t o_in) { + return mp_unary_op(MP_UNARY_OP_ABS, o_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_abs_obj, mp_builtin_abs); + +static mp_obj_t mp_builtin_all(mp_obj_t o_in) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(o_in, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (!mp_obj_is_true(item)) { + return mp_const_false; + } + } + return mp_const_true; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_all_obj, mp_builtin_all); + +static mp_obj_t mp_builtin_any(mp_obj_t o_in) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(o_in, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (mp_obj_is_true(item)) { + return mp_const_true; + } + } + return mp_const_false; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_any_obj, mp_builtin_any); + +static mp_obj_t mp_builtin_bin(mp_obj_t o_in) { + mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_b_brace_close_), o_in }; + return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_bin_obj, mp_builtin_bin); + +static mp_obj_t mp_builtin_callable(mp_obj_t o_in) { + if (mp_obj_is_callable(o_in)) { + return mp_const_true; + } else { + return mp_const_false; + } +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_callable_obj, mp_builtin_callable); + +static mp_obj_t mp_builtin_chr(mp_obj_t o_in) { + #if MICROPY_PY_BUILTINS_STR_UNICODE + mp_uint_t c = mp_obj_get_int(o_in); + if (c >= 0x110000) { + mp_raise_ValueError(MP_ERROR_TEXT("chr() arg not in range(0x110000)")); + } + VSTR_FIXED(buf, 4); + vstr_add_char(&buf, c); + return mp_obj_new_str_via_qstr(buf.buf, buf.len); + #else + mp_int_t ord = mp_obj_get_int(o_in); + if (0 <= ord && ord <= 0xff) { + uint8_t str[1] = {ord}; + return mp_obj_new_str_via_qstr((char *)str, 1); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("chr() arg not in range(256)")); + } + #endif +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_chr_obj, mp_builtin_chr); + +static mp_obj_t mp_builtin_dir(size_t n_args, const mp_obj_t *args) { + mp_obj_t dir = mp_obj_new_list(0, NULL); + if (n_args == 0) { + // Make a list of names in the local namespace + mp_obj_dict_t *dict = mp_locals_get(); + for (size_t i = 0; i < dict->map.alloc; i++) { + if (mp_map_slot_is_filled(&dict->map, i)) { + mp_obj_list_append(dir, dict->map.table[i].key); + } + } + } else { // n_args == 1 + // Make a list of names in the given object + // Implemented by probing all possible qstrs with mp_load_method_maybe + size_t nqstr = QSTR_TOTAL(); + for (size_t i = MP_QSTR_ + 1; i < nqstr; ++i) { + mp_obj_t dest[2]; + mp_load_method_protected(args[0], i, dest, false); + if (dest[0] != MP_OBJ_NULL) { + #if MICROPY_PY_ALL_SPECIAL_METHODS + // Support for __dir__: see if we can dispatch to this special method + // This relies on MP_QSTR__dir__ being first after MP_QSTR_ + if (i == MP_QSTR___dir__ && dest[1] != MP_OBJ_NULL) { + return mp_call_method_n_kw(0, 0, dest); + } + #endif + mp_obj_list_append(dir, MP_OBJ_NEW_QSTR(i)); + } + } + } + return dir; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj, 0, 1, mp_builtin_dir); + +static mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) { + return mp_binary_op(MP_BINARY_OP_DIVMOD, o1_in, o2_in); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod); + +static mp_obj_t mp_builtin_hash(mp_obj_t o_in) { + // result is guaranteed to be a (small) int + return mp_unary_op(MP_UNARY_OP_HASH, o_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hash_obj, mp_builtin_hash); + +static mp_obj_t mp_builtin_hex(mp_obj_t o_in) { + #if MICROPY_PY_BUILTINS_STR_OP_MODULO + return mp_binary_op(MP_BINARY_OP_MODULO, MP_OBJ_NEW_QSTR(MP_QSTR__percent__hash_x), o_in); + #else + mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_x_brace_close_), o_in }; + return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hex_obj, mp_builtin_hex); + +#if MICROPY_PY_BUILTINS_INPUT + +#include "py/mphal.h" +#include "shared/readline/readline.h" + +// A port can define mp_hal_readline if they want to use a custom function here +#ifndef mp_hal_readline +#define mp_hal_readline readline +#endif + +static mp_obj_t mp_builtin_input(size_t n_args, const mp_obj_t *args) { + if (n_args == 1) { + mp_obj_print(args[0], PRINT_STR); + } + vstr_t line; + vstr_init(&line, 16); + int ret = mp_hal_readline(&line, ""); + if (ret == CHAR_CTRL_C) { + mp_raise_type(&mp_type_KeyboardInterrupt); + } + if (line.len == 0 && ret == CHAR_CTRL_D) { + mp_raise_type(&mp_type_EOFError); + } + return mp_obj_new_str_from_vstr(&line); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj, 0, 1, mp_builtin_input); + +#endif + +static mp_obj_t mp_builtin_iter(mp_obj_t o_in) { + return mp_getiter(o_in, NULL); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_iter_obj, mp_builtin_iter); + +#if MICROPY_PY_BUILTINS_MIN_MAX + +static mp_obj_t mp_builtin_min_max(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs, mp_uint_t op) { + mp_map_elem_t *key_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_key), MP_MAP_LOOKUP); + mp_map_elem_t *default_elem; + mp_obj_t key_fn = key_elem == NULL ? MP_OBJ_NULL : key_elem->value; + if (n_args == 1) { + // given an iterable + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(args[0], &iter_buf); + mp_obj_t best_key = MP_OBJ_NULL; + mp_obj_t best_obj = MP_OBJ_NULL; + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + mp_obj_t key = key_fn == MP_OBJ_NULL ? item : mp_call_function_1(key_fn, item); + if (best_obj == MP_OBJ_NULL || (mp_binary_op(op, key, best_key) == mp_const_true)) { + best_key = key; + best_obj = item; + } + } + if (best_obj == MP_OBJ_NULL) { + default_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_default), MP_MAP_LOOKUP); + if (default_elem != NULL) { + best_obj = default_elem->value; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("arg is an empty sequence")); + } + } + return best_obj; + } else { + // given many args + mp_obj_t best_key = MP_OBJ_NULL; + mp_obj_t best_obj = MP_OBJ_NULL; + for (size_t i = 0; i < n_args; i++) { + mp_obj_t key = key_fn == MP_OBJ_NULL ? args[i] : mp_call_function_1(key_fn, args[i]); + if (best_obj == MP_OBJ_NULL || (mp_binary_op(op, key, best_key) == mp_const_true)) { + best_key = key; + best_obj = args[i]; + } + } + return best_obj; + } +} + +static mp_obj_t mp_builtin_max(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_builtin_min_max(n_args, args, kwargs, MP_BINARY_OP_MORE); +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_max_obj, 1, mp_builtin_max); + +static mp_obj_t mp_builtin_min(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_builtin_min_max(n_args, args, kwargs, MP_BINARY_OP_LESS); +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_min_obj, 1, mp_builtin_min); + +#endif + +#if MICROPY_PY_BUILTINS_NEXT2 +static mp_obj_t mp_builtin_next(size_t n_args, const mp_obj_t *args) { + if (n_args == 1) { + mp_obj_t ret = mp_iternext_allow_raise(args[0]); + if (ret == MP_OBJ_STOP_ITERATION) { + mp_raise_StopIteration(MP_STATE_THREAD(stop_iteration_arg)); + } else { + return ret; + } + } else { + mp_obj_t ret = mp_iternext(args[0]); + return ret == MP_OBJ_STOP_ITERATION ? args[1] : ret; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_next_obj, 1, 2, mp_builtin_next); +#else +static mp_obj_t mp_builtin_next(mp_obj_t o) { + mp_obj_t ret = mp_iternext_allow_raise(o); + if (ret == MP_OBJ_STOP_ITERATION) { + mp_raise_StopIteration(MP_STATE_THREAD(stop_iteration_arg)); + } else { + return ret; + } +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_next_obj, mp_builtin_next); +#endif + +static mp_obj_t mp_builtin_oct(mp_obj_t o_in) { + #if MICROPY_PY_BUILTINS_STR_OP_MODULO + return mp_binary_op(MP_BINARY_OP_MODULO, MP_OBJ_NEW_QSTR(MP_QSTR__percent__hash_o), o_in); + #else + mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_o_brace_close_), o_in }; + return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_oct_obj, mp_builtin_oct); + +static mp_obj_t mp_builtin_ord(mp_obj_t o_in) { + size_t len; + const byte *str = (const byte *)mp_obj_str_get_data(o_in, &len); + #if MICROPY_PY_BUILTINS_STR_UNICODE + if (mp_obj_is_str(o_in)) { + len = utf8_charlen(str, len); + if (len == 1) { + return mp_obj_new_int(utf8_get_char(str)); + } + } else + #endif + { + // a bytes object, or a str without unicode support (don't sign extend the char) + if (len == 1) { + return MP_OBJ_NEW_SMALL_INT(str[0]); + } + } + + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("ord expects a character")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("ord() expected a character, but string of length %d found"), (int)len); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_ord_obj, mp_builtin_ord); + +static mp_obj_t mp_builtin_pow(size_t n_args, const mp_obj_t *args) { + switch (n_args) { + case 2: + return mp_binary_op(MP_BINARY_OP_POWER, args[0], args[1]); + default: + #if !MICROPY_PY_BUILTINS_POW3 + mp_raise_NotImplementedError(MP_ERROR_TEXT("3-arg pow() not supported")); + #elif MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_MPZ + return mp_binary_op(MP_BINARY_OP_MODULO, mp_binary_op(MP_BINARY_OP_POWER, args[0], args[1]), args[2]); + #else + return mp_obj_int_pow3(args[0], args[1], args[2]); + #endif + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj, 2, 3, mp_builtin_pow); + +static mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sep, ARG_end, ARG_file }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__space_)} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__0x0a_)} }, + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + { MP_QSTR_file, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_sys_stdout_obj)} }, + #endif + }; + + // parse args (a union is used to reduce the amount of C stack that is needed) + union { + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + size_t len[2]; + } u; + mp_arg_parse_all(0, NULL, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, u.args); + + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + mp_get_stream_raise(u.args[ARG_file].u_obj, MP_STREAM_OP_WRITE); + mp_print_t print = {MP_OBJ_TO_PTR(u.args[ARG_file].u_obj), mp_stream_write_adaptor}; + #endif + + // extract the objects first because we are going to use the other part of the union + mp_obj_t sep = u.args[ARG_sep].u_obj; + mp_obj_t end = u.args[ARG_end].u_obj; + const char *sep_data = mp_obj_str_get_data(sep, &u.len[0]); + const char *end_data = mp_obj_str_get_data(end, &u.len[1]); + + for (size_t i = 0; i < n_args; i++) { + if (i > 0) { + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + mp_stream_write_adaptor(print.data, sep_data, u.len[0]); + #else + mp_print_strn(&mp_plat_print, sep_data, u.len[0], 0, 0, 0); + #endif + } + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + mp_obj_print_helper(&print, pos_args[i], PRINT_STR); + #else + mp_obj_print_helper(&mp_plat_print, pos_args[i], PRINT_STR); + #endif + } + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + mp_stream_write_adaptor(print.data, end_data, u.len[1]); + #else + mp_print_strn(&mp_plat_print, end_data, u.len[1], 0, 0, 0); + #endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_print_obj, 0, mp_builtin_print); + +static mp_obj_t mp_builtin___repl_print__(mp_obj_t o) { + if (o != mp_const_none) { + mp_obj_print_helper(MP_PYTHON_PRINTER, o, PRINT_REPR); + mp_print_str(MP_PYTHON_PRINTER, "\n"); + #if MICROPY_CAN_OVERRIDE_BUILTINS + // Set "_" special variable + mp_obj_t dest[2] = {MP_OBJ_SENTINEL, o}; + MP_OBJ_TYPE_GET_SLOT(&mp_type_module, attr)(MP_OBJ_FROM_PTR(&mp_module_builtins), MP_QSTR__, dest); + #endif + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin___repl_print___obj, mp_builtin___repl_print__); + +static mp_obj_t mp_builtin_repr(mp_obj_t o_in) { + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + mp_obj_print_helper(&print, o_in, PRINT_REPR); + return mp_obj_new_str_from_utf8_vstr(&vstr); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_repr_obj, mp_builtin_repr); + +static mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) { + mp_obj_t o_in = args[0]; + if (mp_obj_is_int(o_in)) { + if (n_args <= 1) { + return o_in; + } + + #if !MICROPY_PY_BUILTINS_ROUND_INT + mp_raise_NotImplementedError(NULL); + #else + mp_int_t num_dig = mp_obj_get_int(args[1]); + if (num_dig >= 0) { + return o_in; + } + + mp_obj_t mult = mp_binary_op(MP_BINARY_OP_POWER, MP_OBJ_NEW_SMALL_INT(10), MP_OBJ_NEW_SMALL_INT(-num_dig)); + mp_obj_t half_mult = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, mult, MP_OBJ_NEW_SMALL_INT(2)); + mp_obj_t modulo = mp_binary_op(MP_BINARY_OP_MODULO, o_in, mult); + mp_obj_t rounded = mp_binary_op(MP_BINARY_OP_SUBTRACT, o_in, modulo); + if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, half_mult, modulo))) { + return rounded; + } else if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, modulo, half_mult))) { + return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult); + } else { + // round to even number + mp_obj_t floor = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, o_in, mult); + if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_AND, floor, MP_OBJ_NEW_SMALL_INT(1)))) { + return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult); + } else { + return rounded; + } + } + #endif + } + #if MICROPY_PY_BUILTINS_FLOAT + mp_float_t val = mp_obj_get_float(o_in); + if (n_args > 1) { + mp_int_t num_dig = mp_obj_get_int(args[1]); + mp_float_t mult = MICROPY_FLOAT_C_FUN(pow)(10, (mp_float_t)num_dig); + // TODO may lead to overflow + mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val * mult) / mult; + return mp_obj_new_float(rounded); + } + mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val); + return mp_obj_new_int_from_float(rounded); + #else + mp_int_t r = mp_obj_get_int(o_in); + return mp_obj_new_int(r); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_round_obj, 1, 2, mp_builtin_round); + +static mp_obj_t mp_builtin_sum(size_t n_args, const mp_obj_t *args) { + mp_obj_t value; + switch (n_args) { + case 1: + value = MP_OBJ_NEW_SMALL_INT(0); + break; + default: + value = args[1]; + break; + } + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(args[0], &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + value = mp_binary_op(MP_BINARY_OP_ADD, value, item); + } + return value; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_sum_obj, 1, 2, mp_builtin_sum); + +static mp_obj_t mp_builtin_sorted(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (n_args > 1) { + mp_raise_TypeError(MP_ERROR_TEXT("must use keyword argument for key function")); + } + mp_obj_t self = mp_obj_list_make_new(&mp_type_list, 1, 0, args); + mp_obj_list_sort(1, &self, kwargs); + + return self; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_sorted_obj, 1, mp_builtin_sorted); + +// See mp_load_attr() if making any changes +static inline mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval) { + mp_obj_t dest[2]; + // use load_method, raising or not raising exception + if (defval == MP_OBJ_NULL) { + mp_load_method(base, attr, dest); + } else { + mp_load_method_protected(base, attr, dest, false); + } + if (dest[0] == MP_OBJ_NULL) { + return defval; + } else if (dest[1] == MP_OBJ_NULL) { + // load_method returned just a normal attribute + return dest[0]; + } else { + // load_method returned a method, so build a bound method object + return mp_obj_new_bound_meth(dest[0], dest[1]); + } +} + +static mp_obj_t mp_builtin_getattr(size_t n_args, const mp_obj_t *args) { + mp_obj_t defval = MP_OBJ_NULL; + if (n_args > 2) { + defval = args[2]; + } + return mp_load_attr_default(args[0], mp_obj_str_get_qstr(args[1]), defval); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj, 2, 3, mp_builtin_getattr); + +static mp_obj_t mp_builtin_setattr(mp_obj_t base, mp_obj_t attr, mp_obj_t value) { + mp_store_attr(base, mp_obj_str_get_qstr(attr), value); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(mp_builtin_setattr_obj, mp_builtin_setattr); + +#if MICROPY_CPYTHON_COMPAT +static mp_obj_t mp_builtin_delattr(mp_obj_t base, mp_obj_t attr) { + return mp_builtin_setattr(base, attr, MP_OBJ_NULL); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_delattr_obj, mp_builtin_delattr); +#endif + +static mp_obj_t mp_builtin_hasattr(mp_obj_t object_in, mp_obj_t attr_in) { + qstr attr = mp_obj_str_get_qstr(attr_in); + mp_obj_t dest[2]; + mp_load_method_protected(object_in, attr, dest, false); + return mp_obj_new_bool(dest[0] != MP_OBJ_NULL); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_hasattr_obj, mp_builtin_hasattr); + +static mp_obj_t mp_builtin_globals(void) { + return MP_OBJ_FROM_PTR(mp_globals_get()); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_builtin_globals_obj, mp_builtin_globals); + +static mp_obj_t mp_builtin_locals(void) { + return MP_OBJ_FROM_PTR(mp_locals_get()); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_builtin_locals_obj, mp_builtin_locals); + +// These are defined in terms of MicroPython API functions right away +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_id_obj, mp_obj_id); +MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_len_obj, mp_obj_len); + +static const mp_rom_map_elem_t mp_module_builtins_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_builtins) }, + + // built-in core functions + { MP_ROM_QSTR(MP_QSTR___build_class__), MP_ROM_PTR(&mp_builtin___build_class___obj) }, + { MP_ROM_QSTR(MP_QSTR___import__), MP_ROM_PTR(&mp_builtin___import___obj) }, + { MP_ROM_QSTR(MP_QSTR___repl_print__), MP_ROM_PTR(&mp_builtin___repl_print___obj) }, + + // built-in types + { MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_PTR(&mp_type_bool) }, + { MP_ROM_QSTR(MP_QSTR_bytes), MP_ROM_PTR(&mp_type_bytes) }, + #if MICROPY_PY_BUILTINS_BYTEARRAY + { MP_ROM_QSTR(MP_QSTR_bytearray), MP_ROM_PTR(&mp_type_bytearray) }, + #endif + #if MICROPY_PY_BUILTINS_COMPLEX + { MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_PTR(&mp_type_complex) }, + #endif + { MP_ROM_QSTR(MP_QSTR_dict), MP_ROM_PTR(&mp_type_dict) }, + #if MICROPY_PY_BUILTINS_ENUMERATE + { MP_ROM_QSTR(MP_QSTR_enumerate), MP_ROM_PTR(&mp_type_enumerate) }, + #endif + #if MICROPY_PY_BUILTINS_FILTER + { MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&mp_type_filter) }, + #endif + #if MICROPY_PY_BUILTINS_FLOAT + { MP_ROM_QSTR(MP_QSTR_float), MP_ROM_PTR(&mp_type_float) }, + #endif + #if MICROPY_PY_BUILTINS_SET && MICROPY_PY_BUILTINS_FROZENSET + { MP_ROM_QSTR(MP_QSTR_frozenset), MP_ROM_PTR(&mp_type_frozenset) }, + #endif + { MP_ROM_QSTR(MP_QSTR_int), MP_ROM_PTR(&mp_type_int) }, + { MP_ROM_QSTR(MP_QSTR_list), MP_ROM_PTR(&mp_type_list) }, + { MP_ROM_QSTR(MP_QSTR_map), MP_ROM_PTR(&mp_type_map) }, + #if MICROPY_PY_BUILTINS_MEMORYVIEW + { MP_ROM_QSTR(MP_QSTR_memoryview), MP_ROM_PTR(&mp_type_memoryview) }, + #endif + { MP_ROM_QSTR(MP_QSTR_object), MP_ROM_PTR(&mp_type_object) }, + #if MICROPY_PY_BUILTINS_PROPERTY + { MP_ROM_QSTR(MP_QSTR_property), MP_ROM_PTR(&mp_type_property) }, + #endif + { MP_ROM_QSTR(MP_QSTR_range), MP_ROM_PTR(&mp_type_range) }, + #if MICROPY_PY_BUILTINS_REVERSED + { MP_ROM_QSTR(MP_QSTR_reversed), MP_ROM_PTR(&mp_type_reversed) }, + #endif + #if MICROPY_PY_BUILTINS_SET + { MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&mp_type_set) }, + #endif + #if MICROPY_PY_BUILTINS_SLICE + { MP_ROM_QSTR(MP_QSTR_slice), MP_ROM_PTR(&mp_type_slice) }, + #endif + { MP_ROM_QSTR(MP_QSTR_str), MP_ROM_PTR(&mp_type_str) }, + { MP_ROM_QSTR(MP_QSTR_super), MP_ROM_PTR(&mp_type_super) }, + { MP_ROM_QSTR(MP_QSTR_tuple), MP_ROM_PTR(&mp_type_tuple) }, + { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&mp_type_type) }, + { MP_ROM_QSTR(MP_QSTR_zip), MP_ROM_PTR(&mp_type_zip) }, + + { MP_ROM_QSTR(MP_QSTR_classmethod), MP_ROM_PTR(&mp_type_classmethod) }, + { MP_ROM_QSTR(MP_QSTR_staticmethod), MP_ROM_PTR(&mp_type_staticmethod) }, + + // built-in objects + { MP_ROM_QSTR(MP_QSTR_Ellipsis), MP_ROM_PTR(&mp_const_ellipsis_obj) }, + #if MICROPY_PY_BUILTINS_NOTIMPLEMENTED + { MP_ROM_QSTR(MP_QSTR_NotImplemented), MP_ROM_PTR(&mp_const_notimplemented_obj) }, + #endif + + // built-in user functions + { MP_ROM_QSTR(MP_QSTR_abs), MP_ROM_PTR(&mp_builtin_abs_obj) }, + { MP_ROM_QSTR(MP_QSTR_all), MP_ROM_PTR(&mp_builtin_all_obj) }, + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&mp_builtin_any_obj) }, + { MP_ROM_QSTR(MP_QSTR_bin), MP_ROM_PTR(&mp_builtin_bin_obj) }, + { MP_ROM_QSTR(MP_QSTR_callable), MP_ROM_PTR(&mp_builtin_callable_obj) }, + #if MICROPY_PY_BUILTINS_COMPILE + { MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mp_builtin_compile_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_chr), MP_ROM_PTR(&mp_builtin_chr_obj) }, + #if MICROPY_CPYTHON_COMPAT + { MP_ROM_QSTR(MP_QSTR_delattr), MP_ROM_PTR(&mp_builtin_delattr_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_dir), MP_ROM_PTR(&mp_builtin_dir_obj) }, + { MP_ROM_QSTR(MP_QSTR_divmod), MP_ROM_PTR(&mp_builtin_divmod_obj) }, + #if MICROPY_PY_BUILTINS_EVAL_EXEC + { MP_ROM_QSTR(MP_QSTR_eval), MP_ROM_PTR(&mp_builtin_eval_obj) }, + { MP_ROM_QSTR(MP_QSTR_exec), MP_ROM_PTR(&mp_builtin_exec_obj) }, + #endif + #if MICROPY_PY_BUILTINS_EXECFILE + { MP_ROM_QSTR(MP_QSTR_execfile), MP_ROM_PTR(&mp_builtin_execfile_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_getattr), MP_ROM_PTR(&mp_builtin_getattr_obj) }, + { MP_ROM_QSTR(MP_QSTR_setattr), MP_ROM_PTR(&mp_builtin_setattr_obj) }, + { MP_ROM_QSTR(MP_QSTR_globals), MP_ROM_PTR(&mp_builtin_globals_obj) }, + { MP_ROM_QSTR(MP_QSTR_hasattr), MP_ROM_PTR(&mp_builtin_hasattr_obj) }, + { MP_ROM_QSTR(MP_QSTR_hash), MP_ROM_PTR(&mp_builtin_hash_obj) }, + #if MICROPY_PY_BUILTINS_HELP + { MP_ROM_QSTR(MP_QSTR_help), MP_ROM_PTR(&mp_builtin_help_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_hex), MP_ROM_PTR(&mp_builtin_hex_obj) }, + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&mp_builtin_id_obj) }, + #if MICROPY_PY_BUILTINS_INPUT + { MP_ROM_QSTR(MP_QSTR_input), MP_ROM_PTR(&mp_builtin_input_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_isinstance), MP_ROM_PTR(&mp_builtin_isinstance_obj) }, + { MP_ROM_QSTR(MP_QSTR_issubclass), MP_ROM_PTR(&mp_builtin_issubclass_obj) }, + { MP_ROM_QSTR(MP_QSTR_iter), MP_ROM_PTR(&mp_builtin_iter_obj) }, + { MP_ROM_QSTR(MP_QSTR_len), MP_ROM_PTR(&mp_builtin_len_obj) }, + { MP_ROM_QSTR(MP_QSTR_locals), MP_ROM_PTR(&mp_builtin_locals_obj) }, + #if MICROPY_PY_BUILTINS_MIN_MAX + { MP_ROM_QSTR(MP_QSTR_max), MP_ROM_PTR(&mp_builtin_max_obj) }, + { MP_ROM_QSTR(MP_QSTR_min), MP_ROM_PTR(&mp_builtin_min_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_next), MP_ROM_PTR(&mp_builtin_next_obj) }, + { MP_ROM_QSTR(MP_QSTR_oct), MP_ROM_PTR(&mp_builtin_oct_obj) }, + #if MICROPY_PY_IO + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_ord), MP_ROM_PTR(&mp_builtin_ord_obj) }, + { MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_builtin_pow_obj) }, + { MP_ROM_QSTR(MP_QSTR_print), MP_ROM_PTR(&mp_builtin_print_obj) }, + { MP_ROM_QSTR(MP_QSTR_repr), MP_ROM_PTR(&mp_builtin_repr_obj) }, + { MP_ROM_QSTR(MP_QSTR_round), MP_ROM_PTR(&mp_builtin_round_obj) }, + { MP_ROM_QSTR(MP_QSTR_sorted), MP_ROM_PTR(&mp_builtin_sorted_obj) }, + { MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&mp_builtin_sum_obj) }, + + // built-in exceptions + { MP_ROM_QSTR(MP_QSTR_BaseException), MP_ROM_PTR(&mp_type_BaseException) }, + { MP_ROM_QSTR(MP_QSTR_ArithmeticError), MP_ROM_PTR(&mp_type_ArithmeticError) }, + { MP_ROM_QSTR(MP_QSTR_AssertionError), MP_ROM_PTR(&mp_type_AssertionError) }, + { MP_ROM_QSTR(MP_QSTR_AttributeError), MP_ROM_PTR(&mp_type_AttributeError) }, + { MP_ROM_QSTR(MP_QSTR_EOFError), MP_ROM_PTR(&mp_type_EOFError) }, + { MP_ROM_QSTR(MP_QSTR_Exception), MP_ROM_PTR(&mp_type_Exception) }, + { MP_ROM_QSTR(MP_QSTR_GeneratorExit), MP_ROM_PTR(&mp_type_GeneratorExit) }, + { MP_ROM_QSTR(MP_QSTR_ImportError), MP_ROM_PTR(&mp_type_ImportError) }, + { MP_ROM_QSTR(MP_QSTR_IndentationError), MP_ROM_PTR(&mp_type_IndentationError) }, + { MP_ROM_QSTR(MP_QSTR_IndexError), MP_ROM_PTR(&mp_type_IndexError) }, + { MP_ROM_QSTR(MP_QSTR_KeyboardInterrupt), MP_ROM_PTR(&mp_type_KeyboardInterrupt) }, + { MP_ROM_QSTR(MP_QSTR_KeyError), MP_ROM_PTR(&mp_type_KeyError) }, + { MP_ROM_QSTR(MP_QSTR_LookupError), MP_ROM_PTR(&mp_type_LookupError) }, + { MP_ROM_QSTR(MP_QSTR_MemoryError), MP_ROM_PTR(&mp_type_MemoryError) }, + { MP_ROM_QSTR(MP_QSTR_NameError), MP_ROM_PTR(&mp_type_NameError) }, + { MP_ROM_QSTR(MP_QSTR_NotImplementedError), MP_ROM_PTR(&mp_type_NotImplementedError) }, + { MP_ROM_QSTR(MP_QSTR_OSError), MP_ROM_PTR(&mp_type_OSError) }, + { MP_ROM_QSTR(MP_QSTR_OverflowError), MP_ROM_PTR(&mp_type_OverflowError) }, + { MP_ROM_QSTR(MP_QSTR_RuntimeError), MP_ROM_PTR(&mp_type_RuntimeError) }, + #if MICROPY_PY_ASYNC_AWAIT + { MP_ROM_QSTR(MP_QSTR_StopAsyncIteration), MP_ROM_PTR(&mp_type_StopAsyncIteration) }, + #endif + { MP_ROM_QSTR(MP_QSTR_StopIteration), MP_ROM_PTR(&mp_type_StopIteration) }, + { MP_ROM_QSTR(MP_QSTR_SyntaxError), MP_ROM_PTR(&mp_type_SyntaxError) }, + { MP_ROM_QSTR(MP_QSTR_SystemExit), MP_ROM_PTR(&mp_type_SystemExit) }, + { MP_ROM_QSTR(MP_QSTR_TypeError), MP_ROM_PTR(&mp_type_TypeError) }, + #if MICROPY_PY_BUILTINS_STR_UNICODE + { MP_ROM_QSTR(MP_QSTR_UnicodeError), MP_ROM_PTR(&mp_type_UnicodeError) }, + #endif + { MP_ROM_QSTR(MP_QSTR_ValueError), MP_ROM_PTR(&mp_type_ValueError) }, + #if MICROPY_EMIT_NATIVE + { MP_ROM_QSTR(MP_QSTR_ViperTypeError), MP_ROM_PTR(&mp_type_ViperTypeError) }, + #endif + { MP_ROM_QSTR(MP_QSTR_ZeroDivisionError), MP_ROM_PTR(&mp_type_ZeroDivisionError) }, + + // Extra builtins as defined by a port + MICROPY_PORT_BUILTINS + MICROPY_PORT_EXTRA_BUILTINS +}; + +MP_DEFINE_CONST_DICT(mp_module_builtins_globals, mp_module_builtins_globals_table); + +const mp_obj_module_t mp_module_builtins = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_builtins_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_builtins, mp_module_builtins); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modcmath.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modcmath.c new file mode 100644 index 00000000..33cb00cb --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modcmath.c @@ -0,0 +1,154 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/builtin.h" + +#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_BUILTINS_COMPLEX && MICROPY_PY_CMATH + +#include + +// phase(z): returns the phase of the number z in the range (-pi, +pi] +static mp_obj_t mp_cmath_phase(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(atan2)(imag, real)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_phase_obj, mp_cmath_phase); + +// polar(z): returns the polar form of z as a tuple +static mp_obj_t mp_cmath_polar(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + mp_obj_t tuple[2] = { + mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag)), + mp_obj_new_float(MICROPY_FLOAT_C_FUN(atan2)(imag, real)), + }; + return mp_obj_new_tuple(2, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_polar_obj, mp_cmath_polar); + +// rect(r, phi): returns the complex number with modulus r and phase phi +static mp_obj_t mp_cmath_rect(mp_obj_t r_obj, mp_obj_t phi_obj) { + mp_float_t r = mp_obj_get_float(r_obj); + mp_float_t phi = mp_obj_get_float(phi_obj); + return mp_obj_new_complex(r * MICROPY_FLOAT_C_FUN(cos)(phi), r * MICROPY_FLOAT_C_FUN(sin)(phi)); +} +static MP_DEFINE_CONST_FUN_OBJ_2(mp_cmath_rect_obj, mp_cmath_rect); + +// exp(z): return the exponential of z +static mp_obj_t mp_cmath_exp(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real); + return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_exp_obj, mp_cmath_exp); + +// log(z): return the natural logarithm of z, with branch cut along the negative real axis +// TODO can take second argument, being the base +static mp_obj_t mp_cmath_log(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + return mp_obj_new_complex(MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(log)(real * real + imag * imag), MICROPY_FLOAT_C_FUN(atan2)(imag, real)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log_obj, mp_cmath_log); + +#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS +// log10(z): return the base-10 logarithm of z, with branch cut along the negative real axis +static mp_obj_t mp_cmath_log10(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + return mp_obj_new_complex(MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(log10)(real * real + imag * imag), MICROPY_FLOAT_CONST(0.4342944819032518) * MICROPY_FLOAT_C_FUN(atan2)(imag, real)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log10_obj, mp_cmath_log10); +#endif + +// sqrt(z): return the square-root of z +static mp_obj_t mp_cmath_sqrt(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(pow)(real * real + imag * imag, MICROPY_FLOAT_CONST(0.25)); + mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real); + return mp_obj_new_complex(sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta), sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_sqrt_obj, mp_cmath_sqrt); + +// cos(z): return the cosine of z +static mp_obj_t mp_cmath_cos(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + return mp_obj_new_complex(MICROPY_FLOAT_C_FUN(cos)(real) * MICROPY_FLOAT_C_FUN(cosh)(imag), -MICROPY_FLOAT_C_FUN(sin)(real) * MICROPY_FLOAT_C_FUN(sinh)(imag)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_cos_obj, mp_cmath_cos); + +// sin(z): return the sine of z +static mp_obj_t mp_cmath_sin(mp_obj_t z_obj) { + mp_float_t real, imag; + mp_obj_get_complex(z_obj, &real, &imag); + return mp_obj_new_complex(MICROPY_FLOAT_C_FUN(sin)(real) * MICROPY_FLOAT_C_FUN(cosh)(imag), MICROPY_FLOAT_C_FUN(cos)(real) * MICROPY_FLOAT_C_FUN(sinh)(imag)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_sin_obj, mp_cmath_sin); + +static const mp_rom_map_elem_t mp_module_cmath_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cmath) }, + { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e }, + { MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi }, + { MP_ROM_QSTR(MP_QSTR_phase), MP_ROM_PTR(&mp_cmath_phase_obj) }, + { MP_ROM_QSTR(MP_QSTR_polar), MP_ROM_PTR(&mp_cmath_polar_obj) }, + { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&mp_cmath_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&mp_cmath_exp_obj) }, + { MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&mp_cmath_log_obj) }, + #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + { MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&mp_cmath_log10_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_cmath_sqrt_obj) }, + // { MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&mp_cmath_acos_obj) }, + // { MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&mp_cmath_asin_obj) }, + // { MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&mp_cmath_atan_obj) }, + { MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&mp_cmath_cos_obj) }, + { MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&mp_cmath_sin_obj) }, + // { MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&mp_cmath_tan_obj) }, + // { MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&mp_cmath_acosh_obj) }, + // { MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&mp_cmath_asinh_obj) }, + // { MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&mp_cmath_atanh_obj) }, + // { MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&mp_cmath_cosh_obj) }, + // { MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&mp_cmath_sinh_obj) }, + // { MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&mp_cmath_tanh_obj) }, + // { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_cmath_isfinite_obj) }, + // { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_cmath_isinf_obj) }, + // { MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_cmath_isnan_obj) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_cmath_globals, mp_module_cmath_globals_table); + +const mp_obj_module_t mp_module_cmath = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_cmath_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_cmath, mp_module_cmath); + +#endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_BUILTINS_COMPLEX && MICROPY_PY_CMATH diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modcollections.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modcollections.c new file mode 100644 index 00000000..46326d13 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modcollections.c @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/builtin.h" + +#if MICROPY_PY_COLLECTIONS + +static const mp_rom_map_elem_t mp_module_collections_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_collections) }, + #if MICROPY_PY_COLLECTIONS_DEQUE + { MP_ROM_QSTR(MP_QSTR_deque), MP_ROM_PTR(&mp_type_deque) }, + #endif + { MP_ROM_QSTR(MP_QSTR_namedtuple), MP_ROM_PTR(&mp_namedtuple_obj) }, + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT + { MP_ROM_QSTR(MP_QSTR_OrderedDict), MP_ROM_PTR(&mp_type_ordereddict) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_collections_globals, mp_module_collections_globals_table); + +const mp_obj_module_t mp_module_collections = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_collections_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_collections, mp_module_collections); + +#endif // MICROPY_PY_COLLECTIONS diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/moderrno.c b/non_catalog_apps/mp_flipper/lib/micropython/py/moderrno.c new file mode 100644 index 00000000..58a141c1 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/moderrno.c @@ -0,0 +1,124 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 "py/obj.h" +#include "py/mperrno.h" + +#if MICROPY_PY_ERRNO + +// This list can be defined per port in mpconfigport.h to tailor it to a +// specific port's needs. If it's not defined then we provide a default. +#ifndef MICROPY_PY_ERRNO_LIST +#define MICROPY_PY_ERRNO_LIST \ + X(EPERM) \ + X(ENOENT) \ + X(EIO) \ + X(EBADF) \ + X(EAGAIN) \ + X(ENOMEM) \ + X(EACCES) \ + X(EEXIST) \ + X(ENODEV) \ + X(EISDIR) \ + X(EINVAL) \ + X(EOPNOTSUPP) \ + X(EADDRINUSE) \ + X(ECONNABORTED) \ + X(ECONNRESET) \ + X(ENOBUFS) \ + X(ENOTCONN) \ + X(ETIMEDOUT) \ + X(ECONNREFUSED) \ + X(EHOSTUNREACH) \ + X(EALREADY) \ + X(EINPROGRESS) \ + +#endif + +#if MICROPY_PY_ERRNO_ERRORCODE +static const mp_rom_map_elem_t errorcode_table[] = { + #define X(e) { MP_ROM_INT(MP_##e), MP_ROM_QSTR(MP_QSTR_##e) }, + MICROPY_PY_ERRNO_LIST +#undef X +}; + +static const mp_obj_dict_t errorcode_dict = { + .base = {&mp_type_dict}, + .map = { + .all_keys_are_qstrs = 0, // keys are integers + .is_fixed = 1, + .is_ordered = 1, + .used = MP_ARRAY_SIZE(errorcode_table), + .alloc = MP_ARRAY_SIZE(errorcode_table), + .table = (mp_map_elem_t *)(mp_rom_map_elem_t *)errorcode_table, + }, +}; +#endif + +static const mp_rom_map_elem_t mp_module_errno_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_errno) }, + #if MICROPY_PY_ERRNO_ERRORCODE + { MP_ROM_QSTR(MP_QSTR_errorcode), MP_ROM_PTR(&errorcode_dict) }, + #endif + + #define X(e) { MP_ROM_QSTR(MP_QSTR_##e), MP_ROM_INT(MP_##e) }, + MICROPY_PY_ERRNO_LIST +#undef X +}; + +static MP_DEFINE_CONST_DICT(mp_module_errno_globals, mp_module_errno_globals_table); + +const mp_obj_module_t mp_module_errno = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_errno_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_errno, mp_module_errno); + +qstr mp_errno_to_str(mp_obj_t errno_val) { + #if MICROPY_PY_ERRNO_ERRORCODE + // We have the errorcode dict so can do a lookup using the hash map + mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)&errorcode_dict.map, errno_val, MP_MAP_LOOKUP); + if (elem == NULL) { + return MP_QSTRnull; + } else { + return MP_OBJ_QSTR_VALUE(elem->value); + } + #else + // We don't have the errorcode dict so do a simple search in the modules dict + for (size_t i = 0; i < MP_ARRAY_SIZE(mp_module_errno_globals_table); ++i) { + if (errno_val == mp_module_errno_globals_table[i].value) { + return MP_OBJ_QSTR_VALUE(mp_module_errno_globals_table[i].key); + } + } + return MP_QSTRnull; + #endif +} + +#endif // MICROPY_PY_ERRNO diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modgc.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modgc.c new file mode 100644 index 00000000..47902d8c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modgc.c @@ -0,0 +1,125 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpstate.h" +#include "py/obj.h" +#include "py/gc.h" + +#if MICROPY_PY_GC && MICROPY_ENABLE_GC + +// collect(): run a garbage collection +static mp_obj_t py_gc_collect(void) { + gc_collect(); + #if MICROPY_PY_GC_COLLECT_RETVAL + return MP_OBJ_NEW_SMALL_INT(MP_STATE_MEM(gc_collected)); + #else + return mp_const_none; + #endif +} +MP_DEFINE_CONST_FUN_OBJ_0(gc_collect_obj, py_gc_collect); + +// disable(): disable the garbage collector +static mp_obj_t gc_disable(void) { + MP_STATE_MEM(gc_auto_collect_enabled) = 0; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(gc_disable_obj, gc_disable); + +// enable(): enable the garbage collector +static mp_obj_t gc_enable(void) { + MP_STATE_MEM(gc_auto_collect_enabled) = 1; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(gc_enable_obj, gc_enable); + +static mp_obj_t gc_isenabled(void) { + return mp_obj_new_bool(MP_STATE_MEM(gc_auto_collect_enabled)); +} +MP_DEFINE_CONST_FUN_OBJ_0(gc_isenabled_obj, gc_isenabled); + +// mem_free(): return the number of bytes of available heap RAM +static mp_obj_t gc_mem_free(void) { + gc_info_t info; + gc_info(&info); + #if MICROPY_GC_SPLIT_HEAP_AUTO + // Include max_new_split value here as a more useful heuristic + return MP_OBJ_NEW_SMALL_INT(info.free + info.max_new_split); + #else + return MP_OBJ_NEW_SMALL_INT(info.free); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_free_obj, gc_mem_free); + +// mem_alloc(): return the number of bytes of heap RAM that are allocated +static mp_obj_t gc_mem_alloc(void) { + gc_info_t info; + gc_info(&info); + return MP_OBJ_NEW_SMALL_INT(info.used); +} +MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_alloc_obj, gc_mem_alloc); + +#if MICROPY_GC_ALLOC_THRESHOLD +static mp_obj_t gc_threshold(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + if (MP_STATE_MEM(gc_alloc_threshold) == (size_t)-1) { + return MP_OBJ_NEW_SMALL_INT(-1); + } + return mp_obj_new_int(MP_STATE_MEM(gc_alloc_threshold) * MICROPY_BYTES_PER_GC_BLOCK); + } + mp_int_t val = mp_obj_get_int(args[0]); + if (val < 0) { + MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1; + } else { + MP_STATE_MEM(gc_alloc_threshold) = val / MICROPY_BYTES_PER_GC_BLOCK; + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gc_threshold_obj, 0, 1, gc_threshold); +#endif + +static const mp_rom_map_elem_t mp_module_gc_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gc) }, + { MP_ROM_QSTR(MP_QSTR_collect), MP_ROM_PTR(&gc_collect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable), MP_ROM_PTR(&gc_disable_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable), MP_ROM_PTR(&gc_enable_obj) }, + { MP_ROM_QSTR(MP_QSTR_isenabled), MP_ROM_PTR(&gc_isenabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem_free), MP_ROM_PTR(&gc_mem_free_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem_alloc), MP_ROM_PTR(&gc_mem_alloc_obj) }, + #if MICROPY_GC_ALLOC_THRESHOLD + { MP_ROM_QSTR(MP_QSTR_threshold), MP_ROM_PTR(&gc_threshold_obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_gc_globals, mp_module_gc_globals_table); + +const mp_obj_module_t mp_module_gc = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_gc_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_gc, mp_module_gc); + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modio.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modio.c new file mode 100644 index 00000000..d3e563db --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modio.c @@ -0,0 +1,231 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" +#include "py/builtin.h" +#include "py/stream.h" +#include "py/binary.h" +#include "py/objarray.h" +#include "py/objstringio.h" +#include "py/frozenmod.h" + +#if MICROPY_PY_IO + +#if MICROPY_PY_IO_IOBASE + +static const mp_obj_type_t mp_type_iobase; + +static const mp_obj_base_t iobase_singleton = {&mp_type_iobase}; + +static mp_obj_t iobase_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type; + (void)n_args; + (void)n_kw; + (void)args; + return MP_OBJ_FROM_PTR(&iobase_singleton); +} + +static mp_uint_t iobase_read_write(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode, qstr qst) { + mp_obj_t dest[3]; + mp_load_method(obj, qst, dest); + mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, size, buf}; + dest[2] = MP_OBJ_FROM_PTR(&ar); + mp_obj_t ret_obj = mp_call_method_n_kw(1, 0, dest); + if (ret_obj == mp_const_none) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + mp_int_t ret = mp_obj_get_int(ret_obj); + if (ret >= 0) { + return ret; + } else { + *errcode = -ret; + return MP_STREAM_ERROR; + } +} +static mp_uint_t iobase_read(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode) { + return iobase_read_write(obj, buf, size, errcode, MP_QSTR_readinto); +} + +static mp_uint_t iobase_write(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode) { + return iobase_read_write(obj, (void *)buf, size, errcode, MP_QSTR_write); +} + +static mp_uint_t iobase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) { + mp_obj_t dest[4]; + mp_load_method(obj, MP_QSTR_ioctl, dest); + dest[2] = mp_obj_new_int_from_uint(request); + dest[3] = mp_obj_new_int_from_uint(arg); + mp_int_t ret = mp_obj_get_int(mp_call_method_n_kw(2, 0, dest)); + if (ret >= 0) { + return ret; + } else { + *errcode = -ret; + return MP_STREAM_ERROR; + } +} + +static const mp_stream_p_t iobase_p = { + .read = iobase_read, + .write = iobase_write, + .ioctl = iobase_ioctl, +}; + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_iobase, + MP_QSTR_IOBase, + MP_TYPE_FLAG_NONE, + make_new, iobase_make_new, + protocol, &iobase_p + ); + +#endif // MICROPY_PY_IO_IOBASE + +#if MICROPY_PY_IO_BUFFEREDWRITER +typedef struct _mp_obj_bufwriter_t { + mp_obj_base_t base; + mp_obj_t stream; + size_t alloc; + size_t len; + byte buf[0]; +} mp_obj_bufwriter_t; + +static mp_obj_t bufwriter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 2, 2, false); + size_t alloc = mp_obj_get_int(args[1]); + mp_obj_bufwriter_t *o = mp_obj_malloc_var(mp_obj_bufwriter_t, buf, byte, alloc, type); + o->stream = args[0]; + o->alloc = alloc; + o->len = 0; + return o; +} + +static mp_uint_t bufwriter_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { + mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in); + + mp_uint_t org_size = size; + + while (size > 0) { + mp_uint_t rem = self->alloc - self->len; + if (size < rem) { + memcpy(self->buf + self->len, buf, size); + self->len += size; + return org_size; + } + + // Buffer flushing policy here is to flush entire buffer all the time. + // This allows e.g. to have a block device as backing storage and write + // entire block to it. memcpy below is not ideal and could be optimized + // in some cases. But the way it is now it at least ensures that buffer + // is word-aligned, to guard against obscure cases when it matters, e.g. + // https://github.com/micropython/micropython/issues/1863 + memcpy(self->buf + self->len, buf, rem); + buf = (byte *)buf + rem; + size -= rem; + mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->alloc, errcode); + (void)out_sz; + if (*errcode != 0) { + return MP_STREAM_ERROR; + } + // TODO: try to recover from a case of non-blocking stream, e.g. move + // remaining chunk to the beginning of buffer. + assert(out_sz == self->alloc); + self->len = 0; + } + + return org_size; +} + +static mp_obj_t bufwriter_flush(mp_obj_t self_in) { + mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->len != 0) { + int err; + mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->len, &err); + (void)out_sz; + // TODO: try to recover from a case of non-blocking stream, e.g. move + // remaining chunk to the beginning of buffer. + assert(out_sz == self->len); + self->len = 0; + if (err != 0) { + mp_raise_OSError(err); + } + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(bufwriter_flush_obj, bufwriter_flush); + +static const mp_rom_map_elem_t bufwriter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&bufwriter_flush_obj) }, +}; +static MP_DEFINE_CONST_DICT(bufwriter_locals_dict, bufwriter_locals_dict_table); + +static const mp_stream_p_t bufwriter_stream_p = { + .write = bufwriter_write, +}; + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bufwriter, + MP_QSTR_BufferedWriter, + MP_TYPE_FLAG_NONE, + make_new, bufwriter_make_new, + protocol, &bufwriter_stream_p, + locals_dict, &bufwriter_locals_dict + ); +#endif // MICROPY_PY_IO_BUFFEREDWRITER + +static const mp_rom_map_elem_t mp_module_io_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_io) }, + // Note: mp_builtin_open_obj should be defined by port, it's not + // part of the core. + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + #if MICROPY_PY_IO_IOBASE + { MP_ROM_QSTR(MP_QSTR_IOBase), MP_ROM_PTR(&mp_type_iobase) }, + #endif + { MP_ROM_QSTR(MP_QSTR_StringIO), MP_ROM_PTR(&mp_type_stringio) }, + #if MICROPY_PY_IO_BYTESIO + { MP_ROM_QSTR(MP_QSTR_BytesIO), MP_ROM_PTR(&mp_type_bytesio) }, + #endif + #if MICROPY_PY_IO_BUFFEREDWRITER + { MP_ROM_QSTR(MP_QSTR_BufferedWriter), MP_ROM_PTR(&mp_type_bufwriter) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_io_globals, mp_module_io_globals_table); + +const mp_obj_module_t mp_module_io = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_io_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_io, mp_module_io); + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modmath.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modmath.c new file mode 100644 index 00000000..db30f0e6 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modmath.c @@ -0,0 +1,440 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 Damien P. George + * + * 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 "py/builtin.h" +#include "py/runtime.h" + +#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH + +#include + +// M_PI is not part of the math.h standard and may not be defined +// And by defining our own we can ensure it uses the correct const format. +#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846) +#define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962) +#define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885) + +static NORETURN void math_error(void) { + mp_raise_ValueError(MP_ERROR_TEXT("math domain error")); +} + +static mp_obj_t math_generic_1(mp_obj_t x_obj, mp_float_t (*f)(mp_float_t)) { + mp_float_t x = mp_obj_get_float(x_obj); + mp_float_t ans = f(x); + if ((isnan(ans) && !isnan(x)) || (isinf(ans) && !isinf(x))) { + math_error(); + } + return mp_obj_new_float(ans); +} + +static mp_obj_t math_generic_2(mp_obj_t x_obj, mp_obj_t y_obj, mp_float_t (*f)(mp_float_t, mp_float_t)) { + mp_float_t x = mp_obj_get_float(x_obj); + mp_float_t y = mp_obj_get_float(y_obj); + mp_float_t ans = f(x, y); + if ((isnan(ans) && !isnan(x) && !isnan(y)) || (isinf(ans) && !isinf(x) && !isinf(y))) { + math_error(); + } + return mp_obj_new_float(ans); +} + +#define MATH_FUN_1(py_name, c_name) \ + static mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { \ + return math_generic_1(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \ + } \ + static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_1_TO_BOOL(py_name, c_name) \ + static mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_bool(c_name(mp_obj_get_float(x_obj))); } \ + static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_1_TO_INT(py_name, c_name) \ + static mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_int_from_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj))); } \ + static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_2(py_name, c_name) \ + static mp_obj_t mp_math_##py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \ + return math_generic_2(x_obj, y_obj, MICROPY_FLOAT_C_FUN(c_name)); \ + } \ + static MP_DEFINE_CONST_FUN_OBJ_2(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_2_FLT_INT(py_name, c_name) \ + static mp_obj_t mp_math_##py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \ + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj), mp_obj_get_int(y_obj))); \ + } \ + static MP_DEFINE_CONST_FUN_OBJ_2(mp_math_##py_name##_obj, mp_math_##py_name); + +#if MP_NEED_LOG2 +#undef log2 +#undef log2f +// 1.442695040888963407354163704 is 1/_M_LN2 +mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) { + return MICROPY_FLOAT_C_FUN(log)(x) * MICROPY_FLOAT_CONST(1.442695040888963407354163704); +} +#endif + +// sqrt(x): returns the square root of x +MATH_FUN_1(sqrt, sqrt) +// pow(x, y): returns x to the power of y +#if MICROPY_PY_MATH_POW_FIX_NAN +mp_float_t pow_func(mp_float_t x, mp_float_t y) { + // pow(base, 0) returns 1 for any base, even when base is NaN + // pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN + if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) { + return MICROPY_FLOAT_CONST(1.0); + } + return MICROPY_FLOAT_C_FUN(pow)(x, y); +} +MATH_FUN_2(pow, pow_func) +#else +MATH_FUN_2(pow, pow) +#endif +// exp(x) +MATH_FUN_1(exp, exp) +#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS +// expm1(x) +MATH_FUN_1(expm1, expm1) +// log2(x) +MATH_FUN_1(log2, log2) +// log10(x) +MATH_FUN_1(log10, log10) +// cosh(x) +MATH_FUN_1(cosh, cosh) +// sinh(x) +MATH_FUN_1(sinh, sinh) +// tanh(x) +MATH_FUN_1(tanh, tanh) +// acosh(x) +MATH_FUN_1(acosh, acosh) +// asinh(x) +MATH_FUN_1(asinh, asinh) +// atanh(x) +MATH_FUN_1(atanh, atanh) +#endif +// cos(x) +MATH_FUN_1(cos, cos) +// sin(x) +MATH_FUN_1(sin, sin) +// tan(x) +MATH_FUN_1(tan, tan) +// acos(x) +MATH_FUN_1(acos, acos) +// asin(x) +MATH_FUN_1(asin, asin) +// atan(x) +MATH_FUN_1(atan, atan) +// atan2(y, x) +#if MICROPY_PY_MATH_ATAN2_FIX_INFNAN +mp_float_t atan2_func(mp_float_t x, mp_float_t y) { + if (isinf(x) && isinf(y)) { + return copysign(y < 0 ? MP_3_PI_4 : MP_PI_4, x); + } + return atan2(x, y); +} +MATH_FUN_2(atan2, atan2_func) +#else +MATH_FUN_2(atan2, atan2) +#endif +// ceil(x) +MATH_FUN_1_TO_INT(ceil, ceil) +// copysign(x, y) +static mp_float_t MICROPY_FLOAT_C_FUN(copysign_func)(mp_float_t x, mp_float_t y) { + return MICROPY_FLOAT_C_FUN(copysign)(x, y); +} +MATH_FUN_2(copysign, copysign_func) +// fabs(x) +static mp_float_t MICROPY_FLOAT_C_FUN(fabs_func)(mp_float_t x) { + return MICROPY_FLOAT_C_FUN(fabs)(x); +} +MATH_FUN_1(fabs, fabs_func) +// floor(x) +MATH_FUN_1_TO_INT(floor, floor) // TODO: delegate to x.__floor__() if x is not a float +// fmod(x, y) +#if MICROPY_PY_MATH_FMOD_FIX_INFNAN +mp_float_t fmod_func(mp_float_t x, mp_float_t y) { + return (!isinf(x) && isinf(y)) ? x : fmod(x, y); +} +MATH_FUN_2(fmod, fmod_func) +#else +MATH_FUN_2(fmod, fmod) +#endif +// isfinite(x) +MATH_FUN_1_TO_BOOL(isfinite, isfinite) +// isinf(x) +MATH_FUN_1_TO_BOOL(isinf, isinf) +// isnan(x) +MATH_FUN_1_TO_BOOL(isnan, isnan) +// trunc(x) +MATH_FUN_1_TO_INT(trunc, trunc) +// ldexp(x, exp) +MATH_FUN_2_FLT_INT(ldexp, ldexp) +#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS +// erf(x): return the error function of x +MATH_FUN_1(erf, erf) +// erfc(x): return the complementary error function of x +MATH_FUN_1(erfc, erfc) +// gamma(x): return the gamma function of x +MATH_FUN_1(gamma, tgamma) +// lgamma(x): return the natural logarithm of the gamma function of x +MATH_FUN_1(lgamma, lgamma) +#endif +// TODO: fsum + +#if MICROPY_PY_MATH_ISCLOSE +static mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_rel_tol, ARG_abs_tol }; + static const mp_arg_t allowed_args[] = { + {MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + const mp_float_t a = mp_obj_get_float(pos_args[0]); + const mp_float_t b = mp_obj_get_float(pos_args[1]); + const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL + ? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj); + const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj); + if (rel_tol < (mp_float_t)0.0 || abs_tol < (mp_float_t)0.0) { + math_error(); + } + if (a == b) { + return mp_const_true; + } + const mp_float_t difference = MICROPY_FLOAT_C_FUN(fabs)(a - b); + if (isinf(difference)) { // Either a or b is inf + return mp_const_false; + } + if ((difference <= abs_tol) || + (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * a)) || + (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * b))) { + return mp_const_true; + } + return mp_const_false; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_math_isclose_obj, 2, mp_math_isclose); +#endif + +// Function that takes a variable number of arguments + +// log(x[, base]) +static mp_obj_t mp_math_log(size_t n_args, const mp_obj_t *args) { + mp_float_t x = mp_obj_get_float(args[0]); + if (x <= (mp_float_t)0.0) { + math_error(); + } + mp_float_t l = MICROPY_FLOAT_C_FUN(log)(x); + if (n_args == 1) { + return mp_obj_new_float(l); + } else { + mp_float_t base = mp_obj_get_float(args[1]); + if (base <= (mp_float_t)0.0) { + math_error(); + } else if (base == (mp_float_t)1.0) { + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); + } + return mp_obj_new_float(l / MICROPY_FLOAT_C_FUN(log)(base)); + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_math_log_obj, 1, 2, mp_math_log); + +// Functions that return a tuple + +// frexp(x): converts a floating-point number to fractional and integral components +static mp_obj_t mp_math_frexp(mp_obj_t x_obj) { + int int_exponent = 0; + mp_float_t significand = MICROPY_FLOAT_C_FUN(frexp)(mp_obj_get_float(x_obj), &int_exponent); + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_float(significand); + tuple[1] = mp_obj_new_int(int_exponent); + return mp_obj_new_tuple(2, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_frexp_obj, mp_math_frexp); + +// modf(x) +static mp_obj_t mp_math_modf(mp_obj_t x_obj) { + mp_float_t int_part = 0.0; + mp_float_t x = mp_obj_get_float(x_obj); + mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(x, &int_part); + #if MICROPY_PY_MATH_MODF_FIX_NEGZERO + if (fractional_part == MICROPY_FLOAT_CONST(0.0)) { + fractional_part = copysign(fractional_part, x); + } + #endif + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_float(fractional_part); + tuple[1] = mp_obj_new_float(int_part); + return mp_obj_new_tuple(2, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_modf_obj, mp_math_modf); + +// Angular conversions + +// radians(x) +static mp_obj_t mp_math_radians(mp_obj_t x_obj) { + return mp_obj_new_float(mp_obj_get_float(x_obj) * (MP_PI / MICROPY_FLOAT_CONST(180.0))); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_radians_obj, mp_math_radians); + +// degrees(x) +static mp_obj_t mp_math_degrees(mp_obj_t x_obj) { + return mp_obj_new_float(mp_obj_get_float(x_obj) * (MICROPY_FLOAT_CONST(180.0) / MP_PI)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_degrees_obj, mp_math_degrees); + +#if MICROPY_PY_MATH_FACTORIAL + +#if MICROPY_OPT_MATH_FACTORIAL + +// factorial(x): slightly efficient recursive implementation +static mp_obj_t mp_math_factorial_inner(mp_uint_t start, mp_uint_t end) { + if (start == end) { + return mp_obj_new_int(start); + } else if (end - start == 1) { + return mp_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_NEW_SMALL_INT(start), MP_OBJ_NEW_SMALL_INT(end)); + } else if (end - start == 2) { + mp_obj_t left = MP_OBJ_NEW_SMALL_INT(start); + mp_obj_t middle = MP_OBJ_NEW_SMALL_INT(start + 1); + mp_obj_t right = MP_OBJ_NEW_SMALL_INT(end); + mp_obj_t tmp = mp_binary_op(MP_BINARY_OP_MULTIPLY, left, middle); + return mp_binary_op(MP_BINARY_OP_MULTIPLY, tmp, right); + } else { + mp_uint_t middle = start + ((end - start) >> 1); + mp_obj_t left = mp_math_factorial_inner(start, middle); + mp_obj_t right = mp_math_factorial_inner(middle + 1, end); + return mp_binary_op(MP_BINARY_OP_MULTIPLY, left, right); + } +} +static mp_obj_t mp_math_factorial(mp_obj_t x_obj) { + mp_int_t max = mp_obj_get_int(x_obj); + if (max < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("negative factorial")); + } else if (max == 0) { + return MP_OBJ_NEW_SMALL_INT(1); + } + return mp_math_factorial_inner(1, max); +} + +#else + +// factorial(x): squared difference implementation +// based on http://www.luschny.de/math/factorial/index.html +static mp_obj_t mp_math_factorial(mp_obj_t x_obj) { + mp_int_t max = mp_obj_get_int(x_obj); + if (max < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("negative factorial")); + } else if (max <= 1) { + return MP_OBJ_NEW_SMALL_INT(1); + } + mp_int_t h = max >> 1; + mp_int_t q = h * h; + mp_int_t r = q << 1; + if (max & 1) { + r *= max; + } + mp_obj_t prod = MP_OBJ_NEW_SMALL_INT(r); + for (mp_int_t num = 1; num < max - 2; num += 2) { + q -= num; + prod = mp_binary_op(MP_BINARY_OP_MULTIPLY, prod, MP_OBJ_NEW_SMALL_INT(q)); + } + return prod; +} + +#endif + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_factorial_obj, mp_math_factorial); + +#endif + +static const mp_rom_map_elem_t mp_module_math_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_math) }, + { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e }, + { MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi }, + #if MICROPY_PY_MATH_CONSTANTS + { MP_ROM_QSTR(MP_QSTR_tau), mp_const_float_tau }, + { MP_ROM_QSTR(MP_QSTR_inf), mp_const_float_inf }, + { MP_ROM_QSTR(MP_QSTR_nan), mp_const_float_nan }, + #endif + { MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_math_sqrt_obj) }, + { MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_math_pow_obj) }, + { MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&mp_math_exp_obj) }, + #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + { MP_ROM_QSTR(MP_QSTR_expm1), MP_ROM_PTR(&mp_math_expm1_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&mp_math_log_obj) }, + #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + { MP_ROM_QSTR(MP_QSTR_log2), MP_ROM_PTR(&mp_math_log2_obj) }, + { MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&mp_math_log10_obj) }, + { MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&mp_math_cosh_obj) }, + { MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&mp_math_sinh_obj) }, + { MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&mp_math_tanh_obj) }, + { MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&mp_math_acosh_obj) }, + { MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&mp_math_asinh_obj) }, + { MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&mp_math_atanh_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&mp_math_cos_obj) }, + { MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&mp_math_sin_obj) }, + { MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&mp_math_tan_obj) }, + { MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&mp_math_acos_obj) }, + { MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&mp_math_asin_obj) }, + { MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&mp_math_atan_obj) }, + { MP_ROM_QSTR(MP_QSTR_atan2), MP_ROM_PTR(&mp_math_atan2_obj) }, + { MP_ROM_QSTR(MP_QSTR_ceil), MP_ROM_PTR(&mp_math_ceil_obj) }, + { MP_ROM_QSTR(MP_QSTR_copysign), MP_ROM_PTR(&mp_math_copysign_obj) }, + { MP_ROM_QSTR(MP_QSTR_fabs), MP_ROM_PTR(&mp_math_fabs_obj) }, + { MP_ROM_QSTR(MP_QSTR_floor), MP_ROM_PTR(&mp_math_floor_obj) }, + { MP_ROM_QSTR(MP_QSTR_fmod), MP_ROM_PTR(&mp_math_fmod_obj) }, + { MP_ROM_QSTR(MP_QSTR_frexp), MP_ROM_PTR(&mp_math_frexp_obj) }, + { MP_ROM_QSTR(MP_QSTR_ldexp), MP_ROM_PTR(&mp_math_ldexp_obj) }, + { MP_ROM_QSTR(MP_QSTR_modf), MP_ROM_PTR(&mp_math_modf_obj) }, + { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) }, + { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) }, + { MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) }, + #if MICROPY_PY_MATH_ISCLOSE + { MP_ROM_QSTR(MP_QSTR_isclose), MP_ROM_PTR(&mp_math_isclose_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) }, + { MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) }, + { MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) }, + #if MICROPY_PY_MATH_FACTORIAL + { MP_ROM_QSTR(MP_QSTR_factorial), MP_ROM_PTR(&mp_math_factorial_obj) }, + #endif + #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + { MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&mp_math_erf_obj) }, + { MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&mp_math_erfc_obj) }, + { MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&mp_math_gamma_obj) }, + { MP_ROM_QSTR(MP_QSTR_lgamma), MP_ROM_PTR(&mp_math_lgamma_obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_math_globals, mp_module_math_globals_table); + +const mp_obj_module_t mp_module_math = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_math_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_math, mp_module_math); + +#endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modmicropython.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modmicropython.c new file mode 100644 index 00000000..af6ad017 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modmicropython.c @@ -0,0 +1,217 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/builtin.h" +#include "py/stackctrl.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mphal.h" + +#if MICROPY_PY_MICROPYTHON + +// Various builtins specific to MicroPython runtime, +// living in micropython module + +#if MICROPY_ENABLE_COMPILER +static mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + return MP_OBJ_NEW_SMALL_INT(MP_STATE_VM(mp_optimise_value)); + } else { + MP_STATE_VM(mp_optimise_value) = mp_obj_get_int(args[0]); + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_opt_level_obj, 0, 1, mp_micropython_opt_level); +#endif + +#if MICROPY_PY_MICROPYTHON_MEM_INFO + +#if MICROPY_MEM_STATS +static mp_obj_t mp_micropython_mem_total(void) { + return MP_OBJ_NEW_SMALL_INT(m_get_total_bytes_allocated()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_total_obj, mp_micropython_mem_total); + +static mp_obj_t mp_micropython_mem_current(void) { + return MP_OBJ_NEW_SMALL_INT(m_get_current_bytes_allocated()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_current_obj, mp_micropython_mem_current); + +static mp_obj_t mp_micropython_mem_peak(void) { + return MP_OBJ_NEW_SMALL_INT(m_get_peak_bytes_allocated()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_peak_obj, mp_micropython_mem_peak); +#endif + +mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args) { + (void)args; + #if MICROPY_MEM_STATS + mp_printf(&mp_plat_print, "mem: total=" UINT_FMT ", current=" UINT_FMT ", peak=" UINT_FMT "\n", + (mp_uint_t)m_get_total_bytes_allocated(), (mp_uint_t)m_get_current_bytes_allocated(), (mp_uint_t)m_get_peak_bytes_allocated()); + #endif + #if MICROPY_STACK_CHECK + mp_printf(&mp_plat_print, "stack: " UINT_FMT " out of " UINT_FMT "\n", + mp_stack_usage(), (mp_uint_t)MP_STATE_THREAD(stack_limit)); + #else + mp_printf(&mp_plat_print, "stack: " UINT_FMT "\n", mp_stack_usage()); + #endif + #if MICROPY_ENABLE_GC + gc_dump_info(&mp_plat_print); + if (n_args == 1) { + // arg given means dump gc allocation table + gc_dump_alloc_table(&mp_plat_print); + } + #else + (void)n_args; + #endif + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_mem_info_obj, 0, 1, mp_micropython_mem_info); + +static mp_obj_t mp_micropython_qstr_info(size_t n_args, const mp_obj_t *args) { + (void)args; + size_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; + qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes); + mp_printf(&mp_plat_print, "qstr pool: n_pool=%u, n_qstr=%u, n_str_data_bytes=%u, n_total_bytes=%u\n", + n_pool, n_qstr, n_str_data_bytes, n_total_bytes); + if (n_args == 1) { + // arg given means dump qstr data + qstr_dump_data(); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_qstr_info_obj, 0, 1, mp_micropython_qstr_info); + +#endif // MICROPY_PY_MICROPYTHON_MEM_INFO + +#if MICROPY_PY_MICROPYTHON_STACK_USE +static mp_obj_t mp_micropython_stack_use(void) { + return MP_OBJ_NEW_SMALL_INT(mp_stack_usage()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_stack_use); +#endif + +#if MICROPY_ENABLE_PYSTACK +static mp_obj_t mp_micropython_pystack_use(void) { + return MP_OBJ_NEW_SMALL_INT(mp_pystack_usage()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_pystack_use_obj, mp_micropython_pystack_use); +#endif + +#if MICROPY_ENABLE_GC +static mp_obj_t mp_micropython_heap_lock(void) { + gc_lock(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_lock_obj, mp_micropython_heap_lock); + +static mp_obj_t mp_micropython_heap_unlock(void) { + gc_unlock(); + return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth)); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_unlock_obj, mp_micropython_heap_unlock); + +#if MICROPY_PY_MICROPYTHON_HEAP_LOCKED +static mp_obj_t mp_micropython_heap_locked(void) { + return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth)); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_locked_obj, mp_micropython_heap_locked); +#endif +#endif + +#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0) +static MP_DEFINE_CONST_FUN_OBJ_1(mp_alloc_emergency_exception_buf_obj, mp_alloc_emergency_exception_buf); +#endif + +#if MICROPY_KBD_EXCEPTION +static mp_obj_t mp_micropython_kbd_intr(mp_obj_t int_chr_in) { + mp_hal_set_interrupt_char(mp_obj_get_int(int_chr_in)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_micropython_kbd_intr_obj, mp_micropython_kbd_intr); +#endif + +#if MICROPY_ENABLE_SCHEDULER +static mp_obj_t mp_micropython_schedule(mp_obj_t function, mp_obj_t arg) { + if (!mp_sched_schedule(function, arg)) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("schedule queue full")); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_schedule); +#endif + +static const mp_rom_map_elem_t mp_module_micropython_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) }, + { MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) }, + #if MICROPY_ENABLE_COMPILER + { MP_ROM_QSTR(MP_QSTR_opt_level), MP_ROM_PTR(&mp_micropython_opt_level_obj) }, + #endif + #if MICROPY_PY_MICROPYTHON_MEM_INFO + #if MICROPY_MEM_STATS + { MP_ROM_QSTR(MP_QSTR_mem_total), MP_ROM_PTR(&mp_micropython_mem_total_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem_current), MP_ROM_PTR(&mp_micropython_mem_current_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem_peak), MP_ROM_PTR(&mp_micropython_mem_peak_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_mem_info), MP_ROM_PTR(&mp_micropython_mem_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_qstr_info), MP_ROM_PTR(&mp_micropython_qstr_info_obj) }, + #endif + #if MICROPY_PY_MICROPYTHON_STACK_USE + { MP_ROM_QSTR(MP_QSTR_stack_use), MP_ROM_PTR(&mp_micropython_stack_use_obj) }, + #endif + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0) + { MP_ROM_QSTR(MP_QSTR_alloc_emergency_exception_buf), MP_ROM_PTR(&mp_alloc_emergency_exception_buf_obj) }, + #endif + #if MICROPY_ENABLE_PYSTACK + { MP_ROM_QSTR(MP_QSTR_pystack_use), MP_ROM_PTR(&mp_micropython_pystack_use_obj) }, + #endif + #if MICROPY_ENABLE_GC + { MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) }, + { MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) }, + #if MICROPY_PY_MICROPYTHON_HEAP_LOCKED + { MP_ROM_QSTR(MP_QSTR_heap_locked), MP_ROM_PTR(&mp_micropython_heap_locked_obj) }, + #endif + #endif + #if MICROPY_KBD_EXCEPTION + { MP_ROM_QSTR(MP_QSTR_kbd_intr), MP_ROM_PTR(&mp_micropython_kbd_intr_obj) }, + #endif + #if MICROPY_ENABLE_SCHEDULER + { MP_ROM_QSTR(MP_QSTR_schedule), MP_ROM_PTR(&mp_micropython_schedule_obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_micropython_globals, mp_module_micropython_globals_table); + +const mp_obj_module_t mp_module_micropython = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_micropython_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_micropython, mp_module_micropython); + +#endif // MICROPY_PY_MICROPYTHON diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modstruct.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modstruct.c new file mode 100644 index 00000000..3b9dd30a --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modstruct.c @@ -0,0 +1,278 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/runtime.h" +#include "py/builtin.h" +#include "py/objtuple.h" +#include "py/binary.h" +#include "py/parsenum.h" + +#if MICROPY_PY_STRUCT + +/* + This module implements most of character typecodes from CPython, with + some extensions: + + O - (Pointer to) an arbitrary Python object. This is useful for callback + data, etc. Note that you must keep reference to passed object in + your Python application, otherwise it may be garbage-collected, + and then when you get back this value from callback it may be + invalid (and lead to crash). + S - Pointer to a string (returned as a Python string). Note the + difference from "Ns", - the latter says "in this place of structure + is character data of up to N bytes length", while "S" means + "in this place of a structure is a pointer to zero-terminated + character data". + */ + +static char get_fmt_type(const char **fmt) { + char t = **fmt; + switch (t) { + case '!': + t = '>'; + break; + case '@': + case '=': + case '<': + case '>': + break; + default: + return '@'; + } + // Skip type char + (*fmt)++; + return t; +} + +static mp_uint_t get_fmt_num(const char **p) { + const char *num = *p; + uint len = 1; + while (unichar_isdigit(*++num)) { + len++; + } + mp_uint_t val = (mp_uint_t)MP_OBJ_SMALL_INT_VALUE(mp_parse_num_integer(*p, len, 10, NULL)); + *p = num; + return val; +} + +static size_t calc_size_items(const char *fmt, size_t *total_sz) { + char fmt_type = get_fmt_type(&fmt); + size_t total_cnt = 0; + size_t size; + for (size = 0; *fmt; fmt++) { + mp_uint_t cnt = 1; + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + + if (*fmt == 'x') { + size += cnt; + } else if (*fmt == 's') { + total_cnt += 1; + size += cnt; + } else { + total_cnt += cnt; + size_t align; + size_t sz = mp_binary_get_size(fmt_type, *fmt, &align); + while (cnt--) { + // Apply alignment + size = (size + align - 1) & ~(align - 1); + size += sz; + } + } + } + *total_sz = size; + return total_cnt; +} + +static mp_obj_t struct_calcsize(mp_obj_t fmt_in) { + const char *fmt = mp_obj_str_get_str(fmt_in); + size_t size; + calc_size_items(fmt, &size); + return MP_OBJ_NEW_SMALL_INT(size); +} +MP_DEFINE_CONST_FUN_OBJ_1(struct_calcsize_obj, struct_calcsize); + +static mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { + // unpack requires that the buffer be exactly the right size. + // unpack_from requires that the buffer be "big enough". + // Since we implement unpack and unpack_from using the same function + // we relax the "exact" requirement, and only implement "big enough". + const char *fmt = mp_obj_str_get_str(args[0]); + size_t total_sz; + size_t num_items = calc_size_items(fmt, &total_sz); + char fmt_type = get_fmt_type(&fmt); + mp_obj_tuple_t *res = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_items, NULL)); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + byte *p = bufinfo.buf; + byte *end_p = &p[bufinfo.len]; + mp_int_t offset = 0; + + if (n_args > 2) { + // offset arg provided + offset = mp_obj_get_int(args[2]); + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = bufinfo.len + offset; + if (offset < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + } + p += offset; + } + byte *p_base = p; + + // Check that the input buffer is big enough to unpack all the values + if (p + total_sz > end_p) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + + for (size_t i = 0; i < num_items;) { + mp_uint_t cnt = 1; + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + mp_obj_t item; + if (*fmt == 'x') { + p += cnt; + } else if (*fmt == 's') { + item = mp_obj_new_bytes(p, cnt); + p += cnt; + res->items[i++] = item; + } else { + while (cnt--) { + item = mp_binary_get_val(fmt_type, *fmt, p_base, &p); + res->items[i++] = item; + } + } + fmt++; + } + return MP_OBJ_FROM_PTR(res); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_unpack_from_obj, 2, 3, struct_unpack_from); + +// This function assumes there is enough room in p to store all the values +static void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, const mp_obj_t *args) { + const char *fmt = mp_obj_str_get_str(fmt_in); + char fmt_type = get_fmt_type(&fmt); + + byte *p_base = p; + size_t i; + for (i = 0; i < n_args;) { + mp_uint_t cnt = 1; + if (*fmt == '\0') { + // more arguments given than used by format string; CPython raises struct.error here + break; + } + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + + if (*fmt == 'x') { + memset(p, 0, cnt); + p += cnt; + } else if (*fmt == 's') { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ); + mp_uint_t to_copy = cnt; + if (bufinfo.len < to_copy) { + to_copy = bufinfo.len; + } + memcpy(p, bufinfo.buf, to_copy); + memset(p + to_copy, 0, cnt - to_copy); + p += cnt; + } else { + // If we run out of args then we just finish; CPython would raise struct.error + while (cnt-- && i < n_args) { + mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p); + } + } + fmt++; + } +} + +static mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) { + // TODO: "The arguments must match the values required by the format exactly." + mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0])); + vstr_t vstr; + vstr_init_len(&vstr, size); + byte *p = (byte *)vstr.buf; + memset(p, 0, size); + struct_pack_into_internal(args[0], p, n_args - 1, &args[1]); + return mp_obj_new_bytes_from_vstr(&vstr); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack); + +static mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + mp_int_t offset = mp_obj_get_int(args[2]); + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = (mp_int_t)bufinfo.len + offset; + if (offset < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + } + byte *p = (byte *)bufinfo.buf; + byte *end_p = &p[bufinfo.len]; + p += offset; + + // Check that the output buffer is big enough to hold all the values + mp_int_t sz = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0])); + if (p + sz > end_p) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + + struct_pack_into_internal(args[0], p, n_args - 3, &args[3]); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_into_obj, 3, MP_OBJ_FUN_ARGS_MAX, struct_pack_into); + +static const mp_rom_map_elem_t mp_module_struct_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_struct) }, + { MP_ROM_QSTR(MP_QSTR_calcsize), MP_ROM_PTR(&struct_calcsize_obj) }, + { MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&struct_pack_obj) }, + { MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&struct_pack_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&struct_unpack_from_obj) }, + { MP_ROM_QSTR(MP_QSTR_unpack_from), MP_ROM_PTR(&struct_unpack_from_obj) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_struct_globals, mp_module_struct_globals_table); + +const mp_obj_module_t mp_module_struct = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_struct_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_struct, mp_module_struct); + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modsys.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modsys.c new file mode 100644 index 00000000..e90ea223 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modsys.c @@ -0,0 +1,376 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2017 Paul Sokolovsky + * + * 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 "py/builtin.h" +#include "py/objlist.h" +#include "py/objmodule.h" +#include "py/objtuple.h" +#include "py/objstr.h" +#include "py/objint.h" +#include "py/objtype.h" +#include "py/stream.h" +#include "py/smallint.h" +#include "py/runtime.h" +#include "py/persistentcode.h" +#include "extmod/modplatform.h" +#include "genhdr/mpversion.h" + +#if MICROPY_PY_SYS_SETTRACE +#include "py/objmodule.h" +#include "py/profile.h" +#endif + +#if MICROPY_PY_SYS + +// defined per port; type of these is irrelevant, just need pointer +extern struct _mp_dummy_t mp_sys_stdin_obj; +extern struct _mp_dummy_t mp_sys_stdout_obj; +extern struct _mp_dummy_t mp_sys_stderr_obj; + +#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES +const mp_print_t mp_sys_stdout_print = {&mp_sys_stdout_obj, mp_stream_write_adaptor}; +#endif + +// version - Python language version that this implementation conforms to, as a string +static const MP_DEFINE_STR_OBJ(mp_sys_version_obj, "3.4.0; " MICROPY_BANNER_NAME_AND_VERSION); + +// version_info - Python language version that this implementation conforms to, as a tuple of ints +// TODO: CPython is now at 5-element array (major, minor, micro, releaselevel, serial), but save 2 els so far... +static const mp_rom_obj_tuple_t mp_sys_version_info_obj = {{&mp_type_tuple}, 3, {MP_ROM_INT(3), MP_ROM_INT(4), MP_ROM_INT(0)}}; + +// sys.implementation object +// this holds the MicroPython version +static const mp_rom_obj_tuple_t mp_sys_implementation_version_info_obj = { + {&mp_type_tuple}, + 4, + { + MP_ROM_INT(MICROPY_VERSION_MAJOR), + MP_ROM_INT(MICROPY_VERSION_MINOR), + MP_ROM_INT(MICROPY_VERSION_MICRO), + #if MICROPY_VERSION_PRERELEASE + MP_ROM_QSTR(MP_QSTR_preview), + #else + MP_ROM_QSTR(MP_QSTR_), + #endif + } +}; +static const MP_DEFINE_STR_OBJ(mp_sys_implementation_machine_obj, MICROPY_BANNER_MACHINE); +#define SYS_IMPLEMENTATION_ELEMS_BASE \ + MP_ROM_QSTR(MP_QSTR_micropython), \ + MP_ROM_PTR(&mp_sys_implementation_version_info_obj), \ + MP_ROM_PTR(&mp_sys_implementation_machine_obj) + +#if MICROPY_PERSISTENT_CODE_LOAD +#define SYS_IMPLEMENTATION_ELEMS__MPY \ + , MP_ROM_INT(MPY_FILE_HEADER_INT) +#else +#define SYS_IMPLEMENTATION_ELEMS__MPY +#endif + +#if MICROPY_PY_ATTRTUPLE +#if MICROPY_PREVIEW_VERSION_2 +#define SYS_IMPLEMENTATION_ELEMS__V2 \ + , MP_ROM_TRUE +#else +#define SYS_IMPLEMENTATION_ELEMS__V2 +#endif + +static const qstr impl_fields[] = { + MP_QSTR_name, + MP_QSTR_version, + MP_QSTR__machine, + #if MICROPY_PERSISTENT_CODE_LOAD + MP_QSTR__mpy, + #endif + #if MICROPY_PREVIEW_VERSION_2 + MP_QSTR__v2, + #endif +}; +static MP_DEFINE_ATTRTUPLE( + mp_sys_implementation_obj, + impl_fields, + 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_PREVIEW_VERSION_2, + SYS_IMPLEMENTATION_ELEMS_BASE + SYS_IMPLEMENTATION_ELEMS__MPY + SYS_IMPLEMENTATION_ELEMS__V2 + ); +#else +static const mp_rom_obj_tuple_t mp_sys_implementation_obj = { + {&mp_type_tuple}, + 3 + MICROPY_PERSISTENT_CODE_LOAD, + // Do not include SYS_IMPLEMENTATION_ELEMS__V2 because + // SYS_IMPLEMENTATION_ELEMS__MPY may be empty if + // MICROPY_PERSISTENT_CODE_LOAD is disabled, which means they'll share + // the same index. Cannot query _v2 if MICROPY_PY_ATTRTUPLE is + // disabled. + { + SYS_IMPLEMENTATION_ELEMS_BASE + SYS_IMPLEMENTATION_ELEMS__MPY + } +}; +#endif + +#undef I + +#ifdef MICROPY_PY_SYS_PLATFORM +// platform - the platform that MicroPython is running on +static const MP_DEFINE_STR_OBJ(mp_sys_platform_obj, MICROPY_PY_SYS_PLATFORM); +#endif + +#ifdef MICROPY_PY_SYS_EXECUTABLE +// executable - the path to the micropython binary +// This object is non-const and is populated at startup in main() +MP_DEFINE_STR_OBJ(mp_sys_executable_obj, ""); +#endif + +#if MICROPY_PY_SYS_INTERN +MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_intern_obj, mp_obj_str_intern_checked); +#endif + +// exit([retval]): raise SystemExit, with optional argument given to the exception +static mp_obj_t mp_sys_exit(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + mp_raise_type(&mp_type_SystemExit); + } else { + mp_raise_type_arg(&mp_type_SystemExit, args[0]); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit); + +static mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) { + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + void *stream_obj = &mp_sys_stdout_obj; + if (n_args > 1) { + mp_get_stream_raise(args[1], MP_STREAM_OP_WRITE); + stream_obj = MP_OBJ_TO_PTR(args[1]); + } + + mp_print_t print = {stream_obj, mp_stream_write_adaptor}; + mp_obj_print_exception(&print, args[0]); + #else + (void)n_args; + mp_obj_print_exception(&mp_plat_print, args[0]); + #endif + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_print_exception_obj, 1, 2, mp_sys_print_exception); + +#if MICROPY_PY_SYS_EXC_INFO +static mp_obj_t mp_sys_exc_info(void) { + mp_obj_t cur_exc = MP_OBJ_FROM_PTR(MP_STATE_VM(cur_exception)); + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); + + if (cur_exc == MP_OBJ_NULL) { + t->items[0] = mp_const_none; + t->items[1] = mp_const_none; + t->items[2] = mp_const_none; + return MP_OBJ_FROM_PTR(t); + } + + t->items[0] = MP_OBJ_FROM_PTR(mp_obj_get_type(cur_exc)); + t->items[1] = cur_exc; + t->items[2] = mp_const_none; + return MP_OBJ_FROM_PTR(t); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_exc_info_obj, mp_sys_exc_info); +#endif + +#if MICROPY_PY_SYS_GETSIZEOF +static mp_obj_t mp_sys_getsizeof(mp_obj_t obj) { + return mp_unary_op(MP_UNARY_OP_SIZEOF, obj); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof); +#endif + +#if MICROPY_PY_SYS_ATEXIT +// atexit(callback): Callback is called when sys.exit is called. +static mp_obj_t mp_sys_atexit(mp_obj_t obj) { + mp_obj_t old = MP_STATE_VM(sys_exitfunc); + MP_STATE_VM(sys_exitfunc) = obj; + return old; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_atexit_obj, mp_sys_atexit); +#endif + +#if MICROPY_PY_SYS_SETTRACE +// settrace(tracefunc): Set the system's trace function. +static mp_obj_t mp_sys_settrace(mp_obj_t obj) { + return mp_prof_settrace(obj); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); +#endif // MICROPY_PY_SYS_SETTRACE + +#if MICROPY_PY_SYS_PATH && !MICROPY_PY_SYS_ATTR_DELEGATION +#error "MICROPY_PY_SYS_PATH requires MICROPY_PY_SYS_ATTR_DELEGATION" +#endif + +#if MICROPY_PY_SYS_PS1_PS2 && !MICROPY_PY_SYS_ATTR_DELEGATION +#error "MICROPY_PY_SYS_PS1_PS2 requires MICROPY_PY_SYS_ATTR_DELEGATION" +#endif + +#if MICROPY_PY_SYS_TRACEBACKLIMIT && !MICROPY_PY_SYS_ATTR_DELEGATION +#error "MICROPY_PY_SYS_TRACEBACKLIMIT requires MICROPY_PY_SYS_ATTR_DELEGATION" +#endif + +#if MICROPY_PY_SYS_ATTR_DELEGATION && !MICROPY_MODULE_ATTR_DELEGATION +#error "MICROPY_PY_SYS_ATTR_DELEGATION requires MICROPY_MODULE_ATTR_DELEGATION" +#endif + +#if MICROPY_PY_SYS_ATTR_DELEGATION +// Must be kept in sync with the enum at the top of mpstate.h. +static const uint16_t sys_mutable_keys[] = { + #if MICROPY_PY_SYS_PATH + // Code should access this (as an mp_obj_t) for use with e.g. + // mp_obj_list_append by using the `mp_sys_path` macro defined in runtime.h. + MP_QSTR_path, + #endif + #if MICROPY_PY_SYS_PS1_PS2 + MP_QSTR_ps1, + MP_QSTR_ps2, + #endif + #if MICROPY_PY_SYS_TRACEBACKLIMIT + MP_QSTR_tracebacklimit, + #endif + MP_QSTRnull, +}; + +void mp_module_sys_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + MP_STATIC_ASSERT(MP_ARRAY_SIZE(sys_mutable_keys) == MP_SYS_MUTABLE_NUM + 1); + MP_STATIC_ASSERT(MP_ARRAY_SIZE(MP_STATE_VM(sys_mutable)) == MP_SYS_MUTABLE_NUM); + mp_module_generic_attr(attr, dest, sys_mutable_keys, MP_STATE_VM(sys_mutable)); +} +#endif + +static const mp_rom_map_elem_t mp_module_sys_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) }, + + #if MICROPY_PY_SYS_ARGV + { MP_ROM_QSTR(MP_QSTR_argv), MP_ROM_PTR(&MP_STATE_VM(mp_sys_argv_obj)) }, + #endif + { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&mp_sys_version_obj) }, + { MP_ROM_QSTR(MP_QSTR_version_info), MP_ROM_PTR(&mp_sys_version_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_implementation), MP_ROM_PTR(&mp_sys_implementation_obj) }, + #ifdef MICROPY_PY_SYS_PLATFORM + { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&mp_sys_platform_obj) }, + #endif + #if MP_ENDIANNESS_LITTLE + { MP_ROM_QSTR(MP_QSTR_byteorder), MP_ROM_QSTR(MP_QSTR_little) }, + #else + { MP_ROM_QSTR(MP_QSTR_byteorder), MP_ROM_QSTR(MP_QSTR_big) }, + #endif + + #if MICROPY_PY_SYS_MAXSIZE + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE + // Maximum mp_int_t value is not representable as small int, so we have + // little choice but to use MP_SMALL_INT_MAX. Apps also should be careful + // to not try to compare sys.maxsize to some literal number (as this + // number might not fit in available int size), but instead count number + // of "one" bits in sys.maxsize. + { MP_ROM_QSTR(MP_QSTR_maxsize), MP_ROM_INT(MP_SMALL_INT_MAX) }, + #else + { MP_ROM_QSTR(MP_QSTR_maxsize), MP_ROM_PTR(&mp_sys_maxsize_obj) }, + #endif + #endif + + #if MICROPY_PY_SYS_INTERN + { MP_ROM_QSTR(MP_QSTR_intern), MP_ROM_PTR(&mp_sys_intern_obj) }, + #endif + + #if MICROPY_PY_SYS_EXIT + { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mp_sys_exit_obj) }, + #endif + + #if MICROPY_PY_SYS_SETTRACE + { MP_ROM_QSTR(MP_QSTR_settrace), MP_ROM_PTR(&mp_sys_settrace_obj) }, + #endif + + #if MICROPY_PY_SYS_STDFILES + { MP_ROM_QSTR(MP_QSTR_stdin), MP_ROM_PTR(&mp_sys_stdin_obj) }, + { MP_ROM_QSTR(MP_QSTR_stdout), MP_ROM_PTR(&mp_sys_stdout_obj) }, + { MP_ROM_QSTR(MP_QSTR_stderr), MP_ROM_PTR(&mp_sys_stderr_obj) }, + #endif + + #if MICROPY_PY_SYS_MODULES + { MP_ROM_QSTR(MP_QSTR_modules), MP_ROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)) }, + #endif + #if MICROPY_PY_SYS_EXC_INFO + { MP_ROM_QSTR(MP_QSTR_exc_info), MP_ROM_PTR(&mp_sys_exc_info_obj) }, + #endif + #if MICROPY_PY_SYS_GETSIZEOF + { MP_ROM_QSTR(MP_QSTR_getsizeof), MP_ROM_PTR(&mp_sys_getsizeof_obj) }, + #endif + + #if MICROPY_PY_SYS_EXECUTABLE + { MP_ROM_QSTR(MP_QSTR_executable), MP_ROM_PTR(&mp_sys_executable_obj) }, + #endif + + /* + * Extensions to CPython + */ + + { MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&mp_sys_print_exception_obj) }, + #if MICROPY_PY_SYS_ATEXIT + { MP_ROM_QSTR(MP_QSTR_atexit), MP_ROM_PTR(&mp_sys_atexit_obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_sys_globals, mp_module_sys_globals_table); + +const mp_obj_module_t mp_module_sys = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_sys_globals, +}; + +// Unlike the other CPython-compatible modules, sys is not extensible from the +// filesystem. We rely on it to work so that things like sys.path are always +// available. +MP_REGISTER_MODULE(MP_QSTR_sys, mp_module_sys); + +#if MICROPY_PY_SYS_ARGV +// Code should access this (as an mp_obj_t) for use with e.g. +// mp_obj_list_append by using the `mp_sys_argv` macro defined in runtime.h. +MP_REGISTER_ROOT_POINTER(mp_obj_list_t mp_sys_argv_obj); +#endif + +#if MICROPY_PY_SYS_EXC_INFO +// current exception being handled, for sys.exc_info() +MP_REGISTER_ROOT_POINTER(mp_obj_base_t * cur_exception); +#endif + +#if MICROPY_PY_SYS_ATEXIT +// exposed through sys.atexit function +MP_REGISTER_ROOT_POINTER(mp_obj_t sys_exitfunc); +#endif + +#if MICROPY_PY_SYS_ATTR_DELEGATION +// Contains mutable sys attributes. +MP_REGISTER_ROOT_POINTER(mp_obj_t sys_mutable[MP_SYS_MUTABLE_NUM]); +MP_REGISTER_MODULE_DELEGATION(mp_module_sys, mp_module_sys_attr); +#endif + +#endif // MICROPY_PY_SYS diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/modthread.c b/non_catalog_apps/mp_flipper/lib/micropython/py/modthread.c new file mode 100644 index 00000000..2826fade --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/modthread.c @@ -0,0 +1,292 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * + * 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 "py/runtime.h" +#include "py/stackctrl.h" + +#if MICROPY_PY_THREAD + +#include "py/mpthread.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + +/****************************************************************/ +// Lock object + +static const mp_obj_type_t mp_type_thread_lock; + +typedef struct _mp_obj_thread_lock_t { + mp_obj_base_t base; + mp_thread_mutex_t mutex; + volatile bool locked; +} mp_obj_thread_lock_t; + +static mp_obj_thread_lock_t *mp_obj_new_thread_lock(void) { + mp_obj_thread_lock_t *self = mp_obj_malloc(mp_obj_thread_lock_t, &mp_type_thread_lock); + mp_thread_mutex_init(&self->mutex); + self->locked = false; + return self; +} + +static mp_obj_t thread_lock_acquire(size_t n_args, const mp_obj_t *args) { + mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(args[0]); + bool wait = true; + if (n_args > 1) { + wait = mp_obj_get_int(args[1]); + // TODO support timeout arg + } + MP_THREAD_GIL_EXIT(); + int ret = mp_thread_mutex_lock(&self->mutex, wait); + MP_THREAD_GIL_ENTER(); + if (ret == 0) { + return mp_const_false; + } else if (ret == 1) { + self->locked = true; + return mp_const_true; + } else { + mp_raise_OSError(-ret); + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock_acquire_obj, 1, 3, thread_lock_acquire); + +static mp_obj_t thread_lock_release(mp_obj_t self_in) { + mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in); + if (!self->locked) { + mp_raise_msg(&mp_type_RuntimeError, NULL); + } + self->locked = false; + MP_THREAD_GIL_EXIT(); + mp_thread_mutex_unlock(&self->mutex); + MP_THREAD_GIL_ENTER(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_release_obj, thread_lock_release); + +static mp_obj_t thread_lock_locked(mp_obj_t self_in) { + mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->locked); +} +static MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_locked_obj, thread_lock_locked); + +static mp_obj_t thread_lock___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; // unused + return thread_lock_release(args[0]); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock___exit___obj, 4, 4, thread_lock___exit__); + +static const mp_rom_map_elem_t thread_lock_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_acquire), MP_ROM_PTR(&thread_lock_acquire_obj) }, + { MP_ROM_QSTR(MP_QSTR_release), MP_ROM_PTR(&thread_lock_release_obj) }, + { MP_ROM_QSTR(MP_QSTR_locked), MP_ROM_PTR(&thread_lock_locked_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&thread_lock_acquire_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&thread_lock___exit___obj) }, +}; + +static MP_DEFINE_CONST_DICT(thread_lock_locals_dict, thread_lock_locals_dict_table); + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_thread_lock, + MP_QSTR_lock, + MP_TYPE_FLAG_NONE, + locals_dict, &thread_lock_locals_dict + ); + +/****************************************************************/ +// _thread module + +static size_t thread_stack_size = 0; + +static mp_obj_t mod_thread_get_ident(void) { + return mp_obj_new_int_from_uint(mp_thread_get_id()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_get_ident_obj, mod_thread_get_ident); + +static mp_obj_t mod_thread_stack_size(size_t n_args, const mp_obj_t *args) { + mp_obj_t ret = mp_obj_new_int_from_uint(thread_stack_size); + if (n_args == 0) { + thread_stack_size = 0; + } else { + thread_stack_size = mp_obj_get_int(args[0]); + } + return ret; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_stack_size_obj, 0, 1, mod_thread_stack_size); + +typedef struct _thread_entry_args_t { + mp_obj_dict_t *dict_locals; + mp_obj_dict_t *dict_globals; + size_t stack_size; + mp_obj_t fun; + size_t n_args; + size_t n_kw; + mp_obj_t args[]; +} thread_entry_args_t; + +static void *thread_entry(void *args_in) { + // Execution begins here for a new thread. We do not have the GIL. + + thread_entry_args_t *args = (thread_entry_args_t *)args_in; + + mp_state_thread_t ts; + mp_thread_init_state(&ts, args->stack_size, args->dict_locals, args->dict_globals); + + #if MICROPY_ENABLE_PYSTACK + // TODO threading and pystack is not fully supported, for now just make a small stack + mp_obj_t mini_pystack[128]; + mp_pystack_init(mini_pystack, &mini_pystack[128]); + #endif + + MP_THREAD_GIL_ENTER(); + + // signal that we are set up and running + mp_thread_start(); + + // TODO set more thread-specific state here: + // cur_exception (root pointer) + + DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top)); + + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_n_kw(args->fun, args->n_args, args->n_kw, args->args); + nlr_pop(); + } else { + // uncaught exception + // check for SystemExit + mp_obj_base_t *exc = (mp_obj_base_t *)nlr.ret_val; + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { + // swallow exception silently + } else { + // print exception out + mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in thread started by "); + mp_obj_print_helper(MICROPY_ERROR_PRINTER, args->fun, PRINT_REPR); + mp_printf(MICROPY_ERROR_PRINTER, "\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(exc)); + } + } + + DEBUG_printf("[thread] finish ts=%p\n", &ts); + + // signal that we are finished + mp_thread_finish(); + + MP_THREAD_GIL_EXIT(); + + return NULL; +} + +static mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) { + // This structure holds the Python function and arguments for thread entry. + // We copy all arguments into this structure to keep ownership of them. + // We must be very careful about root pointers because this pointer may + // disappear from our address space before the thread is created. + thread_entry_args_t *th_args; + + // get positional arguments + size_t pos_args_len; + mp_obj_t *pos_args_items; + mp_obj_get_array(args[1], &pos_args_len, &pos_args_items); + + // check for keyword arguments + if (n_args == 2) { + // just position arguments + th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len); + th_args->n_kw = 0; + } else { + // positional and keyword arguments + if (mp_obj_get_type(args[2]) != &mp_type_dict) { + mp_raise_TypeError(MP_ERROR_TEXT("expecting a dict for keyword args")); + } + mp_map_t *map = &((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[2]))->map; + th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len + 2 * map->used); + th_args->n_kw = map->used; + // copy across the keyword arguments + for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) { + if (mp_map_slot_is_filled(map, i)) { + th_args->args[n++] = map->table[i].key; + th_args->args[n++] = map->table[i].value; + } + } + } + + // copy across the positional arguments + th_args->n_args = pos_args_len; + memcpy(th_args->args, pos_args_items, pos_args_len * sizeof(mp_obj_t)); + + // pass our locals and globals into the new thread + th_args->dict_locals = mp_locals_get(); + th_args->dict_globals = mp_globals_get(); + + // set the stack size to use + th_args->stack_size = thread_stack_size; + + // set the function for thread entry + th_args->fun = args[0]; + + // spawn the thread! + return mp_obj_new_int_from_uint(mp_thread_create(thread_entry, th_args, &th_args->stack_size)); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_start_new_thread_obj, 2, 3, mod_thread_start_new_thread); + +static mp_obj_t mod_thread_exit(void) { + mp_raise_type(&mp_type_SystemExit); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_exit_obj, mod_thread_exit); + +static mp_obj_t mod_thread_allocate_lock(void) { + return MP_OBJ_FROM_PTR(mp_obj_new_thread_lock()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_allocate_lock_obj, mod_thread_allocate_lock); + +static const mp_rom_map_elem_t mp_module_thread_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__thread) }, + { MP_ROM_QSTR(MP_QSTR_LockType), MP_ROM_PTR(&mp_type_thread_lock) }, + { MP_ROM_QSTR(MP_QSTR_get_ident), MP_ROM_PTR(&mod_thread_get_ident_obj) }, + { MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&mod_thread_stack_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_start_new_thread), MP_ROM_PTR(&mod_thread_start_new_thread_obj) }, + { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mod_thread_exit_obj) }, + { MP_ROM_QSTR(MP_QSTR_allocate_lock), MP_ROM_PTR(&mod_thread_allocate_lock_obj) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_thread_globals, mp_module_thread_globals_table); + +const mp_obj_module_t mp_module_thread = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_thread_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR__thread, mp_module_thread); + +#endif // MICROPY_PY_THREAD diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpconfig.h b/non_catalog_apps/mp_flipper/lib/micropython/py/mpconfig.h new file mode 100644 index 00000000..af248026 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpconfig.h @@ -0,0 +1,2075 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MPCONFIG_H +#define MICROPY_INCLUDED_PY_MPCONFIG_H + +// Current version of MicroPython. This is used by sys.implementation.version +// as well as a fallback to generate MICROPY_GIT_TAG if the git repo or tags +// are unavailable. +#define MICROPY_VERSION_MAJOR 1 +#define MICROPY_VERSION_MINOR 23 +#define MICROPY_VERSION_MICRO 0 +#define MICROPY_VERSION_PRERELEASE 1 + +// Combined version as a 32-bit number for convenience to allow version +// comparison. Doesn't include prerelease state. +// e.g. #if MICROPY_VERSION < MICROPY_MAKE_VERSION(1, 22, 0) +#define MICROPY_MAKE_VERSION(major, minor, patch) (major << 16 | minor << 8 | patch) +#define MICROPY_VERSION MICROPY_MAKE_VERSION(MICROPY_VERSION_MAJOR, MICROPY_VERSION_MINOR, MICROPY_VERSION_MICRO) + +// String version. This is only used directly for platform.platform and +// os.uname().release. All other version info available in the firmware (e.g. +// the REPL banner) comes from MICROPY_GIT_TAG. +#define MICROPY_VERSION_STRING_BASE \ + MP_STRINGIFY(MICROPY_VERSION_MAJOR) "." \ + MP_STRINGIFY(MICROPY_VERSION_MINOR) "." \ + MP_STRINGIFY(MICROPY_VERSION_MICRO) +#if MICROPY_VERSION_PRERELEASE +#define MICROPY_VERSION_STRING MICROPY_VERSION_STRING_BASE "-preview" +#else +#define MICROPY_VERSION_STRING MICROPY_VERSION_STRING_BASE +#endif + +// If this is enabled, then in-progress/breaking changes slated for the 2.x +// release will be enabled. +#ifndef MICROPY_PREVIEW_VERSION_2 +#define MICROPY_PREVIEW_VERSION_2 (0) +#endif + +// This file contains default configuration settings for MicroPython. +// You can override any of the options below using mpconfigport.h file +// located in a directory of your port. + +// mpconfigport.h is a file containing configuration settings for a +// particular port. mpconfigport.h is actually a default name for +// such config, and it can be overridden using MP_CONFIGFILE preprocessor +// define (you can do that by passing CFLAGS_EXTRA='-DMP_CONFIGFILE=""' +// argument to make when using standard MicroPython makefiles). +// This is useful to have more than one config per port, for example, +// release vs debug configs, etc. Note that if you switch from one config +// to another, you must rebuild from scratch using "-B" switch to make. + +// Disable all optional features (i.e. minimal port). +#define MICROPY_CONFIG_ROM_LEVEL_MINIMUM (0) +// Only enable core features (constrained flash, e.g. STM32L072) +#define MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES (10) +// Enable most common features (small on-device flash, e.g. STM32F411) +#define MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES (20) +// Enable convenience features (medium on-device flash, e.g. STM32F405) +#define MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES (30) +// Enable all common features (large/external flash, rp2, unix) +#define MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES (40) +// Enable everything (e.g. coverage) +#define MICROPY_CONFIG_ROM_LEVEL_EVERYTHING (50) + +#ifdef MP_CONFIGFILE +#include MP_CONFIGFILE +#else +#include +#endif + +// Ports/boards should set this, but default to level=core. +#ifndef MICROPY_CONFIG_ROM_LEVEL +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES) +#endif + +// Helper macros for "have at least this level". +#define MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES (MICROPY_CONFIG_ROM_LEVEL >= MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES) +#define MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES (MICROPY_CONFIG_ROM_LEVEL >= MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES) +#define MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES (MICROPY_CONFIG_ROM_LEVEL >= MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) +#define MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_FULL_FEATURES (MICROPY_CONFIG_ROM_LEVEL >= MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) +#define MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING (MICROPY_CONFIG_ROM_LEVEL >= MICROPY_CONFIG_ROM_LEVEL_EVERYTHING) + +// Any options not explicitly set in mpconfigport.h will get default +// values below. + +/*****************************************************************************/ +/* Object representation */ + +// A MicroPython object is a machine word having the following form: +// - xxxx...xxx1 : a small int, bits 1 and above are the value +// - xxxx...x010 : a qstr, bits 3 and above are the value +// - xxxx...x110 : an immediate object, bits 3 and above are the value +// - xxxx...xx00 : a pointer to an mp_obj_base_t (unless a fake object) +#define MICROPY_OBJ_REPR_A (0) + +// A MicroPython object is a machine word having the following form: +// - xxxx...xx01 : a small int, bits 2 and above are the value +// - xxxx...x011 : a qstr, bits 3 and above are the value +// - xxxx...x111 : an immediate object, bits 3 and above are the value +// - xxxx...xxx0 : a pointer to an mp_obj_base_t (unless a fake object) +#define MICROPY_OBJ_REPR_B (1) + +// A MicroPython object is a machine word having the following form (called R): +// - iiiiiiii iiiiiiii iiiiiiii iiiiiii1 small int with 31-bit signed value +// - 01111111 1qqqqqqq qqqqqqqq qqqq0110 str with 19-bit qstr value +// - 01111111 10000000 00000000 ssss1110 immediate object with 4-bit value +// - s1111111 10000000 00000000 00000010 +/- inf +// - s1111111 1xxxxxxx xxxxxxxx xxxxx010 nan, x != 0 +// - seeeeeee efffffff ffffffff ffffff10 30-bit fp, e != 0xff +// - pppppppp pppppppp pppppppp pppppp00 ptr (4 byte alignment) +// Str, immediate and float stored as O = R + 0x80800000, retrieved as R = O - 0x80800000. +// This makes strs/immediates easier to encode/decode as they have zeros in the top 9 bits. +// This scheme only works with 32-bit word size and float enabled. +#define MICROPY_OBJ_REPR_C (2) + +// A MicroPython object is a 64-bit word having the following form (called R): +// - seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 64-bit fp, e != 0x7ff +// - s1111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000 +/- inf +// - 01111111 11111000 00000000 00000000 00000000 00000000 00000000 00000000 normalised nan +// - 01111111 11111101 iiiiiiii iiiiiiii iiiiiiii iiiiiiii iiiiiiii iiiiiii1 small int +// - 01111111 11111110 00000000 00000000 qqqqqqqq qqqqqqqq qqqqqqqq qqqqqqq1 str +// - 01111111 11111111 ss000000 00000000 00000000 00000000 00000000 00000000 immediate object +// - 01111111 11111100 00000000 00000000 pppppppp pppppppp pppppppp pppppp00 ptr (4 byte alignment) +// Stored as O = R + 0x8004000000000000, retrieved as R = O - 0x8004000000000000. +// This makes pointers have all zeros in the top 32 bits. +// Small-ints and strs have 1 as LSB to make sure they don't look like pointers +// to the garbage collector. +#define MICROPY_OBJ_REPR_D (3) + +#ifndef MICROPY_OBJ_REPR +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) +#endif + +// Whether to encode None/False/True as immediate objects instead of pointers to +// real objects. Reduces code size by a decent amount without hurting +// performance, for all representations except D on some architectures. +#ifndef MICROPY_OBJ_IMMEDIATE_OBJS +#define MICROPY_OBJ_IMMEDIATE_OBJS (MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D) +#endif + +/*****************************************************************************/ +/* Memory allocation policy */ + +// Number of bytes in memory allocation/GC block. Any size allocated will be +// rounded up to be multiples of this. +#ifndef MICROPY_BYTES_PER_GC_BLOCK +#define MICROPY_BYTES_PER_GC_BLOCK (4 * MP_BYTES_PER_OBJ_WORD) +#endif + +// Number of words allocated (in BSS) to the GC stack (minimum is 1) +#ifndef MICROPY_ALLOC_GC_STACK_SIZE +#define MICROPY_ALLOC_GC_STACK_SIZE (64) +#endif + +// The C-type to use for entries in the GC stack. By default it allows the +// heap to be as large as the address space, but the bit-width of this type can +// be reduced to save memory when the heap is small enough. The type must be +// big enough to index all blocks in the heap, which is set by +// heap-size-in-bytes / MICROPY_BYTES_PER_GC_BLOCK. +#ifndef MICROPY_GC_STACK_ENTRY_TYPE +#define MICROPY_GC_STACK_ENTRY_TYPE size_t +#endif + +// Be conservative and always clear to zero newly (re)allocated memory in the GC. +// This helps eliminate stray pointers that hold on to memory that's no longer +// used. It decreases performance due to unnecessary memory clearing. +// A memory manager which always clears memory can set this to 0. +// TODO Do analysis to understand why some memory is not properly cleared and +// find a more efficient way to clear it. +#ifndef MICROPY_GC_CONSERVATIVE_CLEAR +#define MICROPY_GC_CONSERVATIVE_CLEAR (MICROPY_ENABLE_GC) +#endif + +// Support automatic GC when reaching allocation threshold, +// configurable by gc.threshold(). +#ifndef MICROPY_GC_ALLOC_THRESHOLD +#define MICROPY_GC_ALLOC_THRESHOLD (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Number of bytes to allocate initially when creating new chunks to store +// interned string data. Smaller numbers lead to more chunks being needed +// and more wastage at the end of the chunk. Larger numbers lead to wasted +// space at the end when no more strings need interning. +#ifndef MICROPY_ALLOC_QSTR_CHUNK_INIT +#define MICROPY_ALLOC_QSTR_CHUNK_INIT (128) +#endif + +// Initial amount for lexer indentation level +#ifndef MICROPY_ALLOC_LEXER_INDENT_INIT +#define MICROPY_ALLOC_LEXER_INDENT_INIT (10) +#endif + +// Increment for lexer indentation level +#ifndef MICROPY_ALLOC_LEXEL_INDENT_INC +#define MICROPY_ALLOC_LEXEL_INDENT_INC (8) +#endif + +// Initial amount for parse rule stack +#ifndef MICROPY_ALLOC_PARSE_RULE_INIT +#define MICROPY_ALLOC_PARSE_RULE_INIT (64) +#endif + +// Increment for parse rule stack +#ifndef MICROPY_ALLOC_PARSE_RULE_INC +#define MICROPY_ALLOC_PARSE_RULE_INC (16) +#endif + +// Initial amount for parse result stack +#ifndef MICROPY_ALLOC_PARSE_RESULT_INIT +#define MICROPY_ALLOC_PARSE_RESULT_INIT (32) +#endif + +// Increment for parse result stack +#ifndef MICROPY_ALLOC_PARSE_RESULT_INC +#define MICROPY_ALLOC_PARSE_RESULT_INC (16) +#endif + +// Strings this length or less will be interned by the parser +#ifndef MICROPY_ALLOC_PARSE_INTERN_STRING_LEN +#define MICROPY_ALLOC_PARSE_INTERN_STRING_LEN (10) +#endif + +// Number of bytes to allocate initially when creating new chunks to store +// parse nodes. Small leads to fragmentation, large leads to excess use. +#ifndef MICROPY_ALLOC_PARSE_CHUNK_INIT +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (128) +#endif + +// Initial amount for ids in a scope +#ifndef MICROPY_ALLOC_SCOPE_ID_INIT +#define MICROPY_ALLOC_SCOPE_ID_INIT (4) +#endif + +// Increment for ids in a scope +#ifndef MICROPY_ALLOC_SCOPE_ID_INC +#define MICROPY_ALLOC_SCOPE_ID_INC (6) +#endif + +// Maximum length of a path in the filesystem +// So we can allocate a buffer on the stack for path manipulation in import +#ifndef MICROPY_ALLOC_PATH_MAX +#define MICROPY_ALLOC_PATH_MAX (512) +#endif + +// Initial size of module dict +#ifndef MICROPY_MODULE_DICT_SIZE +#define MICROPY_MODULE_DICT_SIZE (1) +#endif + +// Initial size of sys.modules dict +#ifndef MICROPY_LOADED_MODULES_DICT_SIZE +#define MICROPY_LOADED_MODULES_DICT_SIZE (3) +#endif + +// Whether realloc/free should be passed allocated memory region size +// You must enable this if MICROPY_MEM_STATS is enabled +#ifndef MICROPY_MALLOC_USES_ALLOCATED_SIZE +#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (0) +#endif + +// Number of bytes used to store qstr length +// Dictates hard limit on maximum Python identifier length, but 1 byte +// (limit of 255 bytes in an identifier) should be enough for everyone +#ifndef MICROPY_QSTR_BYTES_IN_LEN +#define MICROPY_QSTR_BYTES_IN_LEN (1) +#endif + +// Number of bytes used to store qstr hash +#ifndef MICROPY_QSTR_BYTES_IN_HASH +#if MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES +#define MICROPY_QSTR_BYTES_IN_HASH (2) +#elif MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES +#define MICROPY_QSTR_BYTES_IN_HASH (1) +#else +#define MICROPY_QSTR_BYTES_IN_HASH (0) +#endif +#endif + +// Avoid using C stack when making Python function calls. C stack still +// may be used if there's no free heap. +#ifndef MICROPY_STACKLESS +#define MICROPY_STACKLESS (0) +#endif + +// Never use C stack when making Python function calls. This may break +// testsuite as will subtly change which exception is thrown in case +// of too deep recursion and other similar cases. +#ifndef MICROPY_STACKLESS_STRICT +#define MICROPY_STACKLESS_STRICT (0) +#endif + +// Don't use alloca calls. As alloca() is not part of ANSI C, this +// workaround option is provided for compilers lacking this de-facto +// standard function. The way it works is allocating from heap, and +// relying on garbage collection to free it eventually. This is of +// course much less optimal than real alloca(). +#if defined(MICROPY_NO_ALLOCA) && MICROPY_NO_ALLOCA +#undef alloca +#define alloca(x) m_malloc(x) +#endif + +/*****************************************************************************/ +/* MicroPython emitters */ + +// Whether to support loading of persistent code +#ifndef MICROPY_PERSISTENT_CODE_LOAD +#define MICROPY_PERSISTENT_CODE_LOAD (0) +#endif + +// Whether to support saving of persistent code, i.e. for mpy-cross to +// generate .mpy files. Enabling this enables additional metadata on raw code +// objects which is also required for sys.settrace. +#ifndef MICROPY_PERSISTENT_CODE_SAVE +#define MICROPY_PERSISTENT_CODE_SAVE (MICROPY_PY_SYS_SETTRACE) +#endif + +// Whether to support saving persistent code to a file via mp_raw_code_save_file +#ifndef MICROPY_PERSISTENT_CODE_SAVE_FILE +#define MICROPY_PERSISTENT_CODE_SAVE_FILE (0) +#endif + +// Whether generated code can persist independently of the VM/runtime instance +// This is enabled automatically when needed by other features +#ifndef MICROPY_PERSISTENT_CODE +#define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY) +#endif + +// Whether bytecode uses a qstr_table to map internal qstr indices in the bytecode +// to global qstr values in the runtime (behaviour when feature is enabled), or +// just stores global qstr values directly in the bytecode. This must be enabled +// if MICROPY_PERSISTENT_CODE is enabled. +#ifndef MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE +#define MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE (MICROPY_PERSISTENT_CODE) +#endif + +// Whether to emit x64 native code +#ifndef MICROPY_EMIT_X64 +#define MICROPY_EMIT_X64 (0) +#endif + +// Whether to emit x86 native code +#ifndef MICROPY_EMIT_X86 +#define MICROPY_EMIT_X86 (0) +#endif + +// Whether to emit thumb native code +#ifndef MICROPY_EMIT_THUMB +#define MICROPY_EMIT_THUMB (0) +#endif + +// Whether to emit ARMv7-M instruction support in thumb native code +#ifndef MICROPY_EMIT_THUMB_ARMV7M +#define MICROPY_EMIT_THUMB_ARMV7M (1) +#endif + +// Whether to enable the thumb inline assembler +#ifndef MICROPY_EMIT_INLINE_THUMB +#define MICROPY_EMIT_INLINE_THUMB (0) +#endif + +// Whether to enable float support in the Thumb2 inline assembler +#ifndef MICROPY_EMIT_INLINE_THUMB_FLOAT +#define MICROPY_EMIT_INLINE_THUMB_FLOAT (1) +#endif + +// Whether to emit ARM native code +#ifndef MICROPY_EMIT_ARM +#define MICROPY_EMIT_ARM (0) +#endif + +// Whether to emit Xtensa native code +#ifndef MICROPY_EMIT_XTENSA +#define MICROPY_EMIT_XTENSA (0) +#endif + +// Whether to enable the Xtensa inline assembler +#ifndef MICROPY_EMIT_INLINE_XTENSA +#define MICROPY_EMIT_INLINE_XTENSA (0) +#endif + +// Whether to emit Xtensa-Windowed native code +#ifndef MICROPY_EMIT_XTENSAWIN +#define MICROPY_EMIT_XTENSAWIN (0) +#endif + +// Convenience definition for whether any native emitter is enabled +#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN) + +// Some architectures cannot read byte-wise from executable memory. In this case +// the prelude for a native function (which usually sits after the machine code) +// must be separated and placed somewhere where it can be read byte-wise. +#define MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE (MICROPY_EMIT_XTENSAWIN) + +// Convenience definition for whether any inline assembler emitter is enabled +#define MICROPY_EMIT_INLINE_ASM (MICROPY_EMIT_INLINE_THUMB || MICROPY_EMIT_INLINE_XTENSA) + +// Convenience definition for whether any native or inline assembler emitter is enabled +#define MICROPY_EMIT_MACHINE_CODE (MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM) + +// Whether native relocatable code loaded from .mpy files is explicitly tracked +// so that the GC cannot reclaim it. Needed on architectures that allocate +// executable memory on the MicroPython heap and don't explicitly track this +// data some other way. +#ifndef MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE +#if !MICROPY_EMIT_MACHINE_CODE || defined(MP_PLAT_ALLOC_EXEC) || defined(MP_PLAT_COMMIT_EXEC) +#define MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE (0) +#else +#define MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE (1) +#endif +#endif + +/*****************************************************************************/ +/* Compiler configuration */ + +// Whether to include the compiler +#ifndef MICROPY_ENABLE_COMPILER +#define MICROPY_ENABLE_COMPILER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether the compiler is dynamically configurable (ie at runtime) +// This will disable the ability to execute native/viper code +#ifndef MICROPY_DYNAMIC_COMPILER +#define MICROPY_DYNAMIC_COMPILER (0) +#endif + +// Whether the compiler allows compiling top-level await expressions +#ifndef MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT +#define MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT (0) +#endif + +// Whether to enable constant folding; eg 1+2 rewritten as 3 +#ifndef MICROPY_COMP_CONST_FOLDING +#define MICROPY_COMP_CONST_FOLDING (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to compile constant tuples immediately to their respective objects; eg (1, True) +// Otherwise the tuple will be built at runtime +#ifndef MICROPY_COMP_CONST_TUPLE +#define MICROPY_COMP_CONST_TUPLE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to enable optimisations for constant literals, eg OrderedDict +#ifndef MICROPY_COMP_CONST_LITERAL +#define MICROPY_COMP_CONST_LITERAL (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to enable lookup of constants in modules; eg module.CONST +#ifndef MICROPY_COMP_MODULE_CONST +#define MICROPY_COMP_MODULE_CONST (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to enable constant optimisation; id = const(value) +#ifndef MICROPY_COMP_CONST +#define MICROPY_COMP_CONST (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to enable optimisation of: a, b = c, d +// Costs 124 bytes (Thumb2) +#ifndef MICROPY_COMP_DOUBLE_TUPLE_ASSIGN +#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to enable optimisation of: a, b, c = d, e, f +// Requires MICROPY_COMP_DOUBLE_TUPLE_ASSIGN and costs 68 bytes (Thumb2) +#ifndef MICROPY_COMP_TRIPLE_TUPLE_ASSIGN +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to enable optimisation of: return a if b else c +// Costs about 80 bytes (Thumb2) and saves 2 bytes of bytecode for each use +#ifndef MICROPY_COMP_RETURN_IF_EXPR +#define MICROPY_COMP_RETURN_IF_EXPR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +/*****************************************************************************/ +/* Internal debugging stuff */ + +// Whether to collect memory allocation stats +#ifndef MICROPY_MEM_STATS +#define MICROPY_MEM_STATS (0) +#endif + +// The mp_print_t printer used for debugging output +#ifndef MICROPY_DEBUG_PRINTER +#define MICROPY_DEBUG_PRINTER (&mp_plat_print) +#endif + +// Whether to build functions that print debugging info: +// mp_bytecode_print +// mp_parse_node_print +#ifndef MICROPY_DEBUG_PRINTERS +#define MICROPY_DEBUG_PRINTERS (0) +#endif + +// Whether to enable all debugging outputs (it will be extremely verbose) +#ifndef MICROPY_DEBUG_VERBOSE +#define MICROPY_DEBUG_VERBOSE (0) +#endif + +// Whether to enable debugging versions of MP_OBJ_NULL/STOP_ITERATION/SENTINEL +#ifndef MICROPY_DEBUG_MP_OBJ_SENTINELS +#define MICROPY_DEBUG_MP_OBJ_SENTINELS (0) +#endif + +// Whether to print parse rule names (rather than integers) in mp_parse_node_print +#ifndef MICROPY_DEBUG_PARSE_RULE_NAME +#define MICROPY_DEBUG_PARSE_RULE_NAME (0) +#endif + +// Whether to enable a simple VM stack overflow check +#ifndef MICROPY_DEBUG_VM_STACK_OVERFLOW +#define MICROPY_DEBUG_VM_STACK_OVERFLOW (0) +#endif + +// Whether to enable extra instrumentation for valgrind +#ifndef MICROPY_DEBUG_VALGRIND +#define MICROPY_DEBUG_VALGRIND (0) +#endif + +/*****************************************************************************/ +/* Optimisations */ + +// Whether to use computed gotos in the VM, or a switch +// Computed gotos are roughly 10% faster, and increase VM code size by a little, +// e.g. ~1kiB on Cortex M4. +// Note: enabling this will use the gcc-specific extensions of ranged designated +// initialisers and addresses of labels, which are not part of the C99 standard. +#ifndef MICROPY_OPT_COMPUTED_GOTO +#define MICROPY_OPT_COMPUTED_GOTO (0) +#endif + +// Optimise the fast path for loading attributes from instance types. Increases +// Thumb2 code size by about 48 bytes. +#ifndef MICROPY_OPT_LOAD_ATTR_FAST_PATH +#define MICROPY_OPT_LOAD_ATTR_FAST_PATH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Use extra RAM to cache map lookups by remembering the likely location of +// the index. Avoids the hash computation on unordered maps, and avoids the +// linear search on ordered (especially in-ROM) maps. Can provide a +10-15% +// performance improvement on benchmarks involving lots of attribute access +// or dictionary lookup. +#ifndef MICROPY_OPT_MAP_LOOKUP_CACHE +#define MICROPY_OPT_MAP_LOOKUP_CACHE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// How much RAM (in bytes) to use for the map lookup cache. +#ifndef MICROPY_OPT_MAP_LOOKUP_CACHE_SIZE +#define MICROPY_OPT_MAP_LOOKUP_CACHE_SIZE (128) +#endif + +// Whether to use fast versions of bitwise operations (and, or, xor) when the +// arguments are both positive. Increases Thumb2 code size by about 250 bytes. +#ifndef MICROPY_OPT_MPZ_BITWISE +#define MICROPY_OPT_MPZ_BITWISE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + + +// Whether math.factorial is large, fast and recursive (1) or small and slow (0). +#ifndef MICROPY_OPT_MATH_FACTORIAL +#define MICROPY_OPT_MATH_FACTORIAL (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +/*****************************************************************************/ +/* Python internal features */ + +// Whether to enable import of external modules +// When disabled, only importing of built-in modules is supported +// When enabled, a port must implement mp_import_stat (among other things) +#ifndef MICROPY_ENABLE_EXTERNAL_IMPORT +#define MICROPY_ENABLE_EXTERNAL_IMPORT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to use the POSIX reader for importing files +#ifndef MICROPY_READER_POSIX +#define MICROPY_READER_POSIX (0) +#endif + +// Whether to use the VFS reader for importing files +#ifndef MICROPY_READER_VFS +#define MICROPY_READER_VFS (0) +#endif + +// Whether any readers have been defined +#ifndef MICROPY_HAS_FILE_READER +#define MICROPY_HAS_FILE_READER (MICROPY_READER_POSIX || MICROPY_READER_VFS) +#endif + +// Hook for the VM at the start of the opcode loop (can contain variable +// definitions usable by the other hook functions) +#ifndef MICROPY_VM_HOOK_INIT +#define MICROPY_VM_HOOK_INIT +#endif + +// Hook for the VM during the opcode loop (but only after jump opcodes) +#ifndef MICROPY_VM_HOOK_LOOP +#define MICROPY_VM_HOOK_LOOP +#endif + +// Hook for the VM just before return opcode is finished being interpreted +#ifndef MICROPY_VM_HOOK_RETURN +#define MICROPY_VM_HOOK_RETURN +#endif + +// Hook for mp_sched_schedule when a function gets scheduled on sched_queue +// (this macro executes within an atomic section) +#ifndef MICROPY_SCHED_HOOK_SCHEDULED +#define MICROPY_SCHED_HOOK_SCHEDULED +#endif + +// Whether to include the garbage collector +#ifndef MICROPY_ENABLE_GC +#define MICROPY_ENABLE_GC (0) +#endif + +// Whether the garbage-collected heap can be split over multiple memory areas. +#ifndef MICROPY_GC_SPLIT_HEAP +#define MICROPY_GC_SPLIT_HEAP (0) +#endif + +// Whether regions should be added/removed from the split heap as needed. +#ifndef MICROPY_GC_SPLIT_HEAP_AUTO +#define MICROPY_GC_SPLIT_HEAP_AUTO (0) +#endif + +// Hook to run code during time consuming garbage collector operations +// *i* is the loop index variable (e.g. can be used to run every x loops) +#ifndef MICROPY_GC_HOOK_LOOP +#define MICROPY_GC_HOOK_LOOP(i) +#endif + +// Whether to provide m_tracked_calloc, m_tracked_free functions +#ifndef MICROPY_TRACKED_ALLOC +#define MICROPY_TRACKED_ALLOC (0) +#endif + +// Whether to enable finalisers in the garbage collector (ie call __del__) +#ifndef MICROPY_ENABLE_FINALISER +#define MICROPY_ENABLE_FINALISER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to enable a separate allocator for the Python stack. +// If enabled then the code must call mp_pystack_init before mp_init. +#ifndef MICROPY_ENABLE_PYSTACK +#define MICROPY_ENABLE_PYSTACK (0) +#endif + +// Number of bytes that memory returned by mp_pystack_alloc will be aligned by. +#ifndef MICROPY_PYSTACK_ALIGN +#define MICROPY_PYSTACK_ALIGN (8) +#endif + +// Whether to check C stack usage. C stack used for calling Python functions, +// etc. Not checking means segfault on overflow. +#ifndef MICROPY_STACK_CHECK +#define MICROPY_STACK_CHECK (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to have an emergency exception buffer +#ifndef MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) +#endif +#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF +#ifndef MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE +#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0) // 0 - implies dynamic allocation +#endif +#endif + +// Whether to provide the mp_kbd_exception object, and micropython.kbd_intr function +#ifndef MICROPY_KBD_EXCEPTION +#define MICROPY_KBD_EXCEPTION (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Prefer to raise KeyboardInterrupt asynchronously (from signal or interrupt +// handler) - if supported by a particular port. +#ifndef MICROPY_ASYNC_KBD_INTR +#define MICROPY_ASYNC_KBD_INTR (0) +#endif + +// Whether to include REPL helper function +#ifndef MICROPY_HELPER_REPL +#define MICROPY_HELPER_REPL (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Allow enabling debug prints after each REPL line +#ifndef MICROPY_REPL_INFO +#define MICROPY_REPL_INFO (0) +#endif + +// Whether to include emacs-style readline behavior in REPL +#ifndef MICROPY_REPL_EMACS_KEYS +#define MICROPY_REPL_EMACS_KEYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to include emacs-style word movement/kill readline behavior in REPL. +// This adds Alt+F, Alt+B, Alt+D and Alt+Backspace for forward-word, backward-word, forward-kill-word +// and backward-kill-word, respectively. +#ifndef MICROPY_REPL_EMACS_WORDS_MOVE +#define MICROPY_REPL_EMACS_WORDS_MOVE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to include extra convenience keys for word movement/kill in readline REPL. +// This adds Ctrl+Right, Ctrl+Left and Ctrl+W for forward-word, backward-word and backward-kill-word +// respectively. Ctrl+Delete is not implemented because it's a very different escape sequence. +// Depends on MICROPY_REPL_EMACS_WORDS_MOVE. +#ifndef MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE +#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to implement auto-indent in REPL +#ifndef MICROPY_REPL_AUTO_INDENT +#define MICROPY_REPL_AUTO_INDENT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether port requires event-driven REPL functions +#ifndef MICROPY_REPL_EVENT_DRIVEN +#define MICROPY_REPL_EVENT_DRIVEN (0) +#endif + +// The number of items to keep in the readline history. +#ifndef MICROPY_READLINE_HISTORY_SIZE +#define MICROPY_READLINE_HISTORY_SIZE (8) +#endif + +// Whether to include lexer helper function for unix +#ifndef MICROPY_HELPER_LEXER_UNIX +#define MICROPY_HELPER_LEXER_UNIX (0) +#endif + +// Long int implementation +#define MICROPY_LONGINT_IMPL_NONE (0) +#define MICROPY_LONGINT_IMPL_LONGLONG (1) +#define MICROPY_LONGINT_IMPL_MPZ (2) + +#ifndef MICROPY_LONGINT_IMPL +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE) +#endif + +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG +typedef long long mp_longint_impl_t; +#endif + +// Whether to include information in the byte code to determine source +// line number (increases RAM usage, but doesn't slow byte code execution) +#ifndef MICROPY_ENABLE_SOURCE_LINE +#define MICROPY_ENABLE_SOURCE_LINE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to include doc strings (increases RAM usage) +#ifndef MICROPY_ENABLE_DOC_STRING +#define MICROPY_ENABLE_DOC_STRING (0) +#endif + +// Exception messages are removed (requires disabling MICROPY_ROM_TEXT_COMPRESSION) +#define MICROPY_ERROR_REPORTING_NONE (0) +// Exception messages are short static strings +#define MICROPY_ERROR_REPORTING_TERSE (1) +// Exception messages provide basic error details +#define MICROPY_ERROR_REPORTING_NORMAL (2) +// Exception messages provide full info, e.g. object names +#define MICROPY_ERROR_REPORTING_DETAILED (3) + +#ifndef MICROPY_ERROR_REPORTING +#if MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_FULL_FEATURES +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) +#elif MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) +#else +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) +#endif +#endif + +// Whether issue warnings during compiling/execution +#ifndef MICROPY_WARNINGS +#define MICROPY_WARNINGS (0) +#endif + +// Whether to support warning categories +#ifndef MICROPY_WARNINGS_CATEGORY +#define MICROPY_WARNINGS_CATEGORY (0) +#endif + +// This macro is used when printing runtime warnings and errors +#ifndef MICROPY_ERROR_PRINTER +#define MICROPY_ERROR_PRINTER (&mp_plat_print) +#endif + +// Float and complex implementation +#define MICROPY_FLOAT_IMPL_NONE (0) +#define MICROPY_FLOAT_IMPL_FLOAT (1) +#define MICROPY_FLOAT_IMPL_DOUBLE (2) + +#ifndef MICROPY_FLOAT_IMPL +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) +#endif + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define MICROPY_PY_BUILTINS_FLOAT (1) +#define MICROPY_FLOAT_CONST(x) x##F +#define MICROPY_FLOAT_C_FUN(fun) fun##f +typedef float mp_float_t; +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define MICROPY_PY_BUILTINS_FLOAT (1) +#define MICROPY_FLOAT_CONST(x) x +#define MICROPY_FLOAT_C_FUN(fun) fun +typedef double mp_float_t; +#else +#define MICROPY_PY_BUILTINS_FLOAT (0) +#endif + +#ifndef MICROPY_PY_BUILTINS_COMPLEX +#define MICROPY_PY_BUILTINS_COMPLEX (MICROPY_PY_BUILTINS_FLOAT) +#endif + +// Whether to use the native _Float16 for 16-bit float support +#ifndef MICROPY_FLOAT_USE_NATIVE_FLT16 +#ifdef __FLT16_MAX__ +#define MICROPY_FLOAT_USE_NATIVE_FLT16 (1) +#else +#define MICROPY_FLOAT_USE_NATIVE_FLT16 (0) +#endif +#endif + +// Whether to provide a high-quality hash for float and complex numbers. +// Otherwise the default is a very simple but correct hashing function. +#ifndef MICROPY_FLOAT_HIGH_QUALITY_HASH +#define MICROPY_FLOAT_HIGH_QUALITY_HASH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Enable features which improve CPython compatibility +// but may lead to more code size/memory usage. +// TODO: Originally intended as generic category to not +// add bunch of once-off options. May need refactoring later +#ifndef MICROPY_CPYTHON_COMPAT +#define MICROPY_CPYTHON_COMPAT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Perform full checks as done by CPython. Disabling this +// may produce incorrect results, if incorrect data is fed, +// but should not lead to MicroPython crashes or similar +// grave issues (in other words, only user app should be, +// affected, not system). +#ifndef MICROPY_FULL_CHECKS +#define MICROPY_FULL_CHECKS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether POSIX-semantics non-blocking streams are supported +#ifndef MICROPY_STREAMS_NON_BLOCK +#define MICROPY_STREAMS_NON_BLOCK (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide stream functions with POSIX-like signatures +// (useful for porting existing libraries to MicroPython). +#ifndef MICROPY_STREAMS_POSIX_API +#define MICROPY_STREAMS_POSIX_API (0) +#endif + +// Whether modules can use MP_REGISTER_MODULE_DELEGATION() to delegate failed +// attribute lookups to a custom handler function. +#ifndef MICROPY_MODULE_ATTR_DELEGATION +#define MICROPY_MODULE_ATTR_DELEGATION (MICROPY_PY_SYS_ATTR_DELEGATION || MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to call __init__ when importing builtin modules for the first time. +// Modules using this need to handle the possibility that __init__ might be +// called multiple times. +#ifndef MICROPY_MODULE_BUILTIN_INIT +#define MICROPY_MODULE_BUILTIN_INIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to allow built-in modules to have sub-packages (by making the +// sub-package a member of their locals dict). Sub-packages should not be +// registered with MP_REGISTER_MODULE, instead they should be added as +// members of the parent's globals dict. To match CPython behavior, +// their __name__ should be "foo.bar"(i.e. QSTR_foo_dot_bar) which will +// require an entry in qstrdefs, although it does also work to just call +// it "bar". Also, because subpackages can be accessed without being +// imported (e.g. as foo.bar after `import foo`), they should not +// have __init__ methods. Instead, the top-level package's __init__ should +// initialise all sub-packages. +#ifndef MICROPY_MODULE_BUILTIN_SUBPACKAGES +#define MICROPY_MODULE_BUILTIN_SUBPACKAGES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to support module-level __getattr__ (see PEP 562) +#ifndef MICROPY_MODULE_GETATTR +#define MICROPY_MODULE_GETATTR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to enable importing foo.py with __name__ set to '__main__' +// Used by the unix port for the -m flag. +#ifndef MICROPY_MODULE_OVERRIDE_MAIN_IMPORT +#define MICROPY_MODULE_OVERRIDE_MAIN_IMPORT (0) +#endif + +// Whether frozen modules are supported in the form of strings +#ifndef MICROPY_MODULE_FROZEN_STR +#define MICROPY_MODULE_FROZEN_STR (0) +#endif + +// Whether frozen modules are supported in the form of .mpy files +#ifndef MICROPY_MODULE_FROZEN_MPY +#define MICROPY_MODULE_FROZEN_MPY (0) +#endif + +// Convenience macro for whether frozen modules are supported +#ifndef MICROPY_MODULE_FROZEN +#define MICROPY_MODULE_FROZEN (MICROPY_MODULE_FROZEN_STR || MICROPY_MODULE_FROZEN_MPY) +#endif + +// Whether you can override builtins in the builtins module +#ifndef MICROPY_CAN_OVERRIDE_BUILTINS +#define MICROPY_CAN_OVERRIDE_BUILTINS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to check that the "self" argument of a builtin method has the +// correct type. Such an explicit check is only needed if a builtin +// method escapes to Python land without a first argument, eg +// list.append([], 1). Without this check such calls will have undefined +// behaviour (usually segfault) if the first argument is the wrong type. +#ifndef MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to use internally defined errno's (otherwise system provided ones) +#ifndef MICROPY_USE_INTERNAL_ERRNO +#define MICROPY_USE_INTERNAL_ERRNO (0) +#endif + +// Whether to use internally defined *printf() functions (otherwise external ones) +#ifndef MICROPY_USE_INTERNAL_PRINTF +#define MICROPY_USE_INTERNAL_PRINTF (1) +#endif + +// The mp_print_t printer used for printf output when MICROPY_USE_INTERNAL_PRINTF is enabled +#ifndef MICROPY_INTERNAL_PRINTF_PRINTER +#define MICROPY_INTERNAL_PRINTF_PRINTER (&mp_plat_print) +#endif + +// Whether to support mp_sched_vm_abort to asynchronously abort to the top level. +#ifndef MICROPY_ENABLE_VM_ABORT +#define MICROPY_ENABLE_VM_ABORT (0) +#endif + +// Support for internal scheduler +#ifndef MICROPY_ENABLE_SCHEDULER +#define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether the scheduler supports scheduling static nodes with C callbacks +#ifndef MICROPY_SCHEDULER_STATIC_NODES +#define MICROPY_SCHEDULER_STATIC_NODES (0) +#endif + +// Maximum number of entries in the scheduler +#ifndef MICROPY_SCHEDULER_DEPTH +#define MICROPY_SCHEDULER_DEPTH (4) +#endif + +// Support for generic VFS sub-system +#ifndef MICROPY_VFS +#define MICROPY_VFS (0) +#endif + +// Support for VFS POSIX component, to mount a POSIX filesystem within VFS +#ifndef MICROPY_VFS_POSIX +#define MICROPY_VFS_POSIX (0) +#endif + +// Support for VFS FAT component, to mount a FAT filesystem within VFS +#ifndef MICROPY_VFS_FAT +#define MICROPY_VFS_FAT (0) +#endif + +// Support for VFS LittleFS v1 component, to mount a LFSv1 filesystem within VFS +#ifndef MICROPY_VFS_LFS1 +#define MICROPY_VFS_LFS1 (0) +#endif + +// Support for VFS LittleFS v2 component, to mount a LFSv2 filesystem within VFS +#ifndef MICROPY_VFS_LFS2 +#define MICROPY_VFS_LFS2 (0) +#endif + +/*****************************************************************************/ +/* Fine control over Python builtins, classes, modules, etc */ + +// Whether to support multiple inheritance of Python classes. Multiple +// inheritance makes some C functions inherently recursive, and adds a bit of +// code overhead. +#ifndef MICROPY_MULTIPLE_INHERITANCE +#define MICROPY_MULTIPLE_INHERITANCE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to implement attributes on functions +#ifndef MICROPY_PY_FUNCTION_ATTRS +#define MICROPY_PY_FUNCTION_ATTRS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support the descriptors __get__, __set__, __delete__ +// This costs some code size and makes load/store/delete of instance +// attributes slower for the classes that use this feature +#ifndef MICROPY_PY_DESCRIPTORS +#define MICROPY_PY_DESCRIPTORS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support class __delattr__ and __setattr__ methods +// This costs some code size and makes store/delete of instance +// attributes slower for the classes that use this feature +#ifndef MICROPY_PY_DELATTR_SETATTR +#define MICROPY_PY_DELATTR_SETATTR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Support for async/await/async for/async with +#ifndef MICROPY_PY_ASYNC_AWAIT +#define MICROPY_PY_ASYNC_AWAIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Support for literal string interpolation, f-strings (see PEP 498, Python 3.6+) +#ifndef MICROPY_PY_FSTRINGS +#define MICROPY_PY_FSTRINGS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Support for assignment expressions with := (see PEP 572, Python 3.8+) +#ifndef MICROPY_PY_ASSIGN_EXPR +#define MICROPY_PY_ASSIGN_EXPR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Non-standard .pend_throw() method for generators, allowing for +// Future-like behavior with respect to exception handling: an +// exception set with .pend_throw() will activate on the next call +// to generator's .send() or .__next__(). (This is useful to implement +// async schedulers.) +#ifndef MICROPY_PY_GENERATOR_PEND_THROW +#define MICROPY_PY_GENERATOR_PEND_THROW (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Issue a warning when comparing str and bytes objects +#ifndef MICROPY_PY_STR_BYTES_CMP_WARN +#define MICROPY_PY_STR_BYTES_CMP_WARN (0) +#endif + +// Add bytes.hex and bytes.fromhex +#ifndef MICROPY_PY_BUILTINS_BYTES_HEX +#define MICROPY_PY_BUILTINS_BYTES_HEX (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether str object is proper unicode +#ifndef MICROPY_PY_BUILTINS_STR_UNICODE +#define MICROPY_PY_BUILTINS_STR_UNICODE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to check for valid UTF-8 when converting bytes to str +#ifndef MICROPY_PY_BUILTINS_STR_UNICODE_CHECK +#define MICROPY_PY_BUILTINS_STR_UNICODE_CHECK (MICROPY_PY_BUILTINS_STR_UNICODE) +#endif + +// Whether str.center() method provided +#ifndef MICROPY_PY_BUILTINS_STR_CENTER +#define MICROPY_PY_BUILTINS_STR_CENTER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether str.count() method provided +#ifndef MICROPY_PY_BUILTINS_STR_COUNT +#define MICROPY_PY_BUILTINS_STR_COUNT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether str % (...) formatting operator provided +#ifndef MICROPY_PY_BUILTINS_STR_OP_MODULO +#define MICROPY_PY_BUILTINS_STR_OP_MODULO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether str.partition()/str.rpartition() method provided +#ifndef MICROPY_PY_BUILTINS_STR_PARTITION +#define MICROPY_PY_BUILTINS_STR_PARTITION (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether str.splitlines() method provided +#ifndef MICROPY_PY_BUILTINS_STR_SPLITLINES +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support bytearray object +#ifndef MICROPY_PY_BUILTINS_BYTEARRAY +#define MICROPY_PY_BUILTINS_BYTEARRAY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support dict.fromkeys() class method +#ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS +#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support memoryview object +#ifndef MICROPY_PY_BUILTINS_MEMORYVIEW +#define MICROPY_PY_BUILTINS_MEMORYVIEW (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support memoryview.itemsize attribute +#ifndef MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE +#define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to support set object +#ifndef MICROPY_PY_BUILTINS_SET +#define MICROPY_PY_BUILTINS_SET (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support slice subscript operators and slice object +#ifndef MICROPY_PY_BUILTINS_SLICE +#define MICROPY_PY_BUILTINS_SLICE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support slice attribute read access, +// i.e. slice.start, slice.stop, slice.step +#ifndef MICROPY_PY_BUILTINS_SLICE_ATTRS +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support the .indices(len) method on slice objects +#ifndef MICROPY_PY_BUILTINS_SLICE_INDICES +#define MICROPY_PY_BUILTINS_SLICE_INDICES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support frozenset object +#ifndef MICROPY_PY_BUILTINS_FROZENSET +#define MICROPY_PY_BUILTINS_FROZENSET (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support property object +#ifndef MICROPY_PY_BUILTINS_PROPERTY +#define MICROPY_PY_BUILTINS_PROPERTY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to implement the start/stop/step attributes (readback) on +// the "range" builtin type. Rarely used, and costs ~60 bytes (x86). +#ifndef MICROPY_PY_BUILTINS_RANGE_ATTRS +#define MICROPY_PY_BUILTINS_RANGE_ATTRS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support binary ops [only (in)equality is defined] between range +// objects. With this option disabled all range objects that are not exactly +// the same object will compare as not-equal. With it enabled the semantics +// match CPython and ranges are equal if they yield the same sequence of items. +#ifndef MICROPY_PY_BUILTINS_RANGE_BINOP +#define MICROPY_PY_BUILTINS_RANGE_BINOP (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Support for calling next() with second argument +#ifndef MICROPY_PY_BUILTINS_NEXT2 +#define MICROPY_PY_BUILTINS_NEXT2 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to support rounding of integers (incl bignum); eg round(123,-1)=120 +#ifndef MICROPY_PY_BUILTINS_ROUND_INT +#define MICROPY_PY_BUILTINS_ROUND_INT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support complete set of special methods for user +// classes, or only the most used ones. "Inplace" methods are +// controlled by MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS below. +// "Reverse" methods are controlled by +// MICROPY_PY_REVERSE_SPECIAL_METHODS below. +#ifndef MICROPY_PY_ALL_SPECIAL_METHODS +#define MICROPY_PY_ALL_SPECIAL_METHODS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support all inplace arithmetic operation methods +// (__imul__, etc.) +#ifndef MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS +#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to support reverse arithmetic operation methods +// (__radd__, etc.). Additionally gated by +// MICROPY_PY_ALL_SPECIAL_METHODS. +#ifndef MICROPY_PY_REVERSE_SPECIAL_METHODS +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support compile function +#ifndef MICROPY_PY_BUILTINS_COMPILE +#define MICROPY_PY_BUILTINS_COMPILE (MICROPY_ENABLE_COMPILER && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support enumerate function(type) +#ifndef MICROPY_PY_BUILTINS_ENUMERATE +#define MICROPY_PY_BUILTINS_ENUMERATE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support eval and exec functions +// By default they are supported if the compiler is enabled +#ifndef MICROPY_PY_BUILTINS_EVAL_EXEC +#define MICROPY_PY_BUILTINS_EVAL_EXEC (MICROPY_ENABLE_COMPILER) +#endif + +// Whether to support the Python 2 execfile function +#ifndef MICROPY_PY_BUILTINS_EXECFILE +#define MICROPY_PY_BUILTINS_EXECFILE (MICROPY_ENABLE_COMPILER && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support filter function(type) +#ifndef MICROPY_PY_BUILTINS_FILTER +#define MICROPY_PY_BUILTINS_FILTER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support reversed function(type) +#ifndef MICROPY_PY_BUILTINS_REVERSED +#define MICROPY_PY_BUILTINS_REVERSED (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to define "NotImplemented" special constant +#ifndef MICROPY_PY_BUILTINS_NOTIMPLEMENTED +#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide the built-in input() function. The implementation of this +// uses shared/readline, so can only be enabled if the port uses this readline. +#ifndef MICROPY_PY_BUILTINS_INPUT +#define MICROPY_PY_BUILTINS_INPUT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support min/max functions +#ifndef MICROPY_PY_BUILTINS_MIN_MAX +#define MICROPY_PY_BUILTINS_MIN_MAX (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Support for calls to pow() with 3 integer arguments +#ifndef MICROPY_PY_BUILTINS_POW3 +#define MICROPY_PY_BUILTINS_POW3 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide the help function +#ifndef MICROPY_PY_BUILTINS_HELP +#define MICROPY_PY_BUILTINS_HELP (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Use this to configure the help text shown for help(). It should be a +// variable with the type "const char*". A sensible default is provided. +#ifndef MICROPY_PY_BUILTINS_HELP_TEXT +#define MICROPY_PY_BUILTINS_HELP_TEXT mp_help_default_text +#endif + +// Add the ability to list the available modules when executing help('modules') +#ifndef MICROPY_PY_BUILTINS_HELP_MODULES +#define MICROPY_PY_BUILTINS_HELP_MODULES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to set __file__ for imported modules +#ifndef MICROPY_PY___FILE__ +#define MICROPY_PY___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to provide mem-info related functions in micropython module +#ifndef MICROPY_PY_MICROPYTHON_MEM_INFO +#define MICROPY_PY_MICROPYTHON_MEM_INFO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide "micropython.stack_use" function +#ifndef MICROPY_PY_MICROPYTHON_STACK_USE +#define MICROPY_PY_MICROPYTHON_STACK_USE (MICROPY_PY_MICROPYTHON_MEM_INFO) +#endif + +// Whether to provide the "micropython.heap_locked" function +#ifndef MICROPY_PY_MICROPYTHON_HEAP_LOCKED +#define MICROPY_PY_MICROPYTHON_HEAP_LOCKED (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to provide "array" module. Note that large chunk of the +// underlying code is shared with "bytearray" builtin type, so to +// get real savings, it should be disabled too. +#ifndef MICROPY_PY_ARRAY +#define MICROPY_PY_ARRAY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to support slice assignments for array (and bytearray). +// This is rarely used, but adds ~0.5K of code. +#ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support attrtuple type (MicroPython extension) +// It provides space-efficient tuples with attribute access +#ifndef MICROPY_PY_ATTRTUPLE +#define MICROPY_PY_ATTRTUPLE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to provide "collections" module +#ifndef MICROPY_PY_COLLECTIONS +#define MICROPY_PY_COLLECTIONS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to provide "collections.deque" type +#ifndef MICROPY_PY_COLLECTIONS_DEQUE +#define MICROPY_PY_COLLECTIONS_DEQUE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether "collections.deque" supports iteration +#ifndef MICROPY_PY_COLLECTIONS_DEQUE_ITER +#define MICROPY_PY_COLLECTIONS_DEQUE_ITER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether "collections.deque" supports subscription +#ifndef MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR +#define MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide "collections.OrderedDict" type +#ifndef MICROPY_PY_COLLECTIONS_ORDEREDDICT +#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide the _asdict function for namedtuple +#ifndef MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT +#define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to provide "math" module +#ifndef MICROPY_PY_MATH +#define MICROPY_PY_MATH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to provide all math module constants (Python 3.5+), or just pi and e. +#ifndef MICROPY_PY_MATH_CONSTANTS +#define MICROPY_PY_MATH_CONSTANTS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide special math functions: math.{erf,erfc,gamma,lgamma} +#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS +#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide math.factorial function +#ifndef MICROPY_PY_MATH_FACTORIAL +#define MICROPY_PY_MATH_FACTORIAL (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide math.isclose function +#ifndef MICROPY_PY_MATH_ISCLOSE +#define MICROPY_PY_MATH_ISCLOSE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide fix for atan2 Inf handling. +#ifndef MICROPY_PY_MATH_ATAN2_FIX_INFNAN +#define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (0) +#endif + +// Whether to provide fix for fmod Inf handling. +#ifndef MICROPY_PY_MATH_FMOD_FIX_INFNAN +#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (0) +#endif + +// Whether to provide fix for modf negative zero handling. +#ifndef MICROPY_PY_MATH_MODF_FIX_NEGZERO +#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (0) +#endif + +// Whether to provide fix for pow(1, NaN) and pow(NaN, 0), which both should be 1 not NaN. +#ifndef MICROPY_PY_MATH_POW_FIX_NAN +#define MICROPY_PY_MATH_POW_FIX_NAN (0) +#endif + +// Whether to provide "cmath" module +#ifndef MICROPY_PY_CMATH +#define MICROPY_PY_CMATH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide "micropython" module +#ifndef MICROPY_PY_MICROPYTHON +#define MICROPY_PY_MICROPYTHON (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to provide "gc" module +#ifndef MICROPY_PY_GC +#define MICROPY_PY_GC (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to return number of collected objects from gc.collect() +#ifndef MICROPY_PY_GC_COLLECT_RETVAL +#define MICROPY_PY_GC_COLLECT_RETVAL (0) +#endif + +// Whether to provide "io" module +#ifndef MICROPY_PY_IO +#define MICROPY_PY_IO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to provide "io.IOBase" class to support user streams +#ifndef MICROPY_PY_IO_IOBASE +#define MICROPY_PY_IO_IOBASE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide "io.BytesIO" class +#ifndef MICROPY_PY_IO_BYTESIO +#define MICROPY_PY_IO_BYTESIO (1) +#endif + +// Whether to provide "io.BufferedWriter" class +#ifndef MICROPY_PY_IO_BUFFEREDWRITER +#define MICROPY_PY_IO_BUFFEREDWRITER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to provide "struct" module +#ifndef MICROPY_PY_STRUCT +#define MICROPY_PY_STRUCT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to provide "sys" module +#ifndef MICROPY_PY_SYS +#define MICROPY_PY_SYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + +// Whether to initialise "sys.path" and "sys.argv" to their defaults in mp_init() +#ifndef MICROPY_PY_SYS_PATH_ARGV_DEFAULTS +#define MICROPY_PY_SYS_PATH_ARGV_DEFAULTS (MICROPY_PY_SYS) +#endif + +// Whether to provide "sys.maxsize" constant +#ifndef MICROPY_PY_SYS_MAXSIZE +#define MICROPY_PY_SYS_MAXSIZE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide "sys.modules" dictionary +#ifndef MICROPY_PY_SYS_MODULES +#define MICROPY_PY_SYS_MODULES (1) +#endif + +// Whether to provide "sys.exc_info" function +// Avoid enabling this, this function is Python2 heritage +#ifndef MICROPY_PY_SYS_EXC_INFO +#define MICROPY_PY_SYS_EXC_INFO (0) +#endif + +// Whether to provide "sys.executable", which is the absolute path to the +// micropython binary +// Intended for use on the "OS" ports (e.g. Unix) +#ifndef MICROPY_PY_SYS_EXECUTABLE +#define MICROPY_PY_SYS_EXECUTABLE (0) +#endif + +// Whether to provide "sys.intern" +#ifndef MICROPY_PY_SYS_INTERN +#define MICROPY_PY_SYS_INTERN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to provide "sys.exit" function +#ifndef MICROPY_PY_SYS_EXIT +#define MICROPY_PY_SYS_EXIT (1) +#endif + +// Whether to provide "sys.atexit" function (MicroPython extension) +#ifndef MICROPY_PY_SYS_ATEXIT +#define MICROPY_PY_SYS_ATEXIT (0) +#endif + +// Whether to provide the "sys.path" attribute (which forces module delegation +// and mutable sys attributes to be enabled). +// If MICROPY_PY_SYS_PATH_ARGV_DEFAULTS is enabled, this is initialised in +// mp_init to an empty list. Otherwise the port must initialise it using +// `mp_sys_path = mp_obj_new_list(...)`. +#ifndef MICROPY_PY_SYS_PATH +#define MICROPY_PY_SYS_PATH (1) +#endif + +// Whether to provide the "sys.argv" attribute. +// If MICROPY_PY_SYS_PATH_ARGV_DEFAULTS is enabled, this is initialised in +// mp_init to an empty list. Otherwise the port must initialise it using +// `mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), ...);` +#ifndef MICROPY_PY_SYS_ARGV +#define MICROPY_PY_SYS_ARGV (1) +#endif + +// Whether to provide sys.{ps1,ps2} mutable attributes, to control REPL prompts +#ifndef MICROPY_PY_SYS_PS1_PS2 +#define MICROPY_PY_SYS_PS1_PS2 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide "sys.settrace" function +#ifndef MICROPY_PY_SYS_SETTRACE +#define MICROPY_PY_SYS_SETTRACE (0) +#endif + +// Whether to provide "sys.getsizeof" function +#ifndef MICROPY_PY_SYS_GETSIZEOF +#define MICROPY_PY_SYS_GETSIZEOF (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether to provide sys.{stdin,stdout,stderr} objects +#ifndef MICROPY_PY_SYS_STDFILES +#define MICROPY_PY_SYS_STDFILES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide sys.{stdin,stdout,stderr}.buffer object +// This is implemented per-port +#ifndef MICROPY_PY_SYS_STDIO_BUFFER +#define MICROPY_PY_SYS_STDIO_BUFFER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide sys.tracebacklimit mutable attribute +#ifndef MICROPY_PY_SYS_TRACEBACKLIMIT +#define MICROPY_PY_SYS_TRACEBACKLIMIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +// Whether the sys module supports attribute delegation +// This is enabled automatically when needed by other features +#ifndef MICROPY_PY_SYS_ATTR_DELEGATION +#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_PATH || MICROPY_PY_SYS_PS1_PS2 || MICROPY_PY_SYS_TRACEBACKLIMIT) +#endif + +// Whether to provide "errno" module +#ifndef MICROPY_PY_ERRNO +#define MICROPY_PY_ERRNO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide the errno.errorcode dict +#ifndef MICROPY_PY_ERRNO_ERRORCODE +#define MICROPY_PY_ERRNO_ERRORCODE (1) +#endif + +// Whether to provide "select" module +#ifndef MICROPY_PY_SELECT +#define MICROPY_PY_SELECT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to enable POSIX optimisations in the "select" module (requires system poll) +#ifndef MICROPY_PY_SELECT_POSIX_OPTIMISATIONS +#define MICROPY_PY_SELECT_POSIX_OPTIMISATIONS (0) +#endif + +// Whether to enable the select() function in the "select" module (baremetal +// implementation). This is present for compatibility but can be disabled to +// save space. +#ifndef MICROPY_PY_SELECT_SELECT +#define MICROPY_PY_SELECT_SELECT (1) +#endif + +// Whether to provide the "time" module +#ifndef MICROPY_PY_TIME +#define MICROPY_PY_TIME (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES) +#endif + +// Whether to provide time.gmtime/localtime/mktime functions +#ifndef MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME +#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (0) +#endif + +// Whether to provide time.time/time_ns functions +#ifndef MICROPY_PY_TIME_TIME_TIME_NS +#define MICROPY_PY_TIME_TIME_TIME_NS (0) +#endif + +// Period of values returned by time.ticks_ms(), ticks_us(), ticks_cpu() +// functions. Should be power of two. All functions above use the same +// period, so if underlying hardware/API has different periods, the +// minimum of them should be used. The value below is the maximum value +// this parameter can take (corresponding to 30 bit tick values on 32-bit +// system). +#ifndef MICROPY_PY_TIME_TICKS_PERIOD +#define MICROPY_PY_TIME_TICKS_PERIOD (MP_SMALL_INT_POSITIVE_MASK + 1) +#endif + +// Whether to provide "_thread" module +#ifndef MICROPY_PY_THREAD +#define MICROPY_PY_THREAD (0) +#endif + +// Whether to make the VM/runtime thread-safe using a global lock +// If not enabled then thread safety must be provided at the Python level +#ifndef MICROPY_PY_THREAD_GIL +#define MICROPY_PY_THREAD_GIL (MICROPY_PY_THREAD) +#endif + +// Number of VM jump-loops to do before releasing the GIL. +// Set this to 0 to disable the divisor. +#ifndef MICROPY_PY_THREAD_GIL_VM_DIVISOR +#define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32) +#endif + +// Extended modules + +#ifndef MICROPY_PY_ASYNCIO +#define MICROPY_PY_ASYNCIO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_UCTYPES +#define MICROPY_PY_UCTYPES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide SHORT, INT, LONG, etc. types in addition to +// exact-bitness types like INT16, INT32, etc. +#ifndef MICROPY_PY_UCTYPES_NATIVE_C_TYPES +#define MICROPY_PY_UCTYPES_NATIVE_C_TYPES (1) +#endif + +// Whether to provide "deflate" module (decompression-only by default) +#ifndef MICROPY_PY_DEFLATE +#define MICROPY_PY_DEFLATE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to provide compression support in "deflate" module +#ifndef MICROPY_PY_DEFLATE_COMPRESS +#define MICROPY_PY_DEFLATE_COMPRESS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_FULL_FEATURES) +#endif + +#ifndef MICROPY_PY_JSON +#define MICROPY_PY_JSON (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to support the "separators" argument to dump, dumps +#ifndef MICROPY_PY_JSON_SEPARATORS +#define MICROPY_PY_JSON_SEPARATORS (1) +#endif + +#ifndef MICROPY_PY_OS +#define MICROPY_PY_OS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_OS_STATVFS +#define MICROPY_PY_OS_STATVFS (MICROPY_PY_OS) +#endif + +#ifndef MICROPY_PY_RE +#define MICROPY_PY_RE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_RE_DEBUG +#define MICROPY_PY_RE_DEBUG (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +#ifndef MICROPY_PY_RE_MATCH_GROUPS +#define MICROPY_PY_RE_MATCH_GROUPS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +#ifndef MICROPY_PY_RE_MATCH_SPAN_START_END +#define MICROPY_PY_RE_MATCH_SPAN_START_END (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + +#ifndef MICROPY_PY_RE_SUB +#define MICROPY_PY_RE_SUB (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_HEAPQ +#define MICROPY_PY_HEAPQ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_HASHLIB +#define MICROPY_PY_HASHLIB (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_HASHLIB_MD5 +#define MICROPY_PY_HASHLIB_MD5 (0) +#endif + +#ifndef MICROPY_PY_HASHLIB_SHA1 +#define MICROPY_PY_HASHLIB_SHA1 (0) +#endif + +#ifndef MICROPY_PY_HASHLIB_SHA256 +#define MICROPY_PY_HASHLIB_SHA256 (1) +#endif + +#ifndef MICROPY_PY_CRYPTOLIB +#define MICROPY_PY_CRYPTOLIB (0) +#endif + +// Depends on MICROPY_PY_CRYPTOLIB +#ifndef MICROPY_PY_CRYPTOLIB_CTR +#define MICROPY_PY_CRYPTOLIB_CTR (0) +#endif + +#ifndef MICROPY_PY_CRYPTOLIB_CONSTS +#define MICROPY_PY_CRYPTOLIB_CONSTS (0) +#endif + +#ifndef MICROPY_PY_BINASCII +#define MICROPY_PY_BINASCII (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Depends on MICROPY_PY_DEFLATE +#ifndef MICROPY_PY_BINASCII_CRC32 +#define MICROPY_PY_BINASCII_CRC32 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_RANDOM +#define MICROPY_PY_RANDOM (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +// Whether to include: randrange, randint, choice, random, uniform +#ifndef MICROPY_PY_RANDOM_EXTRA_FUNCS +#define MICROPY_PY_RANDOM_EXTRA_FUNCS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_MACHINE +#define MICROPY_PY_MACHINE (0) +#endif + +// Whether to include: reset, reset_cause +#ifndef MICROPY_PY_MACHINE_RESET +#define MICROPY_PY_MACHINE_RESET (0) +#endif + +// Whether to include: bitstream +#ifndef MICROPY_PY_MACHINE_BITSTREAM +#define MICROPY_PY_MACHINE_BITSTREAM (0) +#endif + +// Whether to include: time_pulse_us +#ifndef MICROPY_PY_MACHINE_PULSE +#define MICROPY_PY_MACHINE_PULSE (0) +#endif + +// Whether to provide the "machine.mem8/16/32" objects +#ifndef MICROPY_PY_MACHINE_MEMX +#define MICROPY_PY_MACHINE_MEMX (MICROPY_PY_MACHINE) +#endif + +// Whether to provide the "machine.Signal" class +#ifndef MICROPY_PY_MACHINE_SIGNAL +#define MICROPY_PY_MACHINE_SIGNAL (MICROPY_PY_MACHINE) +#endif + +#ifndef MICROPY_PY_MACHINE_I2C +#define MICROPY_PY_MACHINE_I2C (0) +#endif + +// Whether the low-level I2C transfer function supports a separate write as the first transfer +#ifndef MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 +#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (0) +#endif + +// Whether to provide the "machine.SoftI2C" class +#ifndef MICROPY_PY_MACHINE_SOFTI2C +#define MICROPY_PY_MACHINE_SOFTI2C (0) +#endif + +#ifndef MICROPY_PY_MACHINE_SPI +#define MICROPY_PY_MACHINE_SPI (0) +#endif + +// Whether to provide the "machine.SoftSPI" class +#ifndef MICROPY_PY_MACHINE_SOFTSPI +#define MICROPY_PY_MACHINE_SOFTSPI (0) +#endif + +// Whether to provide the "machine.Timer" class +#ifndef MICROPY_PY_MACHINE_TIMER +#define MICROPY_PY_MACHINE_TIMER (0) +#endif + +// The default backlog value for socket.listen(backlog) +#ifndef MICROPY_PY_SOCKET_LISTEN_BACKLOG_DEFAULT +#define MICROPY_PY_SOCKET_LISTEN_BACKLOG_DEFAULT (2) +#endif + +#ifndef MICROPY_PY_SSL +#define MICROPY_PY_SSL (0) +#endif + +// Whether to add finaliser code to ssl objects +#ifndef MICROPY_PY_SSL_FINALISER +#define MICROPY_PY_SSL_FINALISER (MICROPY_ENABLE_FINALISER) +#endif + +// Whether to provide the "vfs" module +#ifndef MICROPY_PY_VFS +#define MICROPY_PY_VFS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES && MICROPY_VFS) +#endif + +#ifndef MICROPY_PY_WEBSOCKET +#define MICROPY_PY_WEBSOCKET (0) +#endif + +#ifndef MICROPY_PY_FRAMEBUF +#define MICROPY_PY_FRAMEBUF (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +#ifndef MICROPY_PY_BTREE +#define MICROPY_PY_BTREE (0) +#endif + +// Whether to provide the low-level "_onewire" module +#ifndef MICROPY_PY_ONEWIRE +#define MICROPY_PY_ONEWIRE (0) +#endif + +// Whether to provide the "platform" module +#ifndef MICROPY_PY_PLATFORM +#define MICROPY_PY_PLATFORM (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + +/*****************************************************************************/ +/* Hooks for a port to add builtins */ + +// Additional builtin function definitions - see modbuiltins.c:mp_module_builtins_globals_table for format. +#ifndef MICROPY_PORT_BUILTINS +#define MICROPY_PORT_BUILTINS +#endif + +// Additional builtin function definitions for extension by command-line, boards or variants. +// See modbuiltins.c:mp_module_builtins_globals_table for format. +#ifndef MICROPY_PORT_EXTRA_BUILTINS +#define MICROPY_PORT_EXTRA_BUILTINS +#endif + +// Additional constant definitions for the compiler - see compile.c:mp_constants_table. +#ifndef MICROPY_PORT_CONSTANTS +#define MICROPY_PORT_CONSTANTS +#endif + +/*****************************************************************************/ +/* Hooks for a port to wrap functions with attributes */ + +#ifndef MICROPY_WRAP_MP_BINARY_OP +#define MICROPY_WRAP_MP_BINARY_OP(f) f +#endif + +#ifndef MICROPY_WRAP_MP_EXECUTE_BYTECODE +#define MICROPY_WRAP_MP_EXECUTE_BYTECODE(f) f +#endif + +#ifndef MICROPY_WRAP_MP_LOAD_GLOBAL +#define MICROPY_WRAP_MP_LOAD_GLOBAL(f) f +#endif + +#ifndef MICROPY_WRAP_MP_LOAD_NAME +#define MICROPY_WRAP_MP_LOAD_NAME(f) f +#endif + +#ifndef MICROPY_WRAP_MP_MAP_LOOKUP +#define MICROPY_WRAP_MP_MAP_LOOKUP(f) f +#endif + +#ifndef MICROPY_WRAP_MP_OBJ_GET_TYPE +#define MICROPY_WRAP_MP_OBJ_GET_TYPE(f) f +#endif + +#ifndef MICROPY_WRAP_MP_SCHED_EXCEPTION +#define MICROPY_WRAP_MP_SCHED_EXCEPTION(f) f +#endif + +#ifndef MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT +#define MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(f) f +#endif + +#ifndef MICROPY_WRAP_MP_SCHED_SCHEDULE +#define MICROPY_WRAP_MP_SCHED_SCHEDULE(f) f +#endif + +#ifndef MICROPY_WRAP_MP_SCHED_VM_ABORT +#define MICROPY_WRAP_MP_SCHED_VM_ABORT(f) f +#endif + +/*****************************************************************************/ +/* Miscellaneous settings */ + +// All uPy objects in ROM must be aligned on at least a 4 byte boundary +// so that the small-int/qstr/pointer distinction can be made. For machines +// that don't do this (eg 16-bit CPU), define the following macro to something +// like __attribute__((aligned(4))). +#ifndef MICROPY_OBJ_BASE_ALIGNMENT +#define MICROPY_OBJ_BASE_ALIGNMENT +#endif + +// String used for the banner, and sys.version additional information +#ifndef MICROPY_BANNER_NAME_AND_VERSION +#if MICROPY_PREVIEW_VERSION_2 +#define MICROPY_BANNER_NAME_AND_VERSION "MicroPython (with v2.0 preview) " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE +#else +#define MICROPY_BANNER_NAME_AND_VERSION "MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE +#endif +#endif + +// String used for the second part of the banner, and sys.implementation._machine +#ifndef MICROPY_BANNER_MACHINE +#ifdef MICROPY_HW_BOARD_NAME +#define MICROPY_BANNER_MACHINE MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME +#else +#define MICROPY_BANNER_MACHINE MICROPY_PY_SYS_PLATFORM " [" MICROPY_PLATFORM_COMPILER "] version" +#endif +#endif + +// Number of bytes in an object word: mp_obj_t, mp_uint_t, mp_uint_t +#ifndef MP_BYTES_PER_OBJ_WORD +#define MP_BYTES_PER_OBJ_WORD (sizeof(mp_uint_t)) +#endif + +// Number of bits in a byte +#ifndef MP_BITS_PER_BYTE +#define MP_BITS_PER_BYTE (8) +#endif +// mp_int_t value with most significant bit set +#define MP_OBJ_WORD_MSBIT_HIGH (((mp_uint_t)1) << (MP_BYTES_PER_OBJ_WORD * MP_BITS_PER_BYTE - 1)) + +// Make sure both MP_ENDIANNESS_LITTLE and MP_ENDIANNESS_BIG are +// defined and that they are the opposite of each other. +#if defined(MP_ENDIANNESS_LITTLE) +#define MP_ENDIANNESS_BIG (!MP_ENDIANNESS_LITTLE) +#elif defined(MP_ENDIANNESS_BIG) +#define MP_ENDIANNESS_LITTLE (!MP_ENDIANNESS_BIG) +#else +// Endianness not defined by port so try to autodetect it. + #if defined(__BYTE_ORDER__) + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define MP_ENDIANNESS_LITTLE (1) + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define MP_ENDIANNESS_LITTLE (0) + #endif + #else + #include + #if defined(__BYTE_ORDER) + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define MP_ENDIANNESS_LITTLE (1) + #elif __BYTE_ORDER == __BIG_ENDIAN + #define MP_ENDIANNESS_LITTLE (0) + #endif + #endif + #endif + #ifndef MP_ENDIANNESS_LITTLE + #error endianness not defined and cannot detect it + #endif + #define MP_ENDIANNESS_BIG (!MP_ENDIANNESS_LITTLE) +#endif + +// Make a pointer to RAM callable (eg set lower bit for Thumb code) +// (This scheme won't work if we want to mix Thumb and normal ARM code.) +#ifndef MICROPY_MAKE_POINTER_CALLABLE +#define MICROPY_MAKE_POINTER_CALLABLE(p) (p) +#endif + +// If these MP_PLAT_*_EXEC macros are overridden then the memory allocated by them +// must be somehow reachable for marking by the GC, since the native code +// generators store pointers to GC managed memory in the code. +#ifndef MP_PLAT_ALLOC_EXEC +#define MP_PLAT_ALLOC_EXEC(min_size, ptr, size) do { *ptr = m_new(byte, min_size); *size = min_size; } while (0) +#endif + +#ifndef MP_PLAT_FREE_EXEC +#define MP_PLAT_FREE_EXEC(ptr, size) m_del(byte, ptr, size) +#endif + +// Allocating new heap area at runtime requires port to be able to allocate from system heap +#if MICROPY_GC_SPLIT_HEAP_AUTO +#ifndef MP_PLAT_ALLOC_HEAP +#define MP_PLAT_ALLOC_HEAP(size) malloc(size) +#endif +#ifndef MP_PLAT_FREE_HEAP +#define MP_PLAT_FREE_HEAP(ptr) free(ptr) +#endif +#endif + +// This macro is used to do all output (except when MICROPY_PY_IO is defined) +#ifndef MP_PLAT_PRINT_STRN +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) +#endif + +#ifndef MP_SSIZE_MAX +#define MP_SSIZE_MAX SSIZE_MAX +#endif + +// printf format spec to use for mp_int_t and friends +#ifndef INT_FMT +#if defined(__LP64__) +// Archs where mp_int_t == long, long != int +#define UINT_FMT "%lu" +#define INT_FMT "%ld" +#elif defined(_WIN64) +#define UINT_FMT "%llu" +#define INT_FMT "%lld" +#else +// Archs where mp_int_t == int +#define UINT_FMT "%u" +#define INT_FMT "%d" +#endif +#endif // INT_FMT + +// Modifier for function which doesn't return +#ifndef NORETURN +#define NORETURN __attribute__((noreturn)) +#endif + +// Modifier for weak functions +#ifndef MP_WEAK +#define MP_WEAK __attribute__((weak)) +#endif + +// Modifier for functions which should be never inlined +#ifndef MP_NOINLINE +#define MP_NOINLINE __attribute__((noinline)) +#endif + +// Modifier for functions which should be always inlined +#ifndef MP_ALWAYSINLINE +#define MP_ALWAYSINLINE __attribute__((always_inline)) +#endif + +// Condition is likely to be true, to help branch prediction +#ifndef MP_LIKELY +#define MP_LIKELY(x) __builtin_expect((x), 1) +#endif + +// Condition is likely to be false, to help branch prediction +#ifndef MP_UNLIKELY +#define MP_UNLIKELY(x) __builtin_expect((x), 0) +#endif + +// To annotate that code is unreachable +#ifndef MP_UNREACHABLE +#if defined(__GNUC__) +#define MP_UNREACHABLE __builtin_unreachable(); +#else +#define MP_UNREACHABLE for (;;); +#endif +#endif + +// Explicitly annotate switch case fall throughs +#if defined(__GNUC__) && __GNUC__ >= 7 +#define MP_FALLTHROUGH __attribute__((fallthrough)); +#else +#define MP_FALLTHROUGH +#endif + +#ifndef MP_HTOBE16 +#if MP_ENDIANNESS_LITTLE +#define MP_HTOBE16(x) ((uint16_t)((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))) +#define MP_BE16TOH(x) MP_HTOBE16(x) +#else +#define MP_HTOBE16(x) (x) +#define MP_BE16TOH(x) (x) +#endif +#endif + +#ifndef MP_HTOBE32 +#if MP_ENDIANNESS_LITTLE +#define MP_HTOBE32(x) ((uint32_t)((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) >> 8) & 0xff00) | (((x) >> 24) & 0xff))) +#define MP_BE32TOH(x) MP_HTOBE32(x) +#else +#define MP_HTOBE32(x) (x) +#define MP_BE32TOH(x) (x) +#endif +#endif + +// Warning categories are by default implemented as strings, though +// hook is left for a port to define them as something else. +#if MICROPY_WARNINGS_CATEGORY +#ifndef MP_WARN_CAT +#define MP_WARN_CAT(x) #x +#endif +#else +#undef MP_WARN_CAT +#define MP_WARN_CAT(x) (NULL) +#endif + +#endif // MICROPY_INCLUDED_PY_MPCONFIG_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mperrno.h b/non_catalog_apps/mp_flipper/lib/micropython/py/mperrno.h new file mode 100644 index 00000000..be2a65a0 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mperrno.h @@ -0,0 +1,152 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MPERRNO_H +#define MICROPY_INCLUDED_PY_MPERRNO_H + +#include "py/mpconfig.h" + +#if MICROPY_USE_INTERNAL_ERRNO + +// MP_Exxx errno's are defined directly as numeric values +// (Linux constants are used as a reference) + +#define MP_EPERM (1) // Operation not permitted +#define MP_ENOENT (2) // No such file or directory +#define MP_ESRCH (3) // No such process +#define MP_EINTR (4) // Interrupted system call +#define MP_EIO (5) // I/O error +#define MP_ENXIO (6) // No such device or address +#define MP_E2BIG (7) // Argument list too long +#define MP_ENOEXEC (8) // Exec format error +#define MP_EBADF (9) // Bad file number +#define MP_ECHILD (10) // No child processes +#define MP_EAGAIN (11) // Try again +#define MP_ENOMEM (12) // Out of memory +#define MP_EACCES (13) // Permission denied +#define MP_EFAULT (14) // Bad address +#define MP_ENOTBLK (15) // Block device required +#define MP_EBUSY (16) // Device or resource busy +#define MP_EEXIST (17) // File exists +#define MP_EXDEV (18) // Cross-device link +#define MP_ENODEV (19) // No such device +#define MP_ENOTDIR (20) // Not a directory +#define MP_EISDIR (21) // Is a directory +#define MP_EINVAL (22) // Invalid argument +#define MP_ENFILE (23) // File table overflow +#define MP_EMFILE (24) // Too many open files +#define MP_ENOTTY (25) // Not a typewriter +#define MP_ETXTBSY (26) // Text file busy +#define MP_EFBIG (27) // File too large +#define MP_ENOSPC (28) // No space left on device +#define MP_ESPIPE (29) // Illegal seek +#define MP_EROFS (30) // Read-only file system +#define MP_EMLINK (31) // Too many links +#define MP_EPIPE (32) // Broken pipe +#define MP_EDOM (33) // Math argument out of domain of func +#define MP_ERANGE (34) // Math result not representable +#define MP_EWOULDBLOCK MP_EAGAIN // Operation would block +#define MP_EOPNOTSUPP (95) // Operation not supported on transport endpoint +#define MP_EAFNOSUPPORT (97) // Address family not supported by protocol +#define MP_EADDRINUSE (98) // Address already in use +#define MP_ECONNABORTED (103) // Software caused connection abort +#define MP_ECONNRESET (104) // Connection reset by peer +#define MP_ENOBUFS (105) // No buffer space available +#define MP_EISCONN (106) // Transport endpoint is already connected +#define MP_ENOTCONN (107) // Transport endpoint is not connected +#define MP_ETIMEDOUT (110) // Connection timed out +#define MP_ECONNREFUSED (111) // Connection refused +#define MP_EHOSTUNREACH (113) // No route to host +#define MP_EALREADY (114) // Operation already in progress +#define MP_EINPROGRESS (115) // Operation now in progress +#define MP_ECANCELED (125) // Operation canceled + +#else + +// MP_Exxx errno's are defined in terms of system supplied ones + +#include + +#define MP_EPERM EPERM +#define MP_ENOENT ENOENT +#define MP_ESRCH ESRCH +#define MP_EINTR EINTR +#define MP_EIO EIO +#define MP_ENXIO ENXIO +#define MP_E2BIG E2BIG +#define MP_ENOEXEC ENOEXEC +#define MP_EBADF EBADF +#define MP_ECHILD ECHILD +#define MP_EAGAIN EAGAIN +#define MP_ENOMEM ENOMEM +#define MP_EACCES EACCES +#define MP_EFAULT EFAULT +#define MP_ENOTBLK ENOTBLK +#define MP_EBUSY EBUSY +#define MP_EEXIST EEXIST +#define MP_EXDEV EXDEV +#define MP_ENODEV ENODEV +#define MP_ENOTDIR ENOTDIR +#define MP_EISDIR EISDIR +#define MP_EINVAL EINVAL +#define MP_ENFILE ENFILE +#define MP_EMFILE EMFILE +#define MP_ENOTTY ENOTTY +#define MP_ETXTBSY ETXTBSY +#define MP_EFBIG EFBIG +#define MP_ENOSPC ENOSPC +#define MP_ESPIPE ESPIPE +#define MP_EROFS EROFS +#define MP_EMLINK EMLINK +#define MP_EPIPE EPIPE +#define MP_EDOM EDOM +#define MP_ERANGE ERANGE +#define MP_EWOULDBLOCK EWOULDBLOCK +#define MP_EOPNOTSUPP EOPNOTSUPP +#define MP_EAFNOSUPPORT EAFNOSUPPORT +#define MP_EADDRINUSE EADDRINUSE +#define MP_ECONNABORTED ECONNABORTED +#define MP_ECONNRESET ECONNRESET +#define MP_ENOBUFS ENOBUFS +#define MP_EISCONN EISCONN +#define MP_ENOTCONN ENOTCONN +#define MP_ETIMEDOUT ETIMEDOUT +#define MP_ECONNREFUSED ECONNREFUSED +#define MP_EHOSTUNREACH EHOSTUNREACH +#define MP_EALREADY EALREADY +#define MP_EINPROGRESS EINPROGRESS +#define MP_ECANCELED ECANCELED + +#endif + +#if MICROPY_PY_ERRNO + +#include "py/obj.h" + +qstr mp_errno_to_str(mp_obj_t errno_val); + +#endif + +#endif // MICROPY_INCLUDED_PY_MPERRNO_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mphal.h b/non_catalog_apps/mp_flipper/lib/micropython/py/mphal.h new file mode 100644 index 00000000..a4f222d0 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mphal.h @@ -0,0 +1,114 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MPHAL_H +#define MICROPY_INCLUDED_PY_MPHAL_H + +#include +#include "py/mpconfig.h" + +#ifdef MICROPY_MPHALPORT_H +#include MICROPY_MPHALPORT_H +#else +#include +#endif + +// On embedded platforms, these will typically enable/disable irqs. +#ifndef MICROPY_BEGIN_ATOMIC_SECTION +#define MICROPY_BEGIN_ATOMIC_SECTION() (0) +#endif +#ifndef MICROPY_END_ATOMIC_SECTION +#define MICROPY_END_ATOMIC_SECTION(state) (void)(state) +#endif + +#ifndef mp_hal_stdio_poll +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags); +#endif + +#ifndef mp_hal_stdin_rx_chr +int mp_hal_stdin_rx_chr(void); +#endif + +#ifndef mp_hal_stdout_tx_str +void mp_hal_stdout_tx_str(const char *str); +#endif + +#ifndef mp_hal_stdout_tx_strn +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len); +#endif + +#ifndef mp_hal_stdout_tx_strn_cooked +void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len); +#endif + +#ifndef mp_hal_delay_ms +void mp_hal_delay_ms(mp_uint_t ms); +#endif + +#ifndef mp_hal_delay_us +void mp_hal_delay_us(mp_uint_t us); +#endif + +#ifndef mp_hal_ticks_ms +mp_uint_t mp_hal_ticks_ms(void); +#endif + +#ifndef mp_hal_ticks_us +mp_uint_t mp_hal_ticks_us(void); +#endif + +#ifndef mp_hal_ticks_cpu +mp_uint_t mp_hal_ticks_cpu(void); +#endif + +#ifndef mp_hal_time_ns +// Nanoseconds since the Epoch. +uint64_t mp_hal_time_ns(void); +#endif + +// If port HAL didn't define its own pin API, use generic +// "virtual pin" API from the core. +#ifndef mp_hal_pin_obj_t +#define mp_hal_pin_obj_t mp_obj_t +#define mp_hal_get_pin_obj(pin) (pin) +#define mp_hal_pin_read(pin) mp_virtual_pin_read(pin) +#define mp_hal_pin_write(pin, v) mp_virtual_pin_write(pin, v) +#include "extmod/virtpin.h" +#endif + +// Event handling and wait-for-event functions. + +#ifndef MICROPY_INTERNAL_WFE +// Fallback definition for ports that don't need to suspend the CPU. +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) (void)0 +#endif + +#ifndef MICROPY_INTERNAL_EVENT_HOOK +// Fallback definition for ports that don't need any port-specific +// non-blocking event processing. +#define MICROPY_INTERNAL_EVENT_HOOK (void)0 +#endif + +#endif // MICROPY_INCLUDED_PY_MPHAL_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpprint.c b/non_catalog_apps/mp_flipper/lib/micropython/py/mpprint.c new file mode 100644 index 00000000..291e4145 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpprint.c @@ -0,0 +1,576 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * + * 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 "py/mphal.h" +#include "py/mpprint.h" +#include "py/obj.h" +#include "py/objint.h" +#include "py/runtime.h" + +#if MICROPY_PY_BUILTINS_FLOAT +#include "py/formatfloat.h" +#endif + +static const char pad_spaces[] = " "; +static const char pad_zeroes[] = "0000000000000000"; + +static void plat_print_strn(void *env, const char *str, size_t len) { + (void)env; + MP_PLAT_PRINT_STRN(str, len); +} + +const mp_print_t mp_plat_print = {NULL, plat_print_strn}; + +int mp_print_str(const mp_print_t *print, const char *str) { + size_t len = strlen(str); + if (len) { + print->print_strn(print->data, str, len); + } + return len; +} + +int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width) { + int left_pad = 0; + int right_pad = 0; + int pad = width - len; + int pad_size; + int total_chars_printed = 0; + const char *pad_chars; + + if (!fill || fill == ' ') { + pad_chars = pad_spaces; + pad_size = sizeof(pad_spaces) - 1; + } else if (fill == '0') { + pad_chars = pad_zeroes; + pad_size = sizeof(pad_zeroes) - 1; + } else { + // Other pad characters are fairly unusual, so we'll take the hit + // and output them 1 at a time. + pad_chars = &fill; + pad_size = 1; + } + + if (flags & PF_FLAG_CENTER_ADJUST) { + left_pad = pad / 2; + right_pad = pad - left_pad; + } else if (flags & PF_FLAG_LEFT_ADJUST) { + right_pad = pad; + } else { + left_pad = pad; + } + + if (left_pad > 0) { + total_chars_printed += left_pad; + while (left_pad > 0) { + int p = left_pad; + if (p > pad_size) { + p = pad_size; + } + print->print_strn(print->data, pad_chars, p); + left_pad -= p; + } + } + if (len) { + print->print_strn(print->data, str, len); + total_chars_printed += len; + } + if (right_pad > 0) { + total_chars_printed += right_pad; + while (right_pad > 0) { + int p = right_pad; + if (p > pad_size) { + p = pad_size; + } + print->print_strn(print->data, pad_chars, p); + right_pad -= p; + } + } + return total_chars_printed; +} + +// 32-bits is 10 digits, add 3 for commas, 1 for sign, 1 for terminating null +// We can use 16 characters for 32-bit and 32 characters for 64-bit +#define INT_BUF_SIZE (sizeof(mp_int_t) * 4) + +// Our mp_vprintf function below does not support the '#' format modifier to +// print the prefix of a non-base-10 number, so we don't need code for this. +#define SUPPORT_INT_BASE_PREFIX (0) + +// This function is used exclusively by mp_vprintf to format ints. +// It needs to be a separate function to mp_print_mp_int, since converting to a mp_int looses the MSB. +static int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) { + char sign = 0; + if (sgn) { + if ((mp_int_t)x < 0) { + sign = '-'; + x = -x; + } else if (flags & PF_FLAG_SHOW_SIGN) { + sign = '+'; + } else if (flags & PF_FLAG_SPACE_SIGN) { + sign = ' '; + } + } + + char buf[INT_BUF_SIZE]; + char *b = buf + INT_BUF_SIZE; + + if (x == 0) { + *(--b) = '0'; + } else { + do { + int c = x % base; + x /= base; + if (c >= 10) { + c += base_char - 10; + } else { + c += '0'; + } + *(--b) = c; + } while (b > buf && x != 0); + } + + #if SUPPORT_INT_BASE_PREFIX + char prefix_char = '\0'; + + if (flags & PF_FLAG_SHOW_PREFIX) { + if (base == 2) { + prefix_char = base_char + 'b' - 'a'; + } else if (base == 8) { + prefix_char = base_char + 'o' - 'a'; + } else if (base == 16) { + prefix_char = base_char + 'x' - 'a'; + } + } + #endif + + int len = 0; + if (flags & PF_FLAG_PAD_AFTER_SIGN) { + if (sign) { + len += mp_print_strn(print, &sign, 1, flags, fill, 1); + width--; + } + #if SUPPORT_INT_BASE_PREFIX + if (prefix_char) { + len += mp_print_strn(print, "0", 1, flags, fill, 1); + len += mp_print_strn(print, &prefix_char, 1, flags, fill, 1); + width -= 2; + } + #endif + } else { + #if SUPPORT_INT_BASE_PREFIX + if (prefix_char && b > &buf[1]) { + *(--b) = prefix_char; + *(--b) = '0'; + } + #endif + if (sign && b > buf) { + *(--b) = sign; + } + } + + len += mp_print_strn(print, b, buf + INT_BUF_SIZE - b, flags, fill, width); + return len; +} + +int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) { + // These are the only values for "base" that are required to be supported by this + // function, since Python only allows the user to format integers in these bases. + // If needed this function could be generalised to handle other values. + assert(base == 2 || base == 8 || base == 10 || base == 16); + + if (!mp_obj_is_int(x)) { + // This will convert booleans to int, or raise an error for + // non-integer types. + x = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int(x)); + } + + if ((flags & (PF_FLAG_LEFT_ADJUST | PF_FLAG_CENTER_ADJUST)) == 0 && fill == '0') { + if (prec > width) { + width = prec; + } + prec = 0; + } + char prefix_buf[4]; + char *prefix = prefix_buf; + + if (mp_obj_int_sign(x) >= 0) { + if (flags & PF_FLAG_SHOW_SIGN) { + *prefix++ = '+'; + } else if (flags & PF_FLAG_SPACE_SIGN) { + *prefix++ = ' '; + } + } + + if (flags & PF_FLAG_SHOW_PREFIX) { + if (base == 2) { + *prefix++ = '0'; + *prefix++ = base_char + 'b' - 'a'; + } else if (base == 8) { + *prefix++ = '0'; + if (flags & PF_FLAG_SHOW_OCTAL_LETTER) { + *prefix++ = base_char + 'o' - 'a'; + } + } else if (base == 16) { + *prefix++ = '0'; + *prefix++ = base_char + 'x' - 'a'; + } + } + *prefix = '\0'; + int prefix_len = prefix - prefix_buf; + prefix = prefix_buf; + + char comma = '\0'; + if (flags & PF_FLAG_SHOW_COMMA) { + comma = ','; + } + + // The size of this buffer is rather arbitrary. If it's not large + // enough, a dynamic one will be allocated. + char stack_buf[sizeof(mp_int_t) * 4]; + char *buf = stack_buf; + size_t buf_size = sizeof(stack_buf); + size_t fmt_size = 0; + char *str; + + if (prec > 1) { + flags |= PF_FLAG_PAD_AFTER_SIGN; + } + char sign = '\0'; + if (flags & PF_FLAG_PAD_AFTER_SIGN) { + // We add the pad in this function, so since the pad goes after + // the sign & prefix, we format without a prefix + str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size, + x, base, NULL, base_char, comma); + if (*str == '-') { + sign = *str++; + fmt_size--; + } + } else { + str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size, + x, base, prefix, base_char, comma); + } + + int spaces_before = 0; + int spaces_after = 0; + + if (prec > 1) { + // If prec was specified, then prec specifies the width to zero-pad the + // the number to. This zero-padded number then gets left or right + // aligned in width characters. + + int prec_width = fmt_size; // The digits + if (prec_width < prec) { + prec_width = prec; + } + if (flags & PF_FLAG_PAD_AFTER_SIGN) { + if (sign) { + prec_width++; + } + prec_width += prefix_len; + } + if (prec_width < width) { + if (flags & PF_FLAG_LEFT_ADJUST) { + spaces_after = width - prec_width; + } else { + spaces_before = width - prec_width; + } + } + fill = '0'; + flags &= ~PF_FLAG_LEFT_ADJUST; + } + + int len = 0; + if (spaces_before) { + len += mp_print_strn(print, "", 0, 0, ' ', spaces_before); + } + if (flags & PF_FLAG_PAD_AFTER_SIGN) { + // pad after sign implies pad after prefix as well. + if (sign) { + len += mp_print_strn(print, &sign, 1, 0, 0, 1); + width--; + } + if (prefix_len) { + len += mp_print_strn(print, prefix, prefix_len, 0, 0, 1); + width -= prefix_len; + } + } + if (prec > 1) { + width = prec; + } + + len += mp_print_strn(print, str, fmt_size, flags, fill, width); + + if (spaces_after) { + len += mp_print_strn(print, "", 0, 0, ' ', spaces_after); + } + + if (buf != stack_buf) { + m_del(char, buf, buf_size); + } + return len; +} + +#if MICROPY_PY_BUILTINS_FLOAT +int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec) { + char buf[32]; + char sign = '\0'; + int chrs = 0; + + if (flags & PF_FLAG_SHOW_SIGN) { + sign = '+'; + } else + if (flags & PF_FLAG_SPACE_SIGN) { + sign = ' '; + } + + int len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign); + + char *s = buf; + + if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) { + buf[len++] = '%'; + buf[len] = '\0'; + } + + // buf[0] < '0' returns true if the first character is space, + or - + if ((flags & PF_FLAG_PAD_AFTER_SIGN) && buf[0] < '0') { + // We have a sign character + s++; + chrs += mp_print_strn(print, &buf[0], 1, 0, 0, 1); + width--; + len--; + } + + chrs += mp_print_strn(print, s, len, flags, fill, width); + + return chrs; +} +#endif + +int mp_printf(const mp_print_t *print, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int ret = mp_vprintf(print, fmt, ap); + va_end(ap); + return ret; +} + +int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { + int chrs = 0; + for (;;) { + { + const char *f = fmt; + while (*f != '\0' && *f != '%') { + ++f; // XXX UTF8 advance char + } + if (f > fmt) { + print->print_strn(print->data, fmt, f - fmt); + chrs += f - fmt; + fmt = f; + } + } + + if (*fmt == '\0') { + break; + } + + // move past % character + ++fmt; + + // parse flags, if they exist + int flags = 0; + char fill = ' '; + while (*fmt != '\0') { + if (*fmt == '-') { + flags |= PF_FLAG_LEFT_ADJUST; + } else if (*fmt == '+') { + flags |= PF_FLAG_SHOW_SIGN; + } else if (*fmt == ' ') { + flags |= PF_FLAG_SPACE_SIGN; + } else if (*fmt == '!') { + flags |= PF_FLAG_NO_TRAILZ; + } else if (*fmt == '0') { + flags |= PF_FLAG_PAD_AFTER_SIGN; + fill = '0'; + } else { + break; + } + ++fmt; + } + + // parse width, if it exists + int width = 0; + for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { + width = width * 10 + *fmt - '0'; + } + + // parse precision, if it exists + int prec = -1; + if (*fmt == '.') { + ++fmt; + if (*fmt == '*') { + ++fmt; + prec = va_arg(args, int); + } else { + prec = 0; + for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { + prec = prec * 10 + *fmt - '0'; + } + } + if (prec < 0) { + prec = 0; + } + } + + // parse long specifiers (only for LP64 model where they make a difference) + #ifndef __LP64__ + const + #endif + bool long_arg = false; + if (*fmt == 'l') { + ++fmt; + #ifdef __LP64__ + long_arg = true; + #endif + } + + if (*fmt == '\0') { + break; + } + + switch (*fmt) { + case 'b': + if (va_arg(args, int)) { + chrs += mp_print_strn(print, "true", 4, flags, fill, width); + } else { + chrs += mp_print_strn(print, "false", 5, flags, fill, width); + } + break; + case 'c': { + char str = va_arg(args, int); + chrs += mp_print_strn(print, &str, 1, flags, fill, width); + break; + } + case 'q': { + qstr qst = va_arg(args, qstr); + size_t len; + const char *str = (const char *)qstr_data(qst, &len); + if (prec >= 0 && (size_t)prec < len) { + len = prec; + } + chrs += mp_print_strn(print, str, len, flags, fill, width); + break; + } + case 's': { + const char *str = va_arg(args, const char *); + #ifndef NDEBUG + // With debugging enabled, catch printing of null string pointers + if (prec != 0 && str == NULL) { + chrs += mp_print_strn(print, "(null)", 6, flags, fill, width); + break; + } + #endif + size_t len = strlen(str); + if (prec >= 0 && (size_t)prec < len) { + len = prec; + } + chrs += mp_print_strn(print, str, len, flags, fill, width); + break; + } + case 'd': { + mp_int_t val; + if (long_arg) { + val = va_arg(args, long int); + } else { + val = va_arg(args, int); + } + chrs += mp_print_int(print, val, 1, 10, 'a', flags, fill, width); + break; + } + case 'u': + case 'x': + case 'X': { + int base = 16 - ((*fmt + 1) & 6); // maps char u/x/X to base 10/16/16 + char fmt_c = (*fmt & 0xf0) - 'P' + 'A'; // maps char u/x/X to char a/a/A + mp_uint_t val; + if (long_arg) { + val = va_arg(args, unsigned long int); + } else { + val = va_arg(args, unsigned int); + } + chrs += mp_print_int(print, val, 0, base, fmt_c, flags, fill, width); + break; + } + case 'p': + case 'P': // don't bother to handle upcase for 'P' + // Use unsigned long int to work on both ILP32 and LP64 systems + chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags, fill, width); + break; + #if MICROPY_PY_BUILTINS_FLOAT + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': { + #if ((MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT) || (MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE)) + mp_float_t f = (mp_float_t)va_arg(args, double); + chrs += mp_print_float(print, f, *fmt, flags, fill, width, prec); + #else + #error Unknown MICROPY FLOAT IMPL + #endif + break; + } + #endif + // Because 'l' is eaten above, another 'l' means %ll. We need to support + // this length specifier for OBJ_REPR_D (64-bit NaN boxing). + // TODO Either enable this unconditionally, or provide a specific config var. + #if (MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D) || defined(_WIN64) + case 'l': { + unsigned long long int arg_value = va_arg(args, unsigned long long int); + ++fmt; + assert(*fmt == 'u' || *fmt == 'd' || !"unsupported fmt char"); + chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); + break; + } + #endif + default: + // if it's not %% then it's an unsupported format character + assert(*fmt == '%' || !"unsupported fmt char"); + print->print_strn(print->data, fmt, 1); + chrs += 1; + break; + } + ++fmt; + } + return chrs; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpprint.h b/non_catalog_apps/mp_flipper/lib/micropython/py/mpprint.h new file mode 100644 index 00000000..8383ea85 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpprint.h @@ -0,0 +1,82 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MPPRINT_H +#define MICROPY_INCLUDED_PY_MPPRINT_H + +#include "py/mpconfig.h" + +#define PF_FLAG_LEFT_ADJUST (0x001) +#define PF_FLAG_SHOW_SIGN (0x002) +#define PF_FLAG_SPACE_SIGN (0x004) +#define PF_FLAG_NO_TRAILZ (0x008) +#define PF_FLAG_SHOW_PREFIX (0x010) +#define PF_FLAG_SHOW_COMMA (0x020) +#define PF_FLAG_PAD_AFTER_SIGN (0x040) +#define PF_FLAG_CENTER_ADJUST (0x080) +#define PF_FLAG_ADD_PERCENT (0x100) +#define PF_FLAG_SHOW_OCTAL_LETTER (0x200) + +#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES +#define MP_PYTHON_PRINTER &mp_sys_stdout_print +#else +#define MP_PYTHON_PRINTER &mp_plat_print +#endif + +typedef void (*mp_print_strn_t)(void *data, const char *str, size_t len); + +typedef struct _mp_print_t { + void *data; + mp_print_strn_t print_strn; +} mp_print_t; + +typedef struct _mp_print_ext_t { + mp_print_t base; + const char *item_separator; + const char *key_separator; +} mp_print_ext_t; + +#define MP_PRINT_GET_EXT(print) ((mp_print_ext_t *)print) + +// All (non-debug) prints go through one of the two interfaces below. +// 1) Wrapper for platform print function, which wraps MP_PLAT_PRINT_STRN. +extern const mp_print_t mp_plat_print; +#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES +// 2) Wrapper for printing to sys.stdout. +extern const mp_print_t mp_sys_stdout_print; +#endif + +int mp_print_str(const mp_print_t *print, const char *str); +int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width); +#if MICROPY_PY_BUILTINS_FLOAT +int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec); +#endif + +int mp_printf(const mp_print_t *print, const char *fmt, ...); +#ifdef va_start +int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); +#endif + +#endif // MICROPY_INCLUDED_PY_MPPRINT_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpstate.c b/non_catalog_apps/mp_flipper/lib/micropython/py/mpstate.c new file mode 100644 index 00000000..6ce64adf --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpstate.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 "py/mpstate.h" + +#if MICROPY_DYNAMIC_COMPILER +mp_dynamic_compiler_t mp_dynamic_compiler = {0}; +#endif + +mp_state_ctx_t mp_state_ctx; diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpstate.h b/non_catalog_apps/mp_flipper/lib/micropython/py/mpstate.h new file mode 100644 index 00000000..af55e764 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpstate.h @@ -0,0 +1,320 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MPSTATE_H +#define MICROPY_INCLUDED_PY_MPSTATE_H + +#include + +#include "py/mpconfig.h" +#include "py/mpthread.h" +#include "py/misc.h" +#include "py/nlr.h" +#include "py/obj.h" +#include "py/objlist.h" +#include "py/objexcept.h" + +// This file contains structures defining the state of the MicroPython +// memory system, runtime and virtual machine. The state is a global +// variable, but in the future it is hoped that the state can become local. + +#if MICROPY_PY_SYS_ATTR_DELEGATION +// Must be kept in sync with sys_mutable_keys in modsys.c. +enum { + #if MICROPY_PY_SYS_PATH + MP_SYS_MUTABLE_PATH, + #endif + #if MICROPY_PY_SYS_PS1_PS2 + MP_SYS_MUTABLE_PS1, + MP_SYS_MUTABLE_PS2, + #endif + #if MICROPY_PY_SYS_TRACEBACKLIMIT + MP_SYS_MUTABLE_TRACEBACKLIMIT, + #endif + MP_SYS_MUTABLE_NUM, +}; +#endif // MICROPY_PY_SYS_ATTR_DELEGATION + +// This structure contains dynamic configuration for the compiler. +#if MICROPY_DYNAMIC_COMPILER +typedef struct mp_dynamic_compiler_t { + uint8_t small_int_bits; // must be <= host small_int_bits + uint8_t native_arch; + uint8_t nlr_buf_num_regs; +} mp_dynamic_compiler_t; +extern mp_dynamic_compiler_t mp_dynamic_compiler; +#endif + +// These are the values for sched_state +#define MP_SCHED_IDLE (1) +#define MP_SCHED_LOCKED (-1) +#define MP_SCHED_PENDING (0) // 0 so it's a quick check in the VM + +typedef struct _mp_sched_item_t { + mp_obj_t func; + mp_obj_t arg; +} mp_sched_item_t; + +// This structure holds information about a single contiguous area of +// memory reserved for the memory manager. +typedef struct _mp_state_mem_area_t { + #if MICROPY_GC_SPLIT_HEAP + struct _mp_state_mem_area_t *next; + #endif + + byte *gc_alloc_table_start; + size_t gc_alloc_table_byte_len; + #if MICROPY_ENABLE_FINALISER + byte *gc_finaliser_table_start; + #endif + byte *gc_pool_start; + byte *gc_pool_end; + + size_t gc_last_free_atb_index; + size_t gc_last_used_block; // The block ID of the highest block allocated in the area +} mp_state_mem_area_t; + +// This structure hold information about the memory allocation system. +typedef struct _mp_state_mem_t { + #if MICROPY_MEM_STATS + size_t total_bytes_allocated; + size_t current_bytes_allocated; + size_t peak_bytes_allocated; + #endif + + mp_state_mem_area_t area; + + int gc_stack_overflow; + MICROPY_GC_STACK_ENTRY_TYPE gc_block_stack[MICROPY_ALLOC_GC_STACK_SIZE]; + #if MICROPY_GC_SPLIT_HEAP + // Array that tracks the area for each block on gc_block_stack. + mp_state_mem_area_t *gc_area_stack[MICROPY_ALLOC_GC_STACK_SIZE]; + #endif + + // This variable controls auto garbage collection. If set to 0 then the + // GC won't automatically run when gc_alloc can't find enough blocks. But + // you can still allocate/free memory and also explicitly call gc_collect. + uint16_t gc_auto_collect_enabled; + + #if MICROPY_GC_ALLOC_THRESHOLD + size_t gc_alloc_amount; + size_t gc_alloc_threshold; + #endif + + #if MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *gc_last_free_area; + #endif + + #if MICROPY_PY_GC_COLLECT_RETVAL + size_t gc_collected; + #endif + + #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL + // This is a global mutex used to make the GC thread-safe. + mp_thread_mutex_t gc_mutex; + #endif +} mp_state_mem_t; + +// This structure hold runtime and VM information. It includes a section +// which contains root pointers that must be scanned by the GC. +typedef struct _mp_state_vm_t { + // + // CONTINUE ROOT POINTER SECTION + // This must start at the start of this structure and follows + // the state in the mp_state_thread_t structure, continuing + // the root pointer section from there. + // + + qstr_pool_t *last_pool; + + #if MICROPY_TRACKED_ALLOC + struct _m_tracked_node_t *m_tracked_head; + #endif + + // non-heap memory for creating an exception if we can't allocate RAM + mp_obj_exception_t mp_emergency_exception_obj; + + // memory for exception arguments if we can't allocate RAM + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + #if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0 + // statically allocated buf (needs to be aligned to mp_obj_t) + mp_obj_t mp_emergency_exception_buf[MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE / sizeof(mp_obj_t)]; + #else + // dynamically allocated buf + byte *mp_emergency_exception_buf; + #endif + #endif + + #if MICROPY_KBD_EXCEPTION + // exception object of type KeyboardInterrupt + mp_obj_exception_t mp_kbd_exception; + #endif + + // dictionary with loaded modules (may be exposed as sys.modules) + mp_obj_dict_t mp_loaded_modules_dict; + + // dictionary for the __main__ module + mp_obj_dict_t dict_main; + + // dictionary for overridden builtins + #if MICROPY_CAN_OVERRIDE_BUILTINS + mp_obj_dict_t *mp_module_builtins_override_dict; + #endif + + // Include any root pointers registered with MP_REGISTER_ROOT_POINTER(). + #ifndef NO_QSTR + // Only include root pointer definitions when not doing qstr extraction, because + // the qstr extraction stage also generates the root pointers header file. + #include "genhdr/root_pointers.h" + #endif + + // + // END ROOT POINTER SECTION + //////////////////////////////////////////////////////////// + + // pointer and sizes to store interned string data + // (qstr_last_chunk can be root pointer but is also stored in qstr pool) + char *qstr_last_chunk; + size_t qstr_last_alloc; + size_t qstr_last_used; + + #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL + // This is a global mutex used to make qstr interning thread-safe. + mp_thread_mutex_t qstr_mutex; + #endif + + #if MICROPY_ENABLE_COMPILER + mp_uint_t mp_optimise_value; + #if MICROPY_EMIT_NATIVE + uint8_t default_emit_opt; // one of MP_EMIT_OPT_xxx + #endif + #endif + + // size of the emergency exception buf, if it's dynamically allocated + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0 + mp_int_t mp_emergency_exception_buf_size; + #endif + + #if MICROPY_ENABLE_SCHEDULER + volatile int16_t sched_state; + + #if MICROPY_SCHEDULER_STATIC_NODES + // These will usually point to statically allocated memory. They are not + // traced by the GC. They are assumed to be zero'd out before mp_init() is + // called (usually because this struct lives in the BSS). + struct _mp_sched_node_t *sched_head; + struct _mp_sched_node_t *sched_tail; + #endif + + // These index sched_queue. + uint8_t sched_len; + uint8_t sched_idx; + #endif + + #if MICROPY_ENABLE_VM_ABORT + bool vm_abort; + nlr_buf_t *nlr_abort; + #endif + + #if MICROPY_PY_THREAD_GIL + // This is a global mutex used to make the VM/runtime thread-safe. + mp_thread_mutex_t gil_mutex; + #endif + + #if MICROPY_OPT_MAP_LOOKUP_CACHE + // See mp_map_lookup. + uint8_t map_lookup_cache[MICROPY_OPT_MAP_LOOKUP_CACHE_SIZE]; + #endif +} mp_state_vm_t; + +// This structure holds state that is specific to a given thread. Everything +// in this structure is scanned for root pointers. Anything added to this +// structure must have corresponding initialisation added to thread_entry (in +// py/modthread.c). +typedef struct _mp_state_thread_t { + // Stack top at the start of program + char *stack_top; + + #if MICROPY_STACK_CHECK + size_t stack_limit; + #endif + + #if MICROPY_ENABLE_PYSTACK + uint8_t *pystack_start; + uint8_t *pystack_end; + uint8_t *pystack_cur; + #endif + + // Locking of the GC is done per thread. + uint16_t gc_lock_depth; + + //////////////////////////////////////////////////////////// + // START ROOT POINTER SECTION + // Everything that needs GC scanning must start here, and + // is followed by state in the mp_state_vm_t structure. + // + + mp_obj_dict_t *dict_locals; + mp_obj_dict_t *dict_globals; + + nlr_buf_t *nlr_top; + nlr_jump_callback_node_t *nlr_jump_callback_top; + + // pending exception object (MP_OBJ_NULL if not pending) + volatile mp_obj_t mp_pending_exception; + + // If MP_OBJ_STOP_ITERATION is propagated then this holds its argument. + mp_obj_t stop_iteration_arg; + + #if MICROPY_PY_SYS_SETTRACE + mp_obj_t prof_trace_callback; + bool prof_callback_is_executing; + struct _mp_code_state_t *current_code_state; + #endif +} mp_state_thread_t; + +// This structure combines the above 3 structures. +// The order of the entries are important for root pointer scanning in the GC to work. +typedef struct _mp_state_ctx_t { + mp_state_thread_t thread; + mp_state_vm_t vm; + mp_state_mem_t mem; +} mp_state_ctx_t; + +extern mp_state_ctx_t mp_state_ctx; + +#define MP_STATE_VM(x) (mp_state_ctx.vm.x) +#define MP_STATE_MEM(x) (mp_state_ctx.mem.x) +#define MP_STATE_MAIN_THREAD(x) (mp_state_ctx.thread.x) + +#if MICROPY_PY_THREAD +#define MP_STATE_THREAD(x) (mp_thread_get_state()->x) +#define mp_thread_is_main_thread() (mp_thread_get_state() == &mp_state_ctx.thread) +#else +#define MP_STATE_THREAD(x) MP_STATE_MAIN_THREAD(x) +#define mp_thread_is_main_thread() (true) +#endif + +#endif // MICROPY_INCLUDED_PY_MPSTATE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpthread.h b/non_catalog_apps/mp_flipper/lib/micropython/py/mpthread.h new file mode 100644 index 00000000..f335cc02 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpthread.h @@ -0,0 +1,62 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MPTHREAD_H +#define MICROPY_INCLUDED_PY_MPTHREAD_H + +#include "py/mpconfig.h" + +#if MICROPY_PY_THREAD + +struct _mp_state_thread_t; + +#ifdef MICROPY_MPTHREADPORT_H +#include MICROPY_MPTHREADPORT_H +#else +#include +#endif + +struct _mp_state_thread_t *mp_thread_get_state(void); +void mp_thread_set_state(struct _mp_state_thread_t *state); +mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size); +mp_uint_t mp_thread_get_id(void); +void mp_thread_start(void); +void mp_thread_finish(void); +void mp_thread_mutex_init(mp_thread_mutex_t *mutex); +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait); +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex); + +#endif // MICROPY_PY_THREAD + +#if MICROPY_PY_THREAD && MICROPY_PY_THREAD_GIL +#include "py/mpstate.h" +#define MP_THREAD_GIL_ENTER() mp_thread_mutex_lock(&MP_STATE_VM(gil_mutex), 1) +#define MP_THREAD_GIL_EXIT() mp_thread_mutex_unlock(&MP_STATE_VM(gil_mutex)) +#else +#define MP_THREAD_GIL_ENTER() +#define MP_THREAD_GIL_EXIT() +#endif + +#endif // MICROPY_INCLUDED_PY_MPTHREAD_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpz.c b/non_catalog_apps/mp_flipper/lib/micropython/py/mpz.c new file mode 100644 index 00000000..502d4e1c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpz.c @@ -0,0 +1,1746 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpz.h" + +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ + +#define DIG_SIZE (MPZ_DIG_SIZE) +#define DIG_MASK ((MPZ_LONG_1 << DIG_SIZE) - 1) +#define DIG_MSB (MPZ_LONG_1 << (DIG_SIZE - 1)) +#define DIG_BASE (MPZ_LONG_1 << DIG_SIZE) + +/* + mpz is an arbitrary precision integer type with a public API. + + mpn functions act on non-negative integers represented by an array of generalised + digits (eg a word per digit). You also need to specify separately the length of the + array. There is no public API for mpn. Rather, the functions are used by mpz to + implement its features. + + Integer values are stored little endian (first digit is first in memory). + + Definition of normalise: ? +*/ + +static size_t mpn_remove_trailing_zeros(mpz_dig_t *oidig, mpz_dig_t *idig) { + for (--idig; idig >= oidig && *idig == 0; --idig) { + } + return idig + 1 - oidig; +} + +/* compares i with j + returns sign(i - j) + assumes i, j are normalised +*/ +static int mpn_cmp(const mpz_dig_t *idig, size_t ilen, const mpz_dig_t *jdig, size_t jlen) { + if (ilen < jlen) { + return -1; + } + if (ilen > jlen) { + return 1; + } + + for (idig += ilen, jdig += ilen; ilen > 0; --ilen) { + mpz_dbl_dig_signed_t cmp = (mpz_dbl_dig_t)*(--idig) - (mpz_dbl_dig_t)*(--jdig); + if (cmp < 0) { + return -1; + } + if (cmp > 0) { + return 1; + } + } + + return 0; +} + +/* computes i = j << n + returns number of digits in i + assumes enough memory in i; assumes normalised j; assumes n > 0 + can have i, j pointing to same memory +*/ +static size_t mpn_shl(mpz_dig_t *idig, mpz_dig_t *jdig, size_t jlen, mp_uint_t n) { + mp_uint_t n_whole = (n + DIG_SIZE - 1) / DIG_SIZE; + mp_uint_t n_part = n % DIG_SIZE; + if (n_part == 0) { + n_part = DIG_SIZE; + } + + // start from the high end of the digit arrays + idig += jlen + n_whole - 1; + jdig += jlen - 1; + + // shift the digits + mpz_dbl_dig_t d = 0; + for (size_t i = jlen; i > 0; i--, idig--, jdig--) { + d |= *jdig; + *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK; + d <<= DIG_SIZE; + } + + // store remaining bits + *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK; + idig -= n_whole - 1; + memset(idig, 0, (n_whole - 1) * sizeof(mpz_dig_t)); + + // work out length of result + jlen += n_whole; + while (jlen != 0 && idig[jlen - 1] == 0) { + jlen--; + } + + // return length of result + return jlen; +} + +/* computes i = j >> n + returns number of digits in i + assumes enough memory in i; assumes normalised j; assumes n > 0 + can have i, j pointing to same memory +*/ +static size_t mpn_shr(mpz_dig_t *idig, mpz_dig_t *jdig, size_t jlen, mp_uint_t n) { + mp_uint_t n_whole = n / DIG_SIZE; + mp_uint_t n_part = n % DIG_SIZE; + + if (n_whole >= jlen) { + return 0; + } + + jdig += n_whole; + jlen -= n_whole; + + for (size_t i = jlen; i > 0; i--, idig++, jdig++) { + mpz_dbl_dig_t d = *jdig; + if (i > 1) { + d |= (mpz_dbl_dig_t)jdig[1] << DIG_SIZE; + } + d >>= n_part; + *idig = d & DIG_MASK; + } + + if (idig[-1] == 0) { + jlen--; + } + + return jlen; +} + +/* computes i = j + k + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen + can have i, j, k pointing to same memory +*/ +static size_t mpn_add(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen) { + mpz_dig_t *oidig = idig; + mpz_dbl_dig_t carry = 0; + + jlen -= klen; + + for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) { + carry += (mpz_dbl_dig_t)*jdig + (mpz_dbl_dig_t)*kdig; + *idig = carry & DIG_MASK; + carry >>= DIG_SIZE; + } + + for (; jlen > 0; --jlen, ++idig, ++jdig) { + carry += *jdig; + *idig = carry & DIG_MASK; + carry >>= DIG_SIZE; + } + + if (carry != 0) { + *idig++ = carry; + } + + return idig - oidig; +} + +/* computes i = j - k + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes j >= k + can have i, j, k pointing to same memory +*/ +static size_t mpn_sub(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen) { + mpz_dig_t *oidig = idig; + mpz_dbl_dig_signed_t borrow = 0; + + jlen -= klen; + + for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) { + borrow += (mpz_dbl_dig_t)*jdig - (mpz_dbl_dig_t)*kdig; + *idig = borrow & DIG_MASK; + borrow >>= DIG_SIZE; + } + + for (; jlen > 0; --jlen, ++idig, ++jdig) { + borrow += *jdig; + *idig = borrow & DIG_MASK; + borrow >>= DIG_SIZE; + } + + return mpn_remove_trailing_zeros(oidig, idig); +} + +#if MICROPY_OPT_MPZ_BITWISE + +/* computes i = j & k + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen (jlen argument not needed) + can have i, j, k pointing to same memory +*/ +static size_t mpn_and(mpz_dig_t *idig, const mpz_dig_t *jdig, const mpz_dig_t *kdig, size_t klen) { + mpz_dig_t *oidig = idig; + + for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) { + *idig = *jdig & *kdig; + } + + return mpn_remove_trailing_zeros(oidig, idig); +} + +#endif + +/* i = -((-j) & (-k)) = ~((~j + 1) & (~k + 1)) + 1 + i = (j & (-k)) = (j & (~k + 1)) = ( j & (~k + 1)) + i = ((-j) & k) = ((~j + 1) & k) = ((~j + 1) & k ) + computes general form: + i = (im ^ (((j ^ jm) + jc) & ((k ^ km) + kc))) + ic where Xm = Xc == 0 ? 0 : DIG_MASK + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes length j >= length k + can have i, j, k pointing to same memory +*/ +static size_t mpn_and_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen, + mpz_dbl_dig_t carryi, mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk) { + mpz_dig_t *oidig = idig; + mpz_dig_t imask = (0 == carryi) ? 0 : DIG_MASK; + mpz_dig_t jmask = (0 == carryj) ? 0 : DIG_MASK; + mpz_dig_t kmask = (0 == carryk) ? 0 : DIG_MASK; + + for (; jlen > 0; ++idig, ++jdig) { + carryj += *jdig ^ jmask; + carryk += (--klen <= --jlen) ? (*kdig++ ^ kmask) : kmask; + carryi += ((carryj & carryk) ^ imask) & DIG_MASK; + *idig = carryi & DIG_MASK; + carryk >>= DIG_SIZE; + carryj >>= DIG_SIZE; + carryi >>= DIG_SIZE; + } + + if (0 != carryi) { + *idig++ = carryi; + } + + return mpn_remove_trailing_zeros(oidig, idig); +} + +#if MICROPY_OPT_MPZ_BITWISE + +/* computes i = j | k + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen + can have i, j, k pointing to same memory +*/ +static size_t mpn_or(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen) { + mpz_dig_t *oidig = idig; + + jlen -= klen; + + for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) { + *idig = *jdig | *kdig; + } + + for (; jlen > 0; --jlen, ++idig, ++jdig) { + *idig = *jdig; + } + + return idig - oidig; +} + +#endif + +/* i = -((-j) | (-k)) = ~((~j + 1) | (~k + 1)) + 1 + i = -(j | (-k)) = -(j | (~k + 1)) = ~( j | (~k + 1)) + 1 + i = -((-j) | k) = -((~j + 1) | k) = ~((~j + 1) | k ) + 1 + computes general form: + i = ~(((j ^ jm) + jc) | ((k ^ km) + kc)) + 1 where Xm = Xc == 0 ? 0 : DIG_MASK + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes length j >= length k + can have i, j, k pointing to same memory +*/ + +#if MICROPY_OPT_MPZ_BITWISE + +static size_t mpn_or_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen, + mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk) { + mpz_dig_t *oidig = idig; + mpz_dbl_dig_t carryi = 1; + mpz_dig_t jmask = (0 == carryj) ? 0 : DIG_MASK; + mpz_dig_t kmask = (0 == carryk) ? 0 : DIG_MASK; + + for (; jlen > 0; ++idig, ++jdig) { + carryj += *jdig ^ jmask; + carryk += (--klen <= --jlen) ? (*kdig++ ^ kmask) : kmask; + carryi += ((carryj | carryk) ^ DIG_MASK) & DIG_MASK; + *idig = carryi & DIG_MASK; + carryk >>= DIG_SIZE; + carryj >>= DIG_SIZE; + carryi >>= DIG_SIZE; + } + + // At least one of j,k must be negative so the above for-loop runs at least + // once. For carryi to be non-zero here it must be equal to 1 at the end of + // each iteration of the loop. So the accumulation of carryi must overflow + // each time, ie carryi += 0xff..ff. So carryj|carryk must be 0 in the + // DIG_MASK bits on each iteration. But considering all cases of signs of + // j,k one sees that this is not possible. + assert(carryi == 0); + + return mpn_remove_trailing_zeros(oidig, idig); +} + +#else + +static size_t mpn_or_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen, + mpz_dbl_dig_t carryi, mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk) { + mpz_dig_t *oidig = idig; + mpz_dig_t imask = (0 == carryi) ? 0 : DIG_MASK; + mpz_dig_t jmask = (0 == carryj) ? 0 : DIG_MASK; + mpz_dig_t kmask = (0 == carryk) ? 0 : DIG_MASK; + + for (; jlen > 0; ++idig, ++jdig) { + carryj += *jdig ^ jmask; + carryk += (--klen <= --jlen) ? (*kdig++ ^ kmask) : kmask; + carryi += ((carryj | carryk) ^ imask) & DIG_MASK; + *idig = carryi & DIG_MASK; + carryk >>= DIG_SIZE; + carryj >>= DIG_SIZE; + carryi >>= DIG_SIZE; + } + + // See comment in above mpn_or_neg for why carryi must be 0. + assert(carryi == 0); + + return mpn_remove_trailing_zeros(oidig, idig); +} + +#endif + +#if MICROPY_OPT_MPZ_BITWISE + +/* computes i = j ^ k + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen + can have i, j, k pointing to same memory +*/ +static size_t mpn_xor(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen) { + mpz_dig_t *oidig = idig; + + jlen -= klen; + + for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) { + *idig = *jdig ^ *kdig; + } + + for (; jlen > 0; --jlen, ++idig, ++jdig) { + *idig = *jdig; + } + + return mpn_remove_trailing_zeros(oidig, idig); +} + +#endif + +/* i = (-j) ^ (-k) = ~(j - 1) ^ ~(k - 1) = (j - 1) ^ (k - 1) + i = -(j ^ (-k)) = -(j ^ ~(k - 1)) = ~(j ^ ~(k - 1)) + 1 = (j ^ (k - 1)) + 1 + i = -((-j) ^ k) = -(~(j - 1) ^ k) = ~(~(j - 1) ^ k) + 1 = ((j - 1) ^ k) + 1 + computes general form: + i = ((j - 1 + jc) ^ (k - 1 + kc)) + ic + returns number of digits in i + assumes enough memory in i; assumes normalised j, k; assumes length j >= length k + can have i, j, k pointing to same memory +*/ +static size_t mpn_xor_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen, + mpz_dbl_dig_t carryi, mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk) { + mpz_dig_t *oidig = idig; + + for (; jlen > 0; ++idig, ++jdig) { + carryj += *jdig + DIG_MASK; + carryk += (--klen <= --jlen) ? (*kdig++ + DIG_MASK) : DIG_MASK; + carryi += (carryj ^ carryk) & DIG_MASK; + *idig = carryi & DIG_MASK; + carryk >>= DIG_SIZE; + carryj >>= DIG_SIZE; + carryi >>= DIG_SIZE; + } + + if (0 != carryi) { + *idig++ = carryi; + } + + return mpn_remove_trailing_zeros(oidig, idig); +} + +/* computes i = i * d1 + d2 + returns number of digits in i + assumes enough memory in i; assumes normalised i; assumes dmul != 0 +*/ +static size_t mpn_mul_dig_add_dig(mpz_dig_t *idig, size_t ilen, mpz_dig_t dmul, mpz_dig_t dadd) { + mpz_dig_t *oidig = idig; + mpz_dbl_dig_t carry = dadd; + + for (; ilen > 0; --ilen, ++idig) { + carry += (mpz_dbl_dig_t)*idig * (mpz_dbl_dig_t)dmul; // will never overflow so long as DIG_SIZE <= 8*sizeof(mpz_dbl_dig_t)/2 + *idig = carry & DIG_MASK; + carry >>= DIG_SIZE; + } + + if (carry != 0) { + *idig++ = carry; + } + + return idig - oidig; +} + +/* computes i = j * k + returns number of digits in i + assumes enough memory in i; assumes i is zeroed; assumes normalised j, k + can have j, k point to same memory +*/ +static size_t mpn_mul(mpz_dig_t *idig, mpz_dig_t *jdig, size_t jlen, mpz_dig_t *kdig, size_t klen) { + mpz_dig_t *oidig = idig; + size_t ilen = 0; + + for (; klen > 0; --klen, ++idig, ++kdig) { + mpz_dig_t *id = idig; + mpz_dbl_dig_t carry = 0; + + size_t jl = jlen; + for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) { + carry += (mpz_dbl_dig_t)*id + (mpz_dbl_dig_t)*jd * (mpz_dbl_dig_t)*kdig; // will never overflow so long as DIG_SIZE <= 8*sizeof(mpz_dbl_dig_t)/2 + *id = carry & DIG_MASK; + carry >>= DIG_SIZE; + } + + if (carry != 0) { + *id++ = carry; + } + + ilen = id - oidig; + } + + return ilen; +} + +/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem) + assumes den != 0 + assumes num_dig has enough memory to be extended by 1 digit + assumes quo_dig has enough memory (as many digits as num) + assumes quo_dig is filled with zeros +*/ +static void mpn_div(mpz_dig_t *num_dig, size_t *num_len, const mpz_dig_t *den_dig, size_t den_len, mpz_dig_t *quo_dig, size_t *quo_len) { + mpz_dig_t *orig_num_dig = num_dig; + mpz_dig_t *orig_quo_dig = quo_dig; + mpz_dig_t norm_shift = 0; + mpz_dbl_dig_t lead_den_digit; + + // handle simple cases + { + int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len); + if (cmp == 0) { + *num_len = 0; + quo_dig[0] = 1; + *quo_len = 1; + return; + } else if (cmp < 0) { + // numerator remains the same + *quo_len = 0; + return; + } + } + + // We need to normalise the denominator (leading bit of leading digit is 1) + // so that the division routine works. Since the denominator memory is + // read-only we do the normalisation on the fly, each time a digit of the + // denominator is needed. We need to know is how many bits to shift by. + + // count number of leading zeros in leading digit of denominator + { + mpz_dig_t d = den_dig[den_len - 1]; + while ((d & DIG_MSB) == 0) { + d <<= 1; + ++norm_shift; + } + } + + // now need to shift numerator by same amount as denominator + // first, increase length of numerator in case we need more room to shift + num_dig[*num_len] = 0; + ++(*num_len); + for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) { + mpz_dig_t n = *num; + *num = ((n << norm_shift) | carry) & DIG_MASK; + carry = (mpz_dbl_dig_t)n >> (DIG_SIZE - norm_shift); + } + + // cache the leading digit of the denominator + lead_den_digit = (mpz_dbl_dig_t)den_dig[den_len - 1] << norm_shift; + if (den_len >= 2) { + lead_den_digit |= (mpz_dbl_dig_t)den_dig[den_len - 2] >> (DIG_SIZE - norm_shift); + } + + // point num_dig to last digit in numerator + num_dig += *num_len - 1; + + // calculate number of digits in quotient + *quo_len = *num_len - den_len; + + // point to last digit to store for quotient + quo_dig += *quo_len - 1; + + // keep going while we have enough digits to divide + while (*num_len > den_len) { + mpz_dbl_dig_t quo = ((mpz_dbl_dig_t)*num_dig << DIG_SIZE) | num_dig[-1]; + + // get approximate quotient + quo /= lead_den_digit; + + // Multiply quo by den and subtract from num to get remainder. + // Must be careful with overflow of the borrow variable. Both + // borrow and low_digs are signed values and need signed right-shift, + // but x is unsigned and may take a full-range value. + const mpz_dig_t *d = den_dig; + mpz_dbl_dig_t d_norm = 0; + mpz_dbl_dig_signed_t borrow = 0; + for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { + // Get the next digit in (den). + d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); + // Multiply the next digit in (quo * den). + mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK); + // Compute the low DIG_MASK bits of the next digit in (num - quo * den) + mpz_dbl_dig_signed_t low_digs = (borrow & DIG_MASK) + *n - (x & DIG_MASK); + // Store the digit result for (num). + *n = low_digs & DIG_MASK; + // Compute the borrow, shifted right before summing to avoid overflow. + borrow = (borrow >> DIG_SIZE) - (x >> DIG_SIZE) + (low_digs >> DIG_SIZE); + } + + // At this point we have either: + // + // 1. quo was the correct value and the most-sig-digit of num is exactly + // cancelled by borrow (borrow + *num_dig == 0). In this case there is + // nothing more to do. + // + // 2. quo was too large, we subtracted too many den from num, and the + // most-sig-digit of num is less than needed (borrow + *num_dig < 0). + // In this case we must reduce quo and add back den to num until the + // carry from this operation cancels out the borrow. + // + borrow += *num_dig; + for (; borrow != 0; --quo) { + d = den_dig; + d_norm = 0; + mpz_dbl_dig_t carry = 0; + for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { + d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); + carry += (mpz_dbl_dig_t)*n + (d_norm & DIG_MASK); + *n = carry & DIG_MASK; + carry >>= DIG_SIZE; + } + borrow += carry; + } + + // store this digit of the quotient + *quo_dig = quo & DIG_MASK; + --quo_dig; + + // move down to next digit of numerator + --num_dig; + --(*num_len); + } + + // unnormalise numerator (remainder now) + for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) { + mpz_dig_t n = *num; + *num = ((n >> norm_shift) | carry) & DIG_MASK; + carry = (mpz_dbl_dig_t)n << (DIG_SIZE - norm_shift); + } + + // strip trailing zeros + + while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) { + --(*quo_len); + } + + while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) { + --(*num_len); + } +} + +#define MIN_ALLOC (2) + +void mpz_init_zero(mpz_t *z) { + z->neg = 0; + z->fixed_dig = 0; + z->alloc = 0; + z->len = 0; + z->dig = NULL; +} + +void mpz_init_from_int(mpz_t *z, mp_int_t val) { + mpz_init_zero(z); + mpz_set_from_int(z, val); +} + +void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, size_t alloc, mp_int_t val) { + z->neg = 0; + z->fixed_dig = 1; + z->alloc = alloc; + z->len = 0; + z->dig = dig; + mpz_set_from_int(z, val); +} + +void mpz_deinit(mpz_t *z) { + if (z != NULL && !z->fixed_dig) { + m_del(mpz_dig_t, z->dig, z->alloc); + } +} + +#if 0 +these functions are unused + +mpz_t *mpz_zero(void) { + mpz_t *z = m_new_obj(mpz_t); + mpz_init_zero(z); + return z; +} + +mpz_t *mpz_from_int(mp_int_t val) { + mpz_t *z = mpz_zero(); + mpz_set_from_int(z, val); + return z; +} + +mpz_t *mpz_from_ll(long long val, bool is_signed) { + mpz_t *z = mpz_zero(); + mpz_set_from_ll(z, val, is_signed); + return z; +} + +#if MICROPY_PY_BUILTINS_FLOAT +mpz_t *mpz_from_float(mp_float_t val) { + mpz_t *z = mpz_zero(); + mpz_set_from_float(z, val); + return z; +} +#endif + +mpz_t *mpz_from_str(const char *str, size_t len, bool neg, unsigned int base) { + mpz_t *z = mpz_zero(); + mpz_set_from_str(z, str, len, neg, base); + return z; +} +#endif + +static void mpz_free(mpz_t *z) { + if (z != NULL) { + m_del(mpz_dig_t, z->dig, z->alloc); + m_del_obj(mpz_t, z); + } +} + +static void mpz_need_dig(mpz_t *z, size_t need) { + if (need < MIN_ALLOC) { + need = MIN_ALLOC; + } + + if (z->dig == NULL || z->alloc < need) { + // if z has fixed digit buffer there's not much we can do as the caller will + // be expecting a buffer with at least "need" bytes (but it shouldn't happen) + assert(!z->fixed_dig); + z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need); + z->alloc = need; + } +} + +static mpz_t *mpz_clone(const mpz_t *src) { + assert(src->alloc != 0); + mpz_t *z = m_new_obj(mpz_t); + z->neg = src->neg; + z->fixed_dig = 0; + z->alloc = src->alloc; + z->len = src->len; + z->dig = m_new(mpz_dig_t, z->alloc); + memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t)); + return z; +} + +/* sets dest = src + can have dest, src the same +*/ +void mpz_set(mpz_t *dest, const mpz_t *src) { + mpz_need_dig(dest, src->len); + dest->neg = src->neg; + dest->len = src->len; + memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t)); +} + +void mpz_set_from_int(mpz_t *z, mp_int_t val) { + if (val == 0) { + z->neg = 0; + z->len = 0; + return; + } + + mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT); + + mp_uint_t uval; + if (val < 0) { + z->neg = 1; + uval = -val; + } else { + z->neg = 0; + uval = val; + } + + z->len = 0; + while (uval > 0) { + z->dig[z->len++] = uval & DIG_MASK; + uval >>= DIG_SIZE; + } +} + +void mpz_set_from_ll(mpz_t *z, long long val, bool is_signed) { + mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL); + + unsigned long long uval; + if (is_signed && val < 0) { + z->neg = 1; + uval = -(unsigned long long)val; + } else { + z->neg = 0; + uval = val; + } + + z->len = 0; + while (uval > 0) { + z->dig[z->len++] = uval & DIG_MASK; + uval >>= DIG_SIZE; + } +} + +#if MICROPY_PY_BUILTINS_FLOAT +void mpz_set_from_float(mpz_t *z, mp_float_t src) { + mp_float_union_t u = {src}; + z->neg = u.p.sgn; + if (u.p.exp == 0) { + // value == 0 || value < 1 + mpz_set_from_int(z, 0); + } else if (u.p.exp == ((1 << MP_FLOAT_EXP_BITS) - 1)) { + // u.p.frc == 0 indicates inf, else NaN + // should be handled by caller + mpz_set_from_int(z, 0); + } else { + const int adj_exp = (int)u.p.exp - MP_FLOAT_EXP_BIAS; + if (adj_exp < 0) { + // value < 1 , truncates to 0 + mpz_set_from_int(z, 0); + } else if (adj_exp == 0) { + // 1 <= value < 2 , so truncates to 1 + mpz_set_from_int(z, 1); + } else { + // 2 <= value + const int dig_cnt = (adj_exp + 1 + (DIG_SIZE - 1)) / DIG_SIZE; + const unsigned int rem = adj_exp % DIG_SIZE; + int dig_ind, shft; + mp_float_uint_t frc = u.p.frc | ((mp_float_uint_t)1 << MP_FLOAT_FRAC_BITS); + + if (adj_exp < MP_FLOAT_FRAC_BITS) { + shft = 0; + dig_ind = 0; + frc >>= MP_FLOAT_FRAC_BITS - adj_exp; + } else { + shft = (rem - MP_FLOAT_FRAC_BITS) % DIG_SIZE; + dig_ind = (adj_exp - MP_FLOAT_FRAC_BITS) / DIG_SIZE; + } + mpz_need_dig(z, dig_cnt); + z->len = dig_cnt; + if (dig_ind != 0) { + memset(z->dig, 0, dig_ind * sizeof(mpz_dig_t)); + } + if (shft != 0) { + z->dig[dig_ind++] = (frc << shft) & DIG_MASK; + frc >>= DIG_SIZE - shft; + } + #if DIG_SIZE < (MP_FLOAT_FRAC_BITS + 1) + while (dig_ind != dig_cnt) { + z->dig[dig_ind++] = frc & DIG_MASK; + frc >>= DIG_SIZE; + } + #else + if (dig_ind != dig_cnt) { + z->dig[dig_ind] = frc; + } + #endif + } + } +} +#endif + +// returns number of bytes from str that were processed +size_t mpz_set_from_str(mpz_t *z, const char *str, size_t len, bool neg, unsigned int base) { + assert(base <= 36); + + const char *cur = str; + const char *top = str + len; + + mpz_need_dig(z, len * 8 / DIG_SIZE + 1); + + if (neg) { + z->neg = 1; + } else { + z->neg = 0; + } + + z->len = 0; + for (; cur < top; ++cur) { // XXX UTF8 next char + // mp_uint_t v = char_to_numeric(cur#); // XXX UTF8 get char + mp_uint_t v = *cur; + if ('0' <= v && v <= '9') { + v -= '0'; + } else if ('A' <= v && v <= 'Z') { + v -= 'A' - 10; + } else if ('a' <= v && v <= 'z') { + v -= 'a' - 10; + } else { + break; + } + if (v >= base) { + break; + } + z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v); + } + + return cur - str; +} + +void mpz_set_from_bytes(mpz_t *z, bool big_endian, size_t len, const byte *buf) { + int delta = 1; + if (big_endian) { + buf += len - 1; + delta = -1; + } + + mpz_need_dig(z, (len * 8 + DIG_SIZE - 1) / DIG_SIZE); + + mpz_dig_t d = 0; + int num_bits = 0; + z->neg = 0; + z->len = 0; + while (len) { + while (len && num_bits < DIG_SIZE) { + d |= *buf << num_bits; + num_bits += 8; + buf += delta; + len--; + } + z->dig[z->len++] = d & DIG_MASK; + // Need this #if because it's C undefined behavior to do: uint32_t >> 32 + #if DIG_SIZE != 8 && DIG_SIZE != 16 && DIG_SIZE != 32 + d >>= DIG_SIZE; + #else + d = 0; + #endif + num_bits -= DIG_SIZE; + } + + z->len = mpn_remove_trailing_zeros(z->dig, z->dig + z->len); +} + +#if 0 +these functions are unused + +bool mpz_is_pos(const mpz_t *z) { + return z->len > 0 && z->neg == 0; +} + +bool mpz_is_odd(const mpz_t *z) { + return z->len > 0 && (z->dig[0] & 1) != 0; +} + +bool mpz_is_even(const mpz_t *z) { + return z->len == 0 || (z->dig[0] & 1) == 0; +} +#endif + +int mpz_cmp(const mpz_t *z1, const mpz_t *z2) { + int cmp = (int)z2->neg - (int)z1->neg; + if (cmp != 0) { + return cmp; + } + cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len); + if (z1->neg != 0) { + cmp = -cmp; + } + return cmp; +} + +#if 0 +// obsolete +// compares mpz with an integer that fits within DIG_SIZE bits +mp_int_t mpz_cmp_sml_int(const mpz_t *z, mp_int_t sml_int) { + mp_int_t cmp; + if (z->neg == 0) { + if (sml_int < 0) { + return 1; + } + if (sml_int == 0) { + if (z->len == 0) { + return 0; + } + return 1; + } + if (z->len == 0) { + return -1; + } + assert(sml_int < (1 << DIG_SIZE)); + if (z->len != 1) { + return 1; + } + cmp = z->dig[0] - sml_int; + } else { + if (sml_int > 0) { + return -1; + } + if (sml_int == 0) { + if (z->len == 0) { + return 0; + } + return -1; + } + if (z->len == 0) { + return 1; + } + assert(sml_int > -(1 << DIG_SIZE)); + if (z->len != 1) { + return -1; + } + cmp = -z->dig[0] - sml_int; + } + if (cmp < 0) { + return -1; + } + if (cmp > 0) { + return 1; + } + return 0; +} +#endif + +#if 0 +these functions are unused + +/* returns abs(z) +*/ +mpz_t *mpz_abs(const mpz_t *z) { + // TODO: handle case of z->alloc=0 + mpz_t *z2 = mpz_clone(z); + z2->neg = 0; + return z2; +} + +/* returns -z +*/ +mpz_t *mpz_neg(const mpz_t *z) { + // TODO: handle case of z->alloc=0 + mpz_t *z2 = mpz_clone(z); + z2->neg = 1 - z2->neg; + return z2; +} + +/* returns lhs + rhs + can have lhs, rhs the same +*/ +mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) { + mpz_t *z = mpz_zero(); + mpz_add_inpl(z, lhs, rhs); + return z; +} + +/* returns lhs - rhs + can have lhs, rhs the same +*/ +mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) { + mpz_t *z = mpz_zero(); + mpz_sub_inpl(z, lhs, rhs); + return z; +} + +/* returns lhs * rhs + can have lhs, rhs the same +*/ +mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) { + mpz_t *z = mpz_zero(); + mpz_mul_inpl(z, lhs, rhs); + return z; +} + +/* returns lhs ** rhs + can have lhs, rhs the same +*/ +mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) { + mpz_t *z = mpz_zero(); + mpz_pow_inpl(z, lhs, rhs); + return z; +} + +/* computes new integers in quo and rem such that: + quo * rhs + rem = lhs + 0 <= rem < rhs + can have lhs, rhs the same +*/ +void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) { + *quo = mpz_zero(); + *rem = mpz_zero(); + mpz_divmod_inpl(*quo, *rem, lhs, rhs); +} +#endif + +/* computes dest = abs(z) + can have dest, z the same +*/ +void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) { + if (dest != z) { + mpz_set(dest, z); + } + dest->neg = 0; +} + +/* computes dest = -z + can have dest, z the same +*/ +void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) { + if (dest != z) { + mpz_set(dest, z); + } + if (dest->len) { + dest->neg = 1 - dest->neg; + } +} + +/* computes dest = ~z (= -z - 1) + can have dest, z the same +*/ +void mpz_not_inpl(mpz_t *dest, const mpz_t *z) { + if (dest != z) { + mpz_set(dest, z); + } + if (dest->len == 0) { + mpz_need_dig(dest, 1); + dest->dig[0] = 1; + dest->len = 1; + dest->neg = 1; + } else if (dest->neg) { + dest->neg = 0; + mpz_dig_t k = 1; + dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1); + } else { + mpz_need_dig(dest, dest->len + 1); + mpz_dig_t k = 1; + dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1); + dest->neg = 1; + } +} + +/* computes dest = lhs << rhs + can have dest, lhs the same +*/ +void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, mp_uint_t rhs) { + if (lhs->len == 0 || rhs == 0) { + mpz_set(dest, lhs); + } else { + mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE); + dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs); + dest->neg = lhs->neg; + } +} + +/* computes dest = lhs >> rhs + can have dest, lhs the same +*/ +void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, mp_uint_t rhs) { + if (lhs->len == 0 || rhs == 0) { + mpz_set(dest, lhs); + } else { + mpz_need_dig(dest, lhs->len); + dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs); + dest->neg = lhs->neg; + if (dest->neg) { + // arithmetic shift right, rounding to negative infinity + mp_uint_t n_whole = rhs / DIG_SIZE; + mp_uint_t n_part = rhs % DIG_SIZE; + mpz_dig_t round_up = 0; + for (size_t i = 0; i < lhs->len && i < n_whole; i++) { + if (lhs->dig[i] != 0) { + round_up = 1; + break; + } + } + if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) { + round_up = 1; + } + if (round_up) { + if (dest->len == 0) { + // dest == 0, so need to add 1 by hand (answer will be -1) + dest->dig[0] = 1; + dest->len = 1; + } else { + // dest > 0, so can use mpn_add to add 1 + dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1); + } + } + } + } +} + +/* computes dest = lhs + rhs + can have dest, lhs, rhs the same +*/ +void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { + if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) { + const mpz_t *temp = lhs; + lhs = rhs; + rhs = temp; + } + + if (lhs->neg == rhs->neg) { + mpz_need_dig(dest, lhs->len + 1); + dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len); + } else { + mpz_need_dig(dest, lhs->len); + dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len); + } + + dest->neg = lhs->neg & !!dest->len; +} + +/* computes dest = lhs - rhs + can have dest, lhs, rhs the same +*/ +void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { + bool neg = false; + + if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) { + const mpz_t *temp = lhs; + lhs = rhs; + rhs = temp; + neg = true; + } + + if (lhs->neg != rhs->neg) { + mpz_need_dig(dest, lhs->len + 1); + dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len); + } else { + mpz_need_dig(dest, lhs->len); + dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len); + } + + if (dest->len == 0) { + dest->neg = 0; + } else if (neg) { + dest->neg = 1 - lhs->neg; + } else { + dest->neg = lhs->neg; + } +} + +/* computes dest = lhs & rhs + can have dest, lhs, rhs the same +*/ +void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { + // make sure lhs has the most digits + if (lhs->len < rhs->len) { + const mpz_t *temp = lhs; + lhs = rhs; + rhs = temp; + } + + #if MICROPY_OPT_MPZ_BITWISE + + if ((0 == lhs->neg) && (0 == rhs->neg)) { + mpz_need_dig(dest, lhs->len); + dest->len = mpn_and(dest->dig, lhs->dig, rhs->dig, rhs->len); + dest->neg = 0; + } else { + mpz_need_dig(dest, lhs->len + 1); + dest->len = mpn_and_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len, + lhs->neg == rhs->neg, 0 != lhs->neg, 0 != rhs->neg); + dest->neg = lhs->neg & rhs->neg; + } + + #else + + mpz_need_dig(dest, lhs->len + (lhs->neg || rhs->neg)); + dest->len = mpn_and_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len, + (lhs->neg == rhs->neg) ? lhs->neg : 0, lhs->neg, rhs->neg); + dest->neg = lhs->neg & rhs->neg; + + #endif +} + +/* computes dest = lhs | rhs + can have dest, lhs, rhs the same +*/ +void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { + // make sure lhs has the most digits + if (lhs->len < rhs->len) { + const mpz_t *temp = lhs; + lhs = rhs; + rhs = temp; + } + + #if MICROPY_OPT_MPZ_BITWISE + + if ((0 == lhs->neg) && (0 == rhs->neg)) { + mpz_need_dig(dest, lhs->len); + dest->len = mpn_or(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len); + dest->neg = 0; + } else { + mpz_need_dig(dest, lhs->len + 1); + dest->len = mpn_or_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len, + 0 != lhs->neg, 0 != rhs->neg); + dest->neg = 1; + } + + #else + + mpz_need_dig(dest, lhs->len + (lhs->neg || rhs->neg)); + dest->len = mpn_or_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len, + (lhs->neg || rhs->neg), lhs->neg, rhs->neg); + dest->neg = lhs->neg | rhs->neg; + + #endif +} + +/* computes dest = lhs ^ rhs + can have dest, lhs, rhs the same +*/ +void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { + // make sure lhs has the most digits + if (lhs->len < rhs->len) { + const mpz_t *temp = lhs; + lhs = rhs; + rhs = temp; + } + + #if MICROPY_OPT_MPZ_BITWISE + + if (lhs->neg == rhs->neg) { + mpz_need_dig(dest, lhs->len); + if (lhs->neg == 0) { + dest->len = mpn_xor(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len); + } else { + dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len, 0, 0, 0); + } + dest->neg = 0; + } else { + mpz_need_dig(dest, lhs->len + 1); + dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len, 1, + 0 == lhs->neg, 0 == rhs->neg); + dest->neg = 1; + } + + #else + + mpz_need_dig(dest, lhs->len + (lhs->neg || rhs->neg)); + dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len, + (lhs->neg != rhs->neg), 0 == lhs->neg, 0 == rhs->neg); + dest->neg = lhs->neg ^ rhs->neg; + + #endif +} + +/* computes dest = lhs * rhs + can have dest, lhs, rhs the same +*/ +void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { + if (lhs->len == 0 || rhs->len == 0) { + mpz_set_from_int(dest, 0); + return; + } + + mpz_t *temp = NULL; + if (lhs == dest) { + lhs = temp = mpz_clone(lhs); + if (rhs == dest) { + rhs = lhs; + } + } else if (rhs == dest) { + rhs = temp = mpz_clone(rhs); + } + + mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r + memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t)); + dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len); + + if (lhs->neg == rhs->neg) { + dest->neg = 0; + } else { + dest->neg = 1; + } + + mpz_free(temp); +} + +/* computes dest = lhs ** rhs + can have dest, lhs, rhs the same +*/ +void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { + if (lhs->len == 0 || rhs->neg != 0) { + mpz_set_from_int(dest, 0); + return; + } + + if (rhs->len == 0) { + mpz_set_from_int(dest, 1); + return; + } + + mpz_t *x = mpz_clone(lhs); + mpz_t *n = mpz_clone(rhs); + + mpz_set_from_int(dest, 1); + + while (n->len > 0) { + if ((n->dig[0] & 1) != 0) { + mpz_mul_inpl(dest, dest, x); + } + n->len = mpn_shr(n->dig, n->dig, n->len, 1); + if (n->len == 0) { + break; + } + mpz_mul_inpl(x, x, x); + } + + mpz_free(x); + mpz_free(n); +} + +/* computes dest = (lhs ** rhs) % mod + can have dest, lhs, rhs the same; mod can't be the same as dest +*/ +void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t *mod) { + if (lhs->len == 0 || rhs->neg != 0 || (mod->len == 1 && mod->dig[0] == 1)) { + mpz_set_from_int(dest, 0); + return; + } + + mpz_set_from_int(dest, 1); + + if (rhs->len == 0) { + return; + } + + mpz_t *x = mpz_clone(lhs); + mpz_t *n = mpz_clone(rhs); + mpz_t quo; + mpz_init_zero(&quo); + + while (n->len > 0) { + if ((n->dig[0] & 1) != 0) { + mpz_mul_inpl(dest, dest, x); + mpz_divmod_inpl(&quo, dest, dest, mod); + } + n->len = mpn_shr(n->dig, n->dig, n->len, 1); + if (n->len == 0) { + break; + } + mpz_mul_inpl(x, x, x); + mpz_divmod_inpl(&quo, x, x, mod); + } + + mpz_deinit(&quo); + mpz_free(x); + mpz_free(n); +} + +#if 0 +these functions are unused + +/* computes gcd(z1, z2) + based on Knuth's modified gcd algorithm (I think?) + gcd(z1, z2) >= 0 + gcd(0, 0) = 0 + gcd(z, 0) = abs(z) +*/ +mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) { + if (z1->len == 0) { + // TODO: handle case of z2->alloc=0 + mpz_t *a = mpz_clone(z2); + a->neg = 0; + return a; + } else if (z2->len == 0) { + mpz_t *a = mpz_clone(z1); + a->neg = 0; + return a; + } + + mpz_t *a = mpz_clone(z1); + mpz_t *b = mpz_clone(z2); + mpz_t c; + mpz_init_zero(&c); + a->neg = 0; + b->neg = 0; + + for (;;) { + if (mpz_cmp(a, b) < 0) { + if (a->len == 0) { + mpz_free(a); + mpz_deinit(&c); + return b; + } + mpz_t *t = a; + a = b; + b = t; + } + if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0 + break; + } + mpz_set(&c, b); + do { + mpz_add_inpl(&c, &c, &c); + } while (mpz_cmp(&c, a) <= 0); + c.len = mpn_shr(c.dig, c.dig, c.len, 1); + mpz_sub_inpl(a, a, &c); + } + + mpz_deinit(&c); + + if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0 + mpz_free(a); + return b; + } else { + mpz_free(b); + return a; + } +} + +/* computes lcm(z1, z2) + = abs(z1) / gcd(z1, z2) * abs(z2) + lcm(z1, z1) >= 0 + lcm(0, 0) = 0 + lcm(z, 0) = 0 +*/ +mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2) { + if (z1->len == 0 || z2->len == 0) { + return mpz_zero(); + } + + mpz_t *gcd = mpz_gcd(z1, z2); + mpz_t *quo = mpz_zero(); + mpz_t *rem = mpz_zero(); + mpz_divmod_inpl(quo, rem, z1, gcd); + mpz_mul_inpl(rem, quo, z2); + mpz_free(gcd); + mpz_free(quo); + rem->neg = 0; + return rem; +} +#endif + +/* computes new integers in quo and rem such that: + quo * rhs + rem = lhs + 0 <= rem < rhs + can have lhs, rhs the same + assumes rhs != 0 (undefined behaviour if it is) +*/ +void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) { + assert(!mpz_is_zero(rhs)); + + mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary? + memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t)); + dest_quo->neg = 0; + dest_quo->len = 0; + mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary? + mpz_set(dest_rem, lhs); + mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len); + dest_rem->neg &= !!dest_rem->len; + + // check signs and do Python style modulo + if (lhs->neg != rhs->neg) { + dest_quo->neg = !!dest_quo->len; + if (!mpz_is_zero(dest_rem)) { + mpz_t mpzone; + mpz_init_from_int(&mpzone, -1); + mpz_add_inpl(dest_quo, dest_quo, &mpzone); + mpz_add_inpl(dest_rem, dest_rem, rhs); + } + } +} + +#if 0 +these functions are unused + +/* computes floor(lhs / rhs) + can have lhs, rhs the same +*/ +mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) { + mpz_t *quo = mpz_zero(); + mpz_t rem; + mpz_init_zero(&rem); + mpz_divmod_inpl(quo, &rem, lhs, rhs); + mpz_deinit(&rem); + return quo; +} + +/* computes lhs % rhs ( >= 0) + can have lhs, rhs the same +*/ +mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) { + mpz_t quo; + mpz_init_zero(&quo); + mpz_t *rem = mpz_zero(); + mpz_divmod_inpl(&quo, rem, lhs, rhs); + mpz_deinit(&quo); + return rem; +} +#endif + +// must return actual int value if it fits in mp_int_t +mp_int_t mpz_hash(const mpz_t *z) { + mp_uint_t val = 0; + mpz_dig_t *d = z->dig + z->len; + + while (d-- > z->dig) { + val = (val << DIG_SIZE) | *d; + } + + if (z->neg != 0) { + val = -val; + } + + return val; +} + +bool mpz_as_int_checked(const mpz_t *i, mp_int_t *value) { + mp_uint_t val = 0; + mpz_dig_t *d = i->dig + i->len; + + while (d-- > i->dig) { + if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> DIG_SIZE)) { + // will overflow + return false; + } + val = (val << DIG_SIZE) | *d; + } + + if (i->neg != 0) { + val = -val; + } + + *value = val; + return true; +} + +bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) { + if (i->neg != 0) { + // can't represent signed values + return false; + } + + mp_uint_t val = 0; + mpz_dig_t *d = i->dig + i->len; + + while (d-- > i->dig) { + if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) { + // will overflow + return false; + } + val = (val << DIG_SIZE) | *d; + } + + *value = val; + return true; +} + +void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf) { + byte *b = buf; + if (big_endian) { + b += len; + } + mpz_dig_t *zdig = z->dig; + int bits = 0; + mpz_dbl_dig_t d = 0; + mpz_dbl_dig_t carry = 1; + for (size_t zlen = z->len; zlen > 0; --zlen) { + bits += DIG_SIZE; + d = (d << DIG_SIZE) | *zdig++; + for (; bits >= 8; bits -= 8, d >>= 8) { + mpz_dig_t val = d; + if (z->neg) { + val = (~val & 0xff) + carry; + carry = val >> 8; + } + if (big_endian) { + *--b = val; + if (b == buf) { + return; + } + } else { + *b++ = val; + if (b == buf + len) { + return; + } + } + } + } + + // fill remainder of buf with zero/sign extension of the integer + if (big_endian) { + len = b - buf; + } else { + len = buf + len - b; + buf = b; + } + memset(buf, z->neg ? 0xff : 0x00, len); +} + +#if MICROPY_PY_BUILTINS_FLOAT +mp_float_t mpz_as_float(const mpz_t *i) { + mp_float_t val = 0; + mpz_dig_t *d = i->dig + i->len; + + while (d-- > i->dig) { + val = val * DIG_BASE + *d; + } + + if (i->neg != 0) { + val = -val; + } + + return val; +} +#endif + +#if 0 +this function is unused +char *mpz_as_str(const mpz_t *i, unsigned int base) { + char *s = m_new(char, mp_int_format_size(mpz_max_num_bits(i), base, NULL, '\0')); + mpz_as_str_inpl(i, base, NULL, 'a', '\0', s); + return s; +} +#endif + +// assumes enough space in str as calculated by mp_int_format_size +// base must be between 2 and 32 inclusive +// returns length of string, not including null byte +size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, char base_char, char comma, char *str) { + assert(str != NULL); + assert(2 <= base && base <= 32); + + size_t ilen = i->len; + + char *s = str; + if (ilen == 0) { + if (prefix) { + while (*prefix) { + *s++ = *prefix++; + } + } + *s++ = '0'; + *s = '\0'; + return s - str; + } + + // make a copy of mpz digits, so we can do the div/mod calculation + mpz_dig_t *dig = m_new(mpz_dig_t, ilen); + memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t)); + + // convert + char *last_comma = str; + bool done; + do { + mpz_dig_t *d = dig + ilen; + mpz_dbl_dig_t a = 0; + + // compute next remainder + while (--d >= dig) { + a = (a << DIG_SIZE) | *d; + *d = a / base; + a %= base; + } + + // convert to character + a += '0'; + if (a > '9') { + a += base_char - '9' - 1; + } + *s++ = a; + + // check if number is zero + done = true; + for (d = dig; d < dig + ilen; ++d) { + if (*d != 0) { + done = false; + break; + } + } + if (comma && (s - last_comma) == 3) { + *s++ = comma; + last_comma = s; + } + } + while (!done); + + // free the copy of the digits array + m_del(mpz_dig_t, dig, ilen); + + if (prefix) { + const char *p = &prefix[strlen(prefix)]; + while (p > prefix) { + *s++ = *--p; + } + } + if (i->neg != 0) { + *s++ = '-'; + } + + // reverse string + for (char *u = str, *v = s - 1; u < v; ++u, --v) { + char temp = *u; + *u = *v; + *v = temp; + } + + *s = '\0'; // null termination + + return s - str; +} + +#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/mpz.h b/non_catalog_apps/mp_flipper/lib/micropython/py/mpz.h new file mode 100644 index 00000000..d27f5724 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/mpz.h @@ -0,0 +1,154 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_MPZ_H +#define MICROPY_INCLUDED_PY_MPZ_H + +#include + +#include "py/mpconfig.h" +#include "py/misc.h" + +// This mpz module implements arbitrary precision integers. +// +// The storage for each digit is defined by mpz_dig_t. The actual number of +// bits in mpz_dig_t that are used is defined by MPZ_DIG_SIZE. The machine must +// also provide a type that is twice as wide as mpz_dig_t, in both signed and +// unsigned versions. +// +// MPZ_DIG_SIZE can be between 4 and 8*sizeof(mpz_dig_t), but it makes most +// sense to have it as large as possible. If MPZ_DIG_SIZE is not already +// defined then it is auto-detected below, depending on the machine. The types +// are then set based on the value of MPZ_DIG_SIZE (although they can be freely +// changed so long as the constraints mentioned above are met). + +#ifndef MPZ_DIG_SIZE + #if defined(__x86_64__) || defined(_WIN64) +// 64-bit machine, using 32-bit storage for digits + #define MPZ_DIG_SIZE (32) + #else +// default: 32-bit machine, using 16-bit storage for digits + #define MPZ_DIG_SIZE (16) + #endif +#endif + +#if MPZ_DIG_SIZE > 16 +#define MPZ_DBL_DIG_SIZE (64) +typedef uint32_t mpz_dig_t; +typedef uint64_t mpz_dbl_dig_t; +typedef int64_t mpz_dbl_dig_signed_t; +#elif MPZ_DIG_SIZE > 8 +#define MPZ_DBL_DIG_SIZE (32) +typedef uint16_t mpz_dig_t; +typedef uint32_t mpz_dbl_dig_t; +typedef int32_t mpz_dbl_dig_signed_t; +#elif MPZ_DIG_SIZE > 4 +#define MPZ_DBL_DIG_SIZE (16) +typedef uint8_t mpz_dig_t; +typedef uint16_t mpz_dbl_dig_t; +typedef int16_t mpz_dbl_dig_signed_t; +#else +#define MPZ_DBL_DIG_SIZE (8) +typedef uint8_t mpz_dig_t; +typedef uint8_t mpz_dbl_dig_t; +typedef int8_t mpz_dbl_dig_signed_t; +#endif + +#ifdef _WIN64 + #ifdef __MINGW32__ + #define MPZ_LONG_1 1LL + #else + #define MPZ_LONG_1 1i64 + #endif +#else + #define MPZ_LONG_1 1L +#endif + +// these define the maximum storage needed to hold an int or long long +#define MPZ_NUM_DIG_FOR_INT ((sizeof(mp_int_t) * 8 + MPZ_DIG_SIZE - 1) / MPZ_DIG_SIZE) +#define MPZ_NUM_DIG_FOR_LL ((sizeof(long long) * 8 + MPZ_DIG_SIZE - 1) / MPZ_DIG_SIZE) + +typedef struct _mpz_t { + // Zero has neg=0, len=0. Negative zero is not allowed. + size_t neg : 1; + size_t fixed_dig : 1; + size_t alloc : (8 * sizeof(size_t) - 2); + size_t len; + mpz_dig_t *dig; +} mpz_t; + +// convenience macro to declare an mpz with a digit array from the stack, initialised by an integer +#define MPZ_CONST_INT(z, val) mpz_t z; mpz_dig_t z##_digits[MPZ_NUM_DIG_FOR_INT]; mpz_init_fixed_from_int(&z, z_digits, MPZ_NUM_DIG_FOR_INT, val); + +void mpz_init_zero(mpz_t *z); +void mpz_init_from_int(mpz_t *z, mp_int_t val); +void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, size_t dig_alloc, mp_int_t val); +void mpz_deinit(mpz_t *z); + +void mpz_set(mpz_t *dest, const mpz_t *src); +void mpz_set_from_int(mpz_t *z, mp_int_t src); +void mpz_set_from_ll(mpz_t *z, long long i, bool is_signed); +#if MICROPY_PY_BUILTINS_FLOAT +void mpz_set_from_float(mpz_t *z, mp_float_t src); +#endif +size_t mpz_set_from_str(mpz_t *z, const char *str, size_t len, bool neg, unsigned int base); +void mpz_set_from_bytes(mpz_t *z, bool big_endian, size_t len, const byte *buf); + +static inline bool mpz_is_zero(const mpz_t *z) { + return z->len == 0; +} +static inline bool mpz_is_neg(const mpz_t *z) { + return z->neg != 0; +} +int mpz_cmp(const mpz_t *lhs, const mpz_t *rhs); + +void mpz_abs_inpl(mpz_t *dest, const mpz_t *z); +void mpz_neg_inpl(mpz_t *dest, const mpz_t *z); +void mpz_not_inpl(mpz_t *dest, const mpz_t *z); +void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, mp_uint_t rhs); +void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, mp_uint_t rhs); +void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); +void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); +void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); +void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); +void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t *mod); +void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); +void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); +void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); +void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs); + +static inline size_t mpz_max_num_bits(const mpz_t *z) { + return z->len * MPZ_DIG_SIZE; +} +mp_int_t mpz_hash(const mpz_t *z); +bool mpz_as_int_checked(const mpz_t *z, mp_int_t *value); +bool mpz_as_uint_checked(const mpz_t *z, mp_uint_t *value); +void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf); +#if MICROPY_PY_BUILTINS_FLOAT +mp_float_t mpz_as_float(const mpz_t *z); +#endif +size_t mpz_as_str_inpl(const mpz_t *z, unsigned int base, const char *prefix, char base_char, char comma, char *str); + +#endif // MICROPY_INCLUDED_PY_MPZ_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nativeglue.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nativeglue.c new file mode 100644 index 00000000..ba3d93f7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nativeglue.c @@ -0,0 +1,357 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 "py/binary.h" +#include "py/runtime.h" +#include "py/smallint.h" +#include "py/nativeglue.h" +#include "py/gc.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_printf(...) (void)0 +#endif + +#if MICROPY_EMIT_NATIVE + +int mp_native_type_from_qstr(qstr qst) { + switch (qst) { + case MP_QSTR_object: + return MP_NATIVE_TYPE_OBJ; + case MP_QSTR_bool: + return MP_NATIVE_TYPE_BOOL; + case MP_QSTR_int: + return MP_NATIVE_TYPE_INT; + case MP_QSTR_uint: + return MP_NATIVE_TYPE_UINT; + case MP_QSTR_ptr: + return MP_NATIVE_TYPE_PTR; + case MP_QSTR_ptr8: + return MP_NATIVE_TYPE_PTR8; + case MP_QSTR_ptr16: + return MP_NATIVE_TYPE_PTR16; + case MP_QSTR_ptr32: + return MP_NATIVE_TYPE_PTR32; + default: + return -1; + } +} + +// convert a MicroPython object to a valid native value based on type +mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type) { + DEBUG_printf("mp_native_from_obj(%p, " UINT_FMT ")\n", obj, type); + switch (type & 0xf) { + case MP_NATIVE_TYPE_OBJ: + return (mp_uint_t)obj; + case MP_NATIVE_TYPE_BOOL: + return mp_obj_is_true(obj); + case MP_NATIVE_TYPE_INT: + case MP_NATIVE_TYPE_UINT: + return mp_obj_get_int_truncated(obj); + default: { // cast obj to a pointer + mp_buffer_info_t bufinfo; + if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_READ)) { + return (mp_uint_t)bufinfo.buf; + } else { + // assume obj is an integer that represents an address + return mp_obj_get_int_truncated(obj); + } + } + } +} + +#endif + +#if MICROPY_EMIT_MACHINE_CODE + +// convert a native value to a MicroPython object based on type +mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) { + DEBUG_printf("mp_native_to_obj(" UINT_FMT ", " UINT_FMT ")\n", val, type); + switch (type & 0xf) { + case MP_NATIVE_TYPE_OBJ: + return (mp_obj_t)val; + case MP_NATIVE_TYPE_BOOL: + return mp_obj_new_bool(val); + case MP_NATIVE_TYPE_INT: + return mp_obj_new_int(val); + case MP_NATIVE_TYPE_UINT: + return mp_obj_new_int_from_uint(val); + case MP_NATIVE_TYPE_QSTR: + return MP_OBJ_NEW_QSTR(val); + default: // a pointer + // we return just the value of the pointer as an integer + return mp_obj_new_int_from_uint(val); + } +} + +#endif + +#if MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER + +#if !MICROPY_PY_BUILTINS_SET +mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) { + (void)n_args; + (void)items; + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("set unsupported")); +} + +void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item) { + (void)self_in; + (void)item; + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("set unsupported")); +} +#endif + +#if !MICROPY_PY_BUILTINS_SLICE +mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) { + (void)ostart; + (void)ostop; + (void)ostep; + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("slice unsupported")); +} +#endif + +static mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals) { + if (new_globals == NULL) { + // Globals were the originally the same so don't restore them + return NULL; + } + mp_obj_dict_t *old_globals = mp_globals_get(); + if (old_globals == new_globals) { + // Don't set globals if they are the same, and return NULL to indicate this + return NULL; + } + mp_globals_set(new_globals); + return old_globals; +} + +// wrapper that accepts n_args and n_kw in one argument +// (native emitter can only pass at most 3 arguments to a function) +static mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args) { + return mp_call_function_n_kw(fun_in, n_args_kw & 0xff, (n_args_kw >> 8) & 0xff, args); +} + +// wrapper that makes raise obj and raises it +// END_FINALLY opcode requires that we don't raise if o==None +static void mp_native_raise(mp_obj_t o) { + if (o != MP_OBJ_NULL && o != mp_const_none) { + nlr_raise(mp_make_raise_obj(o)); + } +} + +// wrapper that handles iterator buffer +static mp_obj_t mp_native_getiter(mp_obj_t obj, mp_obj_iter_buf_t *iter) { + if (iter == NULL) { + return mp_getiter(obj, NULL); + } else { + obj = mp_getiter(obj, iter); + if (obj != MP_OBJ_FROM_PTR(iter)) { + // Iterator didn't use the stack so indicate that with MP_OBJ_NULL. + iter->base.type = MP_OBJ_NULL; + iter->buf[0] = obj; + } + return NULL; + } +} + +// wrapper that handles iterator buffer +static mp_obj_t mp_native_iternext(mp_obj_iter_buf_t *iter) { + mp_obj_t obj; + if (iter->base.type == MP_OBJ_NULL) { + obj = iter->buf[0]; + } else { + obj = MP_OBJ_FROM_PTR(iter); + } + return mp_iternext(obj); +} + +static bool mp_native_yield_from(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value) { + mp_vm_return_kind_t ret_kind; + nlr_buf_t nlr_buf; + mp_obj_t throw_value = *ret_value; + if (nlr_push(&nlr_buf) == 0) { + if (throw_value != MP_OBJ_NULL) { + send_value = MP_OBJ_NULL; + } + ret_kind = mp_resume(gen, send_value, throw_value, ret_value); + nlr_pop(); + } else { + ret_kind = MP_VM_RETURN_EXCEPTION; + *ret_value = nlr_buf.ret_val; + } + + if (ret_kind == MP_VM_RETURN_YIELD) { + return true; + } else if (ret_kind == MP_VM_RETURN_NORMAL) { + if (*ret_value == MP_OBJ_STOP_ITERATION) { + *ret_value = mp_const_none; + } + } else { + assert(ret_kind == MP_VM_RETURN_EXCEPTION); + if (!mp_obj_exception_match(*ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { + nlr_raise(*ret_value); + } + *ret_value = mp_obj_exception_get_value(*ret_value); + } + + if (throw_value != MP_OBJ_NULL && mp_obj_exception_match(throw_value, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { + nlr_raise(mp_make_raise_obj(throw_value)); + } + + return false; +} + +#if !MICROPY_PY_BUILTINS_FLOAT + +static mp_obj_t mp_obj_new_float_from_f(float f) { + (void)f; + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported")); +} + +static mp_obj_t mp_obj_new_float_from_d(double d) { + (void)d; + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported")); +} + +static float mp_obj_get_float_to_f(mp_obj_t o) { + (void)o; + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported")); +} + +static double mp_obj_get_float_to_d(mp_obj_t o) { + (void)o; + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported")); +} + +#endif + +// these must correspond to the respective enum in nativeglue.h +const mp_fun_table_t mp_fun_table = { + mp_const_none, + mp_const_false, + mp_const_true, + mp_native_from_obj, + mp_native_to_obj, + mp_native_swap_globals, + mp_load_name, + mp_load_global, + mp_load_build_class, + mp_load_attr, + mp_load_method, + mp_load_super_method, + mp_store_name, + mp_store_global, + mp_store_attr, + mp_obj_subscr, + mp_obj_is_true, + mp_unary_op, + mp_binary_op, + mp_obj_new_tuple, + mp_obj_new_list, + mp_obj_new_dict, + mp_obj_new_set, + mp_obj_set_store, + mp_obj_list_append, + mp_obj_dict_store, + mp_make_function_from_proto_fun, + mp_native_call_function_n_kw, + mp_call_method_n_kw, + mp_call_method_n_kw_var, + mp_native_getiter, + mp_native_iternext, + #if MICROPY_NLR_SETJMP + nlr_push_tail, + #else + nlr_push, + #endif + nlr_pop, + mp_native_raise, + mp_import_name, + mp_import_from, + mp_import_all, + mp_obj_new_slice, + mp_unpack_sequence, + mp_unpack_ex, + mp_delete_name, + mp_delete_global, + mp_obj_new_closure, + mp_arg_check_num_sig, + mp_setup_code_state_native, + mp_small_int_floor_divide, + mp_small_int_modulo, + mp_native_yield_from, + #if MICROPY_NLR_SETJMP + setjmp, + #else + NULL, + #endif + // Additional entries for dynamic runtime, starts at index 50 + memset, + memmove, + gc_realloc, + mp_printf, + mp_vprintf, + mp_raise_msg, + mp_obj_get_type, + mp_obj_new_str, + mp_obj_new_bytes, + mp_obj_new_bytearray_by_ref, + mp_obj_new_float_from_f, + mp_obj_new_float_from_d, + mp_obj_get_float_to_f, + mp_obj_get_float_to_d, + mp_get_buffer, + mp_get_stream_raise, + mp_binary_get_size, + mp_binary_get_val_array, + mp_binary_set_val_array, + &mp_plat_print, + &mp_type_type, + &mp_type_str, + &mp_type_list, + &mp_type_dict, + &mp_type_fun_builtin_0, + &mp_type_fun_builtin_1, + &mp_type_fun_builtin_2, + &mp_type_fun_builtin_3, + &mp_type_fun_builtin_var, + &mp_stream_read_obj, + &mp_stream_readinto_obj, + &mp_stream_unbuffered_readline_obj, + &mp_stream_write_obj, +}; + +#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER + +const int mp_fun_table; + +#endif // MICROPY_EMIT_NATIVE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nativeglue.h b/non_catalog_apps/mp_flipper/lib/micropython/py/nativeglue.h new file mode 100644 index 00000000..1fa85933 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nativeglue.h @@ -0,0 +1,188 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_NATIVEGLUE_H +#define MICROPY_INCLUDED_PY_NATIVEGLUE_H + +#include +#include "py/obj.h" +#include "py/persistentcode.h" +#include "py/stream.h" + +typedef enum { + MP_F_CONST_NONE_OBJ = 0, + MP_F_CONST_FALSE_OBJ, + MP_F_CONST_TRUE_OBJ, + MP_F_CONVERT_OBJ_TO_NATIVE, + MP_F_CONVERT_NATIVE_TO_OBJ, + MP_F_NATIVE_SWAP_GLOBALS, + MP_F_LOAD_NAME, + MP_F_LOAD_GLOBAL, + MP_F_LOAD_BUILD_CLASS, + MP_F_LOAD_ATTR, + MP_F_LOAD_METHOD, + MP_F_LOAD_SUPER_METHOD, + MP_F_STORE_NAME, + MP_F_STORE_GLOBAL, + MP_F_STORE_ATTR, + MP_F_OBJ_SUBSCR, + MP_F_OBJ_IS_TRUE, + MP_F_UNARY_OP, + MP_F_BINARY_OP, + MP_F_BUILD_TUPLE, + MP_F_BUILD_LIST, + MP_F_BUILD_MAP, + MP_F_BUILD_SET, + MP_F_STORE_SET, + MP_F_LIST_APPEND, + MP_F_STORE_MAP, + MP_F_MAKE_FUNCTION_FROM_PROTO_FUN, + MP_F_NATIVE_CALL_FUNCTION_N_KW, + MP_F_CALL_METHOD_N_KW, + MP_F_CALL_METHOD_N_KW_VAR, + MP_F_NATIVE_GETITER, + MP_F_NATIVE_ITERNEXT, + MP_F_NLR_PUSH, + MP_F_NLR_POP, + MP_F_NATIVE_RAISE, + MP_F_IMPORT_NAME, + MP_F_IMPORT_FROM, + MP_F_IMPORT_ALL, + MP_F_NEW_SLICE, + MP_F_UNPACK_SEQUENCE, + MP_F_UNPACK_EX, + MP_F_DELETE_NAME, + MP_F_DELETE_GLOBAL, + MP_F_NEW_CLOSURE, + MP_F_ARG_CHECK_NUM_SIG, + MP_F_SETUP_CODE_STATE, + MP_F_SMALL_INT_FLOOR_DIVIDE, + MP_F_SMALL_INT_MODULO, + MP_F_NATIVE_YIELD_FROM, + MP_F_SETJMP, + MP_F_NUMBER_OF, +} mp_fun_kind_t; + +typedef struct _mp_fun_table_t { + mp_const_obj_t const_none; + mp_const_obj_t const_false; + mp_const_obj_t const_true; + mp_uint_t (*native_from_obj)(mp_obj_t obj, mp_uint_t type); + mp_obj_t (*native_to_obj)(mp_uint_t val, mp_uint_t type); + mp_obj_dict_t *(*swap_globals)(mp_obj_dict_t * new_globals); + mp_obj_t (*load_name)(qstr qst); + mp_obj_t (*load_global)(qstr qst); + mp_obj_t (*load_build_class)(void); + mp_obj_t (*load_attr)(mp_obj_t base, qstr attr); + void (*load_method)(mp_obj_t base, qstr attr, mp_obj_t *dest); + void (*load_super_method)(qstr attr, mp_obj_t *dest); + void (*store_name)(qstr qst, mp_obj_t obj); + void (*store_global)(qstr qst, mp_obj_t obj); + void (*store_attr)(mp_obj_t base, qstr attr, mp_obj_t val); + mp_obj_t (*obj_subscr)(mp_obj_t base, mp_obj_t index, mp_obj_t val); + bool (*obj_is_true)(mp_obj_t arg); + mp_obj_t (*unary_op)(mp_unary_op_t op, mp_obj_t arg); + mp_obj_t (*binary_op)(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs); + mp_obj_t (*new_tuple)(size_t n, const mp_obj_t *items); + mp_obj_t (*new_list)(size_t n, mp_obj_t *items); + mp_obj_t (*new_dict)(size_t n_args); + mp_obj_t (*new_set)(size_t n_args, mp_obj_t *items); + void (*set_store)(mp_obj_t self_in, mp_obj_t item); + mp_obj_t (*list_append)(mp_obj_t self_in, mp_obj_t arg); + mp_obj_t (*dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); + mp_obj_t (*make_function_from_proto_fun)(mp_proto_fun_t proto_fun, const mp_module_context_t *cm, const mp_obj_t *def_args); + mp_obj_t (*call_function_n_kw)(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args); + mp_obj_t (*call_method_n_kw)(size_t n_args, size_t n_kw, const mp_obj_t *args); + mp_obj_t (*call_method_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args); + mp_obj_t (*getiter)(mp_obj_t obj, mp_obj_iter_buf_t *iter); + mp_obj_t (*iternext)(mp_obj_iter_buf_t *iter); + unsigned int (*nlr_push)(nlr_buf_t *); + void (*nlr_pop)(void); + void (*raise)(mp_obj_t o); + mp_obj_t (*import_name)(qstr name, mp_obj_t fromlist, mp_obj_t level); + mp_obj_t (*import_from)(mp_obj_t module, qstr name); + void (*import_all)(mp_obj_t module); + mp_obj_t (*new_slice)(mp_obj_t start, mp_obj_t stop, mp_obj_t step); + void (*unpack_sequence)(mp_obj_t seq, size_t num, mp_obj_t *items); + void (*unpack_ex)(mp_obj_t seq, size_t num, mp_obj_t *items); + void (*delete_name)(qstr qst); + void (*delete_global)(qstr qst); + mp_obj_t (*new_closure)(mp_obj_t fun, size_t n_closed_over, const mp_obj_t *closed); + void (*arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig); + void (*setup_code_state_native)(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); + mp_int_t (*small_int_floor_divide)(mp_int_t num, mp_int_t denom); + mp_int_t (*small_int_modulo)(mp_int_t dividend, mp_int_t divisor); + bool (*yield_from)(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value); + void *setjmp_; + // Additional entries for dynamic runtime, starts at index 50 + void *(*memset_)(void *s, int c, size_t n); + void *(*memmove_)(void *dest, const void *src, size_t n); + void *(*realloc_)(void *ptr, size_t n_bytes, bool allow_move); + int (*printf_)(const mp_print_t *print, const char *fmt, ...); + int (*vprintf_)(const mp_print_t *print, const char *fmt, va_list args); + #if defined(__GNUC__) + NORETURN // Only certain compilers support no-return attributes in function pointer declarations + #endif + void (*raise_msg)(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); + const mp_obj_type_t *(*obj_get_type)(mp_const_obj_t o_in); + mp_obj_t (*obj_new_str)(const char *data, size_t len); + mp_obj_t (*obj_new_bytes)(const byte *data, size_t len); + mp_obj_t (*obj_new_bytearray_by_ref)(size_t n, void *items); + mp_obj_t (*obj_new_float_from_f)(float f); + mp_obj_t (*obj_new_float_from_d)(double d); + float (*obj_get_float_to_f)(mp_obj_t o); + double (*obj_get_float_to_d)(mp_obj_t o); + bool (*get_buffer)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); + const mp_stream_p_t *(*get_stream_raise)(mp_obj_t self_in, int flags); + size_t (*binary_get_size)(char struct_type, char val_type, size_t *palign); + mp_obj_t (*binary_get_val_array)(char typecode, void *p, size_t index); + void (*binary_set_val_array)(char typecode, void *p, size_t index, mp_obj_t val_in); + const mp_print_t *plat_print; + // The following entries start at index 70 and are referenced by tools-mpy_ld.py, + // see constant MP_FUN_TABLE_MP_TYPE_TYPE_OFFSET. + const mp_obj_type_t *type_type; + const mp_obj_type_t *type_str; + const mp_obj_type_t *type_list; + const mp_obj_type_t *type_dict; + const mp_obj_type_t *type_fun_builtin_0; + const mp_obj_type_t *type_fun_builtin_1; + const mp_obj_type_t *type_fun_builtin_2; + const mp_obj_type_t *type_fun_builtin_3; + const mp_obj_type_t *type_fun_builtin_var; + const mp_obj_fun_builtin_var_t *stream_read_obj; + const mp_obj_fun_builtin_var_t *stream_readinto_obj; + const mp_obj_fun_builtin_var_t *stream_unbuffered_readline_obj; + const mp_obj_fun_builtin_var_t *stream_write_obj; +} mp_fun_table_t; + +#if (MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER) || MICROPY_ENABLE_DYNRUNTIME +extern const mp_fun_table_t mp_fun_table; +#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER +// In dynamic-compiler mode eliminate dependency on entries in mp_fun_table. +// This only needs to be an independent pointer, content doesn't matter. +extern const int mp_fun_table; +#endif + +#endif // MICROPY_INCLUDED_PY_NATIVEGLUE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlr.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlr.c new file mode 100644 index 00000000..7ab0c095 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlr.c @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2023 Damien P. George + * + * 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 "py/mpstate.h" + +#if !MICROPY_NLR_SETJMP +// When not using setjmp, nlr_push_tail is called from inline asm so needs special care +#if MICROPY_NLR_X86 && MICROPY_NLR_OS_WINDOWS +// On these 32-bit platforms make sure nlr_push_tail doesn't have a leading underscore +unsigned int nlr_push_tail(nlr_buf_t *nlr) asm ("nlr_push_tail"); +#else +// LTO can't see inside inline asm functions so explicitly mark nlr_push_tail as used +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); +#endif +#endif + +unsigned int nlr_push_tail(nlr_buf_t *nlr) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); + *top = nlr; + return 0; // normal return +} + +void nlr_pop(void) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + *top = (*top)->prev; +} + +void nlr_push_jump_callback(nlr_jump_callback_node_t *node, nlr_jump_callback_fun_t fun) { + nlr_jump_callback_node_t **top = &MP_STATE_THREAD(nlr_jump_callback_top); + node->prev = *top; + node->fun = fun; + *top = node; +} + +void nlr_pop_jump_callback(bool run_callback) { + nlr_jump_callback_node_t **top = &MP_STATE_THREAD(nlr_jump_callback_top); + nlr_jump_callback_node_t *cur = *top; + *top = (*top)->prev; + if (run_callback) { + cur->fun(cur); + } +} + +// This function pops and runs all callbacks that were registered after `nlr` +// was pushed (via nlr_push). It assumes: +// - a descending C stack, +// - that all nlr_jump_callback_node_t's in the linked-list pointed to by +// nlr_jump_callback_top are on the C stack +// It works by popping each node in turn until the next node is NULL or above +// the `nlr` pointer on the C stack (and so pushed before `nlr` was pushed). +void nlr_call_jump_callbacks(nlr_buf_t *nlr) { + nlr_jump_callback_node_t **top = &MP_STATE_THREAD(nlr_jump_callback_top); + while (*top != NULL && (void *)*top < (void *)nlr) { + nlr_pop_jump_callback(true); + } +} + +#if MICROPY_ENABLE_VM_ABORT +NORETURN void nlr_jump_abort(void) { + MP_STATE_THREAD(nlr_top) = MP_STATE_VM(nlr_abort); + nlr_jump(NULL); +} +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlr.h b/non_catalog_apps/mp_flipper/lib/micropython/py/nlr.h new file mode 100644 index 00000000..62972dba --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlr.h @@ -0,0 +1,213 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2023 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_NLR_H +#define MICROPY_INCLUDED_PY_NLR_H + +// non-local return +// exception handling, basically a stack of setjmp/longjmp buffers + +#include +#include +#include + +#include "py/mpconfig.h" + +#define MICROPY_NLR_NUM_REGS_X86 (6) +#define MICROPY_NLR_NUM_REGS_X64 (8) +#define MICROPY_NLR_NUM_REGS_X64_WIN (10) +#define MICROPY_NLR_NUM_REGS_ARM_THUMB (10) +#define MICROPY_NLR_NUM_REGS_ARM_THUMB_FP (10 + 6) +#define MICROPY_NLR_NUM_REGS_AARCH64 (13) +#define MICROPY_NLR_NUM_REGS_MIPS (13) +#define MICROPY_NLR_NUM_REGS_XTENSA (10) +#define MICROPY_NLR_NUM_REGS_XTENSAWIN (17) + +// *FORMAT-OFF* + +// If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch +#if !MICROPY_NLR_SETJMP +// A lot of nlr-related things need different treatment on Windows +#if defined(_WIN32) || defined(__CYGWIN__) +#define MICROPY_NLR_OS_WINDOWS 1 +#else +#define MICROPY_NLR_OS_WINDOWS 0 +#endif +#if defined(__i386__) + #define MICROPY_NLR_X86 (1) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X86) +#elif defined(__x86_64__) + #define MICROPY_NLR_X64 (1) + #if MICROPY_NLR_OS_WINDOWS + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X64_WIN) + #else + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X64) + #endif +#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) + #define MICROPY_NLR_THUMB (1) + #if defined(__SOFTFP__) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_ARM_THUMB) + #else + // With hardware FP registers s16-s31 are callee save so in principle + // should be saved and restored by the NLR code. gcc only uses s16-s21 + // so only save/restore those as an optimisation. + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_ARM_THUMB_FP) + #endif +#elif defined(__aarch64__) + #define MICROPY_NLR_AARCH64 (1) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_AARCH64) +#elif defined(__xtensa__) + #define MICROPY_NLR_XTENSA (1) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_XTENSA) +#elif defined(__powerpc__) + #define MICROPY_NLR_POWERPC (1) + // this could be less but using 128 for safety + #define MICROPY_NLR_NUM_REGS (128) +#elif defined(__mips__) + #define MICROPY_NLR_MIPS (1) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_MIPS) +#else + #define MICROPY_NLR_SETJMP (1) + //#warning "No native NLR support for this arch, using setjmp implementation" +#endif +#endif + +// *FORMAT-ON* + +#if MICROPY_NLR_SETJMP +#include +#endif + +typedef struct _nlr_buf_t nlr_buf_t; +struct _nlr_buf_t { + // The entries in this struct must all be machine word size. + + // Pointer to the previous nlr_buf_t in the chain. + // Or NULL if it's the top-level one. + nlr_buf_t *prev; + + // The exception that is being raised: + // - NULL means the jump is because of a VM abort (only if MICROPY_ENABLE_VM_ABORT enabled) + // - otherwise it's always a concrete object (an exception instance) + void *ret_val; + + #if MICROPY_NLR_SETJMP + jmp_buf jmpbuf; + #else + void *regs[MICROPY_NLR_NUM_REGS]; + #endif + + #if MICROPY_ENABLE_PYSTACK + void *pystack; + #endif +}; + +typedef void (*nlr_jump_callback_fun_t)(void *ctx); + +typedef struct _nlr_jump_callback_node_t nlr_jump_callback_node_t; + +struct _nlr_jump_callback_node_t { + nlr_jump_callback_node_t *prev; + nlr_jump_callback_fun_t fun; +}; + +// Helper macros to save/restore the pystack state +#if MICROPY_ENABLE_PYSTACK +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur) +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack +#else +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf +#endif + +// Helper macro to use at the start of a specific nlr_jump implementation +#define MP_NLR_JUMP_HEAD(val, top) \ + nlr_buf_t **_top_ptr = &MP_STATE_THREAD(nlr_top); \ + nlr_buf_t *top = *_top_ptr; \ + if (top == NULL) { \ + nlr_jump_fail(val); \ + } \ + top->ret_val = val; \ + nlr_call_jump_callbacks(top); \ + MP_NLR_RESTORE_PYSTACK(top); \ + *_top_ptr = top->prev; \ + +#if MICROPY_NLR_SETJMP +// nlr_push() must be defined as a macro, because "The stack context will be +// invalidated if the function which called setjmp() returns." +// For this case it is safe to call nlr_push_tail() first. +#define nlr_push(buf) (nlr_push_tail(buf), setjmp((buf)->jmpbuf)) +#else +unsigned int nlr_push(nlr_buf_t *); +#endif + +unsigned int nlr_push_tail(nlr_buf_t *top); +void nlr_pop(void); +NORETURN void nlr_jump(void *val); + +#if MICROPY_ENABLE_VM_ABORT +#define nlr_set_abort(buf) MP_STATE_VM(nlr_abort) = buf +#define nlr_get_abort() MP_STATE_VM(nlr_abort) +NORETURN void nlr_jump_abort(void); +#endif + +// This must be implemented by a port. It's called by nlr_jump +// if no nlr buf has been pushed. It must not return, but rather +// should bail out with a fatal error. +NORETURN void nlr_jump_fail(void *val); + +// use nlr_raise instead of nlr_jump so that debugging is easier +#ifndef MICROPY_DEBUG_NLR +#define nlr_raise(val) nlr_jump(MP_OBJ_TO_PTR(val)) +#else + +#define nlr_raise(val) \ + do { \ + void *_val = MP_OBJ_TO_PTR(val); \ + assert(_val != NULL); \ + assert(mp_obj_is_exception_instance(val)); \ + nlr_jump(_val); \ + } while (0) + +#if !MICROPY_NLR_SETJMP +#define nlr_push(val) \ + assert(MP_STATE_THREAD(nlr_top) != val), nlr_push(val) +#endif + +#endif + +// Push a callback on to the linked-list of NLR jump callbacks. The `node` pointer must +// be on the C stack. The `fun` callback will be executed if an NLR jump is taken which +// unwinds the C stack through this `node`. +void nlr_push_jump_callback(nlr_jump_callback_node_t *node, nlr_jump_callback_fun_t fun); + +// Pop a callback from the linked-list of NLR jump callbacks. The corresponding function +// will be called if `run_callback` is true. +void nlr_pop_jump_callback(bool run_callback); + +// Pop and call all NLR jump callbacks that were registered after `nlr` buffer was pushed. +void nlr_call_jump_callbacks(nlr_buf_t *nlr); + +#endif // MICROPY_INCLUDED_PY_NLR_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlraarch64.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlraarch64.c new file mode 100644 index 00000000..d6d87ebc --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlraarch64.c @@ -0,0 +1,84 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Yonatan Goldschmidt + * + * 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 "py/mpstate.h" // needed for NLR defs + +#if MICROPY_NLR_AARCH64 + +// AArch64 callee-saved registers are x19-x29. +// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A64) + +// Implemented purely as inline assembly; inside a function, we have to deal with undoing the prologue, restoring +// SP and LR. This way, we don't. +__asm( + #if defined(__APPLE__) && defined(__MACH__) + "_nlr_push: \n" + ".global _nlr_push \n" + #else + "nlr_push: \n" + ".global nlr_push \n" + #endif + "mov x9, sp \n" + "stp lr, x9, [x0, #16]\n" // 16 == offsetof(nlr_buf_t, regs) + "stp x19, x20, [x0, #32]\n" + "stp x21, x22, [x0, #48]\n" + "stp x23, x24, [x0, #64]\n" + "stp x25, x26, [x0, #80]\n" + "stp x27, x28, [x0, #96]\n" + "str x29, [x0, #112]\n" + #if defined(__APPLE__) && defined(__MACH__) + "b _nlr_push_tail \n" // do the rest in C + #else + "b nlr_push_tail \n" // do the rest in C + #endif + ); + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + MP_STATIC_ASSERT(offsetof(nlr_buf_t, regs) == 16); // asm assumes it + + __asm volatile ( + "mov x0, %0 \n" + "ldr x29, [x0, #112]\n" + "ldp x27, x28, [x0, #96]\n" + "ldp x25, x26, [x0, #80]\n" + "ldp x23, x24, [x0, #64]\n" + "ldp x21, x22, [x0, #48]\n" + "ldp x19, x20, [x0, #32]\n" + "ldp lr, x9, [x0, #16]\n" // 16 == offsetof(nlr_buf_t, regs) + "mov sp, x9 \n" + "mov x0, #1 \n" // non-local return + "ret \n" + : + : "r" (top) + : "memory" + ); + + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_AARCH64 diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlrmips.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrmips.c new file mode 100644 index 00000000..cba52b16 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrmips.c @@ -0,0 +1,86 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 Damien P. George + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_MIPS + +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); + +__asm( + ".globl nlr_push \n" + "nlr_push: \n" + ".ent nlr_push \n" + ".frame $29, 0, $31 \n" + ".set noreorder \n" + ".cpload $25 \n" + ".set reorder \n" + "sw $31, 8($4) \n" /* is the offset of regs in nlr_buf_t */ + "sw $30, 12($4) \n" + "sw $29, 16($4) \n" + "sw $28, 20($4) \n" + "sw $23, 24($4) \n" + "sw $22, 28($4) \n" + "sw $21, 32($4) \n" + "sw $20, 36($4) \n" + "sw $19, 40($4) \n" + "sw $18, 44($4) \n" + "sw $17, 48($4) \n" + "sw $16, 52($4) \n" +#ifdef __pic__ + "la $25, nlr_push_tail \n" +#endif + "j nlr_push_tail \n" + ".end nlr_push \n" + ); + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + __asm( + "move $4, %0 \n" + "lw $31, 8($4) \n" + "lw $30, 12($4) \n" + "lw $29, 16($4) \n" + "lw $28, 20($4) \n" + "lw $23, 24($4) \n" + "lw $22, 28($4) \n" + "lw $21, 32($4) \n" + "lw $20, 36($4) \n" + "lw $19, 40($4) \n" + "lw $18, 44($4) \n" + "lw $17, 48($4) \n" + "lw $16, 52($4) \n" + "lui $2,1 \n" // set return value 1 + "j $31 \n" + "nop \n" + : + : "r" (top) + : "memory" + ); + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_MIPS diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlrpowerpc.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrpowerpc.c new file mode 100644 index 00000000..8a69fe1e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrpowerpc.c @@ -0,0 +1,214 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019, Michael Neuling, IBM Corporation. + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_POWERPC + +#undef nlr_push + +// Saving all ABI non-vol registers here + +#ifdef __LP64__ + +unsigned int nlr_push(nlr_buf_t *nlr) { + + __asm__ volatile ( + "li 4, 0x4eed ; " // Store canary + "std 4, 0x00(%0) ;" + "std 0, 0x08(%0) ;" + "std 1, 0x10(%0) ;" + "std 2, 0x18(%0) ;" + "std 14, 0x20(%0) ;" + "std 15, 0x28(%0) ;" + "std 16, 0x30(%0) ;" + "std 17, 0x38(%0) ;" + "std 18, 0x40(%0) ;" + "std 19, 0x48(%0) ;" + "std 20, 0x50(%0) ;" + "std 21, 0x58(%0) ;" + "std 22, 0x60(%0) ;" + "std 23, 0x68(%0) ;" + "std 24, 0x70(%0) ;" + "std 25, 0x78(%0) ;" + "std 26, 0x80(%0) ;" + "std 27, 0x88(%0) ;" + "std 28, 0x90(%0) ;" + "std 29, 0x98(%0) ;" + "std 30, 0xA0(%0) ;" + "std 31, 0xA8(%0) ;" + + "mfcr 4 ; " + "std 4, 0xB0(%0) ;" + "mflr 4 ;" + "std 4, 0xB8(%0) ;" + "li 4, nlr_push_tail@l ;" + "oris 4, 4, nlr_push_tail@h ;" + "mtctr 4 ;" + "mr 3, %1 ; " + "bctr ;" + : + : "r" (&nlr->regs), "r" (nlr) + : + ); + + return 0; +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm__ volatile ( + "ld 3, 0x0(%0) ;" + "cmpdi 3, 0x4eed ; " // Check canary + "bne . ; " + "ld 0, 0x08(%0) ;" + "ld 1, 0x10(%0) ;" + "ld 2, 0x18(%0) ;" + "ld 14, 0x20(%0) ;" + "ld 15, 0x28(%0) ;" + "ld 16, 0x30(%0) ;" + "ld 17, 0x38(%0) ;" + "ld 18, 0x40(%0) ;" + "ld 19, 0x48(%0) ;" + "ld 20, 0x50(%0) ;" + "ld 21, 0x58(%0) ;" + "ld 22, 0x60(%0) ;" + "ld 23, 0x68(%0) ;" + "ld 24, 0x70(%0) ;" + "ld 25, 0x78(%0) ;" + "ld 26, 0x80(%0) ;" + "ld 27, 0x88(%0) ;" + "ld 28, 0x90(%0) ;" + "ld 29, 0x98(%0) ;" + "ld 30, 0xA0(%0) ;" + "ld 31, 0xA8(%0) ;" + "ld 3, 0xB0(%0) ;" + "mtcr 3 ;" + "ld 3, 0xB8(%0) ;" + "mtlr 3 ; " + "li 3, 1;" + "blr ;" + : + : "r" (&top->regs) + : "memory" + ); + + MP_UNREACHABLE; +} + +#else +// Saving all ABI non-vol registers here + +unsigned int nlr_push(nlr_buf_t *nlr) { + + __asm__ volatile ( + "li 4, 0x4eed ; " // Store canary + "stw 4, 0x00(%0) ;" + "stw 0, 0x04(%0) ;" + "stw 1, 0x08(%0) ;" + "stw 2, 0x0c(%0) ;" + "stw 14, 0x10(%0) ;" + "stw 15, 0x14(%0) ;" + "stw 16, 0x18(%0) ;" + "stw 17, 0x1c(%0) ;" + "stw 18, 0x20(%0) ;" + "stw 19, 0x24(%0) ;" + "stw 20, 0x28(%0) ;" + "stw 21, 0x2c(%0) ;" + "stw 22, 0x30(%0) ;" + "stw 23, 0x34(%0) ;" + "stw 24, 0x38(%0) ;" + "stw 25, 0x3c(%0) ;" + "stw 26, 0x40(%0) ;" + "stw 27, 0x44(%0) ;" + "stw 28, 0x48(%0) ;" + "stw 29, 0x4c(%0) ;" + "stw 30, 0x50(%0) ;" + "stw 31, 0x54(%0) ;" + + "mfcr 4 ; " + "stw 4, 0x58(%0) ;" + "mflr 4 ;" + "stw 4, 0x5c(%0) ;" + "li 4, nlr_push_tail@l ;" + "oris 4, 4, nlr_push_tail@h ;" + "mtctr 4 ;" + "mr 3, %1 ; " + "bctr ;" + : + : "r" (&nlr->regs), "r" (nlr) + : + ); + + return 0; +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm__ volatile ( + "l 3, 0x0(%0) ;" + "cmpdi 3, 0x4eed ; " // Check canary + "bne . ; " + "l 0, 0x04(%0) ;" + "l 1, 0x08(%0) ;" + "l 2, 0x0c(%0) ;" + "l 14, 0x10(%0) ;" + "l 15, 0x14(%0) ;" + "l 16, 0x18(%0) ;" + "l 17, 0x1c(%0) ;" + "l 18, 0x20(%0) ;" + "l 19, 0x24(%0) ;" + "l 20, 0x28(%0) ;" + "l 21, 0x2c(%0) ;" + "l 22, 0x30(%0) ;" + "l 23, 0x34(%0) ;" + "l 24, 0x38(%0) ;" + "l 25, 0x3c(%0) ;" + "l 26, 0x40(%0) ;" + "l 27, 0x44(%0) ;" + "l 28, 0x48(%0) ;" + "l 29, 0x4c(%0) ;" + "l 30, 0x50(%0) ;" + "l 31, 0x54(%0) ;" + "l 3, 0x58(%0) ;" + "mtcr 3 ;" + "l 3, 0x5c(%0) ;" + "mtlr 3 ; " + "li 3, 1;" + "blr ;" + : + : "r" (&top->regs) + : "memory" + ); + + MP_UNREACHABLE; +} + +#endif // __LP64__ + +#endif // MICROPY_NLR_POWERPC diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlrsetjmp.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrsetjmp.c new file mode 100644 index 00000000..73fbe812 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrsetjmp.c @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2023 Damien P. George + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_SETJMP + +void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top); + longjmp(top->jmpbuf, 1); +} + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlrthumb.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrthumb.c new file mode 100644 index 00000000..a22c5df5 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrthumb.c @@ -0,0 +1,141 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 Damien P. George + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_THUMB + +#undef nlr_push + +// We only need the functions here if we are on arm/thumb, and we are not +// using setjmp/longjmp. +// +// For reference, arm/thumb callee save regs are: +// r4-r11, r13=sp + +__attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { + + __asm volatile ( + "str r4, [r0, #12] \n" // store r4 into nlr_buf + "str r5, [r0, #16] \n" // store r5 into nlr_buf + "str r6, [r0, #20] \n" // store r6 into nlr_buf + "str r7, [r0, #24] \n" // store r7 into nlr_buf + + #if !defined(__thumb2__) + "mov r1, r8 \n" + "str r1, [r0, #28] \n" // store r8 into nlr_buf + "mov r1, r9 \n" + "str r1, [r0, #32] \n" // store r9 into nlr_buf + "mov r1, r10 \n" + "str r1, [r0, #36] \n" // store r10 into nlr_buf + "mov r1, r11 \n" + "str r1, [r0, #40] \n" // store r11 into nlr_buf + "mov r1, r13 \n" + "str r1, [r0, #44] \n" // store r13=sp into nlr_buf + "mov r1, lr \n" + "str r1, [r0, #8] \n" // store lr into nlr_buf + #else + "str r8, [r0, #28] \n" // store r8 into nlr_buf + "str r9, [r0, #32] \n" // store r9 into nlr_buf + "str r10, [r0, #36] \n" // store r10 into nlr_buf + "str r11, [r0, #40] \n" // store r11 into nlr_buf + "str r13, [r0, #44] \n" // store r13=sp into nlr_buf + #if MICROPY_NLR_NUM_REGS == 16 + "vstr d8, [r0, #48] \n" // store s16-s17 into nlr_buf + "vstr d9, [r0, #56] \n" // store s18-s19 into nlr_buf + "vstr d10, [r0, #64] \n" // store s20-s21 into nlr_buf + #endif + "str lr, [r0, #8] \n" // store lr into nlr_buf + #endif + + #if !defined(__thumb2__) + "ldr r1, nlr_push_tail_var \n" + "bx r1 \n" // do the rest in C + ".align 2 \n" + "nlr_push_tail_var: .word nlr_push_tail \n" + #else + #if defined(__APPLE__) || defined(__MACH__) + "b _nlr_push_tail \n" // do the rest in C + #else + "b nlr_push_tail \n" // do the rest in C + #endif + #endif + ); + + #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) + // Older versions of gcc give an error when naked functions don't return a value + // Additionally exclude Clang as it also defines __GNUC__ but doesn't need this statement + return 0; + #endif +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm volatile ( + "mov r0, %0 \n" // r0 points to nlr_buf + "ldr r4, [r0, #12] \n" // load r4 from nlr_buf + "ldr r5, [r0, #16] \n" // load r5 from nlr_buf + "ldr r6, [r0, #20] \n" // load r6 from nlr_buf + "ldr r7, [r0, #24] \n" // load r7 from nlr_buf + + #if !defined(__thumb2__) + "ldr r1, [r0, #28] \n" // load r8 from nlr_buf + "mov r8, r1 \n" + "ldr r1, [r0, #32] \n" // load r9 from nlr_buf + "mov r9, r1 \n" + "ldr r1, [r0, #36] \n" // load r10 from nlr_buf + "mov r10, r1 \n" + "ldr r1, [r0, #40] \n" // load r11 from nlr_buf + "mov r11, r1 \n" + "ldr r1, [r0, #44] \n" // load r13=sp from nlr_buf + "mov r13, r1 \n" + "ldr r1, [r0, #8] \n" // load lr from nlr_buf + "mov lr, r1 \n" + #else + "ldr r8, [r0, #28] \n" // load r8 from nlr_buf + "ldr r9, [r0, #32] \n" // load r9 from nlr_buf + "ldr r10, [r0, #36] \n" // load r10 from nlr_buf + "ldr r11, [r0, #40] \n" // load r11 from nlr_buf + "ldr r13, [r0, #44] \n" // load r13=sp from nlr_buf + #if MICROPY_NLR_NUM_REGS == 16 + "vldr d8, [r0, #48] \n" // load s16-s17 from nlr_buf + "vldr d9, [r0, #56] \n" // load s18-s19 from nlr_buf + "vldr d10, [r0, #64] \n" // load s20-s21 from nlr_buf + #endif + "ldr lr, [r0, #8] \n" // load lr from nlr_buf + #endif + "movs r0, #1 \n" // return 1, non-local return + "bx lr \n" // return + : // output operands + : "r" (top) // input operands + : "memory" // clobbered registers + ); + + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_THUMB diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlrx64.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrx64.c new file mode 100644 index 00000000..d1ad91ff --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrx64.c @@ -0,0 +1,132 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 Damien P. George + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_X64 + +#undef nlr_push + +// x86-64 callee-save registers are: +// rbx, rbp, rsp, r12, r13, r14, r15 + +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); + +#if !MICROPY_NLR_OS_WINDOWS +#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 8) +#define USE_NAKED 1 +#else +// On older gcc the equivalent here is to force omit-frame-pointer +__attribute__((optimize("omit-frame-pointer"))) +#endif +#endif + +#if !defined(USE_NAKED) +#define USE_NAKED 0 +#endif + +#if USE_NAKED +// nlr_push prelude should never push frame pointer register ebp onto the stack +__attribute__((naked)) +#endif +unsigned int nlr_push(nlr_buf_t *nlr) { + #if !USE_NAKED + (void)nlr; + #endif + + #if MICROPY_NLR_OS_WINDOWS + + __asm volatile ( + "movq (%rsp), %rax \n" // load return %rip + "movq %rax, 16(%rcx) \n" // store %rip into nlr_buf + "movq %rbp, 24(%rcx) \n" // store %rbp into nlr_buf + "movq %rsp, 32(%rcx) \n" // store %rsp into nlr_buf + "movq %rbx, 40(%rcx) \n" // store %rbx into nlr_buf + "movq %r12, 48(%rcx) \n" // store %r12 into nlr_buf + "movq %r13, 56(%rcx) \n" // store %r13 into nlr_buf + "movq %r14, 64(%rcx) \n" // store %r14 into nlr_buf + "movq %r15, 72(%rcx) \n" // store %r15 into nlr_buf + "movq %rdi, 80(%rcx) \n" // store %rdr into nlr_buf + "movq %rsi, 88(%rcx) \n" // store %rsi into nlr_buf + "jmp nlr_push_tail \n" // do the rest in C + ); + + #else + + __asm volatile ( + "movq (%rsp), %rax \n" // load return %rip + "movq %rax, 16(%rdi) \n" // store %rip into nlr_buf + "movq %rbp, 24(%rdi) \n" // store %rbp into nlr_buf + "movq %rsp, 32(%rdi) \n" // store %rsp into nlr_buf + "movq %rbx, 40(%rdi) \n" // store %rbx into nlr_buf + "movq %r12, 48(%rdi) \n" // store %r12 into nlr_buf + "movq %r13, 56(%rdi) \n" // store %r13 into nlr_buf + "movq %r14, 64(%rdi) \n" // store %r14 into nlr_buf + "movq %r15, 72(%rdi) \n" // store %r15 into nlr_buf + #if defined(__APPLE__) && defined(__MACH__) + "jmp _nlr_push_tail \n" // do the rest in C + #else + "jmp nlr_push_tail \n" // do the rest in C + #endif + ); + + #endif + + #if !USE_NAKED + return 0; // needed to silence compiler warning + #endif +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm volatile ( + "movq %0, %%rcx \n" // %rcx points to nlr_buf + #if MICROPY_NLR_OS_WINDOWS + "movq 88(%%rcx), %%rsi \n" // load saved %rsi + "movq 80(%%rcx), %%rdi \n" // load saved %rdi + #endif + "movq 72(%%rcx), %%r15 \n" // load saved %r15 + "movq 64(%%rcx), %%r14 \n" // load saved %r14 + "movq 56(%%rcx), %%r13 \n" // load saved %r13 + "movq 48(%%rcx), %%r12 \n" // load saved %r12 + "movq 40(%%rcx), %%rbx \n" // load saved %rbx + "movq 32(%%rcx), %%rsp \n" // load saved %rsp + "movq 24(%%rcx), %%rbp \n" // load saved %rbp + "movq 16(%%rcx), %%rax \n" // load saved %rip + "movq %%rax, (%%rsp) \n" // store saved %rip to stack + "xorq %%rax, %%rax \n" // clear return register + "inc %%al \n" // increase to make 1, non-local return + "ret \n" // return + : // output operands + : "r" (top) // input operands + : "memory" // clobbered registers + ); + + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_X64 diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlrx86.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrx86.c new file mode 100644 index 00000000..085e30d2 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrx86.c @@ -0,0 +1,104 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 Damien P. George + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_X86 + +#undef nlr_push + +// For reference, x86 callee save regs are: +// ebx, esi, edi, ebp, esp, eip + +#if MICROPY_NLR_OS_WINDOWS +unsigned int nlr_push_tail(nlr_buf_t *nlr) asm ("nlr_push_tail"); +#else +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); +#endif + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8 +// Since gcc 8.0 the naked attribute is supported +#define USE_NAKED (1) +#define UNDO_PRELUDE (0) +#elif defined(__ZEPHYR__) || defined(__ANDROID__) +// Zephyr and Android use a different calling convention by default +#define USE_NAKED (0) +#define UNDO_PRELUDE (0) +#else +#define USE_NAKED (0) +#define UNDO_PRELUDE (1) +#endif + +#if USE_NAKED +__attribute__((naked)) +#endif +unsigned int nlr_push(nlr_buf_t *nlr) { + (void)nlr; + + __asm volatile ( + #if UNDO_PRELUDE + "pop %ebp \n" // undo function's prelude + #endif + "mov 4(%esp), %edx \n" // load nlr_buf + "mov (%esp), %eax \n" // load return %eip + "mov %eax, 8(%edx) \n" // store %eip into nlr_buf + "mov %ebp, 12(%edx) \n" // store %ebp into nlr_buf + "mov %esp, 16(%edx) \n" // store %esp into nlr_buf + "mov %ebx, 20(%edx) \n" // store %ebx into nlr_buf + "mov %edi, 24(%edx) \n" // store %edi into nlr_buf + "mov %esi, 28(%edx) \n" // store %esi into nlr_buf + "jmp nlr_push_tail \n" // do the rest in C + ); + + #if !USE_NAKED + return 0; // needed to silence compiler warning + #endif +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm volatile ( + "mov %0, %%edx \n" // %edx points to nlr_buf + "mov 28(%%edx), %%esi \n" // load saved %esi + "mov 24(%%edx), %%edi \n" // load saved %edi + "mov 20(%%edx), %%ebx \n" // load saved %ebx + "mov 16(%%edx), %%esp \n" // load saved %esp + "mov 12(%%edx), %%ebp \n" // load saved %ebp + "mov 8(%%edx), %%eax \n" // load saved %eip + "mov %%eax, (%%esp) \n" // store saved %eip to stack + "xor %%eax, %%eax \n" // clear return register + "inc %%al \n" // increase to make 1, non-local return + "ret \n" // return + : // output operands + : "r" (top) // input operands + : "memory" // clobbered registers + ); + + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_X86 diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/nlrxtensa.c b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrxtensa.c new file mode 100644 index 00000000..ff7af6ed --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/nlrxtensa.c @@ -0,0 +1,83 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Damien P. George + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_XTENSA + +#undef nlr_push + +// Xtensa calling conventions: +// a0 = return address +// a1 = stack pointer +// a2 = first arg, return value +// a3-a7 = rest of args + +unsigned int nlr_push(nlr_buf_t *nlr) { + + __asm volatile ( + "s32i.n a0, a2, 8 \n" // save regs... + "s32i.n a1, a2, 12 \n" + "s32i.n a8, a2, 16 \n" + "s32i.n a9, a2, 20 \n" + "s32i.n a10, a2, 24 \n" + "s32i.n a11, a2, 28 \n" + "s32i.n a12, a2, 32 \n" + "s32i.n a13, a2, 36 \n" + "s32i.n a14, a2, 40 \n" + "s32i.n a15, a2, 44 \n" + "j nlr_push_tail \n" // do the rest in C + ); + + return 0; // needed to silence compiler warning +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm volatile ( + "mov.n a2, %0 \n" // a2 points to nlr_buf + "l32i.n a0, a2, 8 \n" // restore regs... + "l32i.n a1, a2, 12 \n" + "l32i.n a8, a2, 16 \n" + "l32i.n a9, a2, 20 \n" + "l32i.n a10, a2, 24 \n" + "l32i.n a11, a2, 28 \n" + "l32i.n a12, a2, 32 \n" + "l32i.n a13, a2, 36 \n" + "l32i.n a14, a2, 40 \n" + "l32i.n a15, a2, 44 \n" + "movi.n a2, 1 \n" // return 1, non-local return + "ret.n \n" // return + : // output operands + : "r" (top) // input operands + : "memory" // clobbered registers + ); + + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_XTENSA diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/obj.c b/non_catalog_apps/mp_flipper/lib/micropython/py/obj.c new file mode 100644 index 00000000..e43451da --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/obj.c @@ -0,0 +1,597 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" +#include "py/objtype.h" +#include "py/objint.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/stackctrl.h" +#include "py/stream.h" // for mp_obj_print + +// Allocates an object and also sets type, for mp_obj_malloc{,_var} macros. +MP_NOINLINE void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { + mp_obj_base_t *base = (mp_obj_base_t *)m_malloc(num_bytes); + base->type = type; + return base; +} + +#if MICROPY_ENABLE_FINALISER +// Allocates an object and also sets type, for mp_obj_malloc{,_var}_with_finaliser macros. +MP_NOINLINE void *mp_obj_malloc_with_finaliser_helper(size_t num_bytes, const mp_obj_type_t *type) { + mp_obj_base_t *base = (mp_obj_base_t *)m_malloc_with_finaliser(num_bytes); + base->type = type; + return base; +} +#endif + +const mp_obj_type_t *MICROPY_WRAP_MP_OBJ_GET_TYPE(mp_obj_get_type)(mp_const_obj_t o_in) { + #if MICROPY_OBJ_IMMEDIATE_OBJS && MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A + + if (mp_obj_is_obj(o_in)) { + const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in); + return o->type; + } else { + static const mp_obj_type_t *const types[] = { + NULL, &mp_type_int, &mp_type_str, &mp_type_int, + NULL, &mp_type_int, &mp_type_NoneType, &mp_type_int, + NULL, &mp_type_int, &mp_type_str, &mp_type_int, + NULL, &mp_type_int, &mp_type_bool, &mp_type_int, + }; + return types[(uintptr_t)o_in & 0xf]; + } + + #elif MICROPY_OBJ_IMMEDIATE_OBJS && MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + + if (mp_obj_is_small_int(o_in)) { + return &mp_type_int; + } else if (mp_obj_is_obj(o_in)) { + const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in); + return o->type; + #if MICROPY_PY_BUILTINS_FLOAT + } else if ((((mp_uint_t)(o_in)) & 0xff800007) != 0x00000006) { + return &mp_type_float; + #endif + } else { + static const mp_obj_type_t *const types[] = { + &mp_type_str, &mp_type_NoneType, &mp_type_str, &mp_type_bool, + }; + return types[((uintptr_t)o_in >> 3) & 3]; + } + + #else + + if (mp_obj_is_small_int(o_in)) { + return &mp_type_int; + } else if (mp_obj_is_qstr(o_in)) { + return &mp_type_str; + #if MICROPY_PY_BUILTINS_FLOAT && ( \ + MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D) + } else if (mp_obj_is_float(o_in)) { + return &mp_type_float; + #endif + #if MICROPY_OBJ_IMMEDIATE_OBJS + } else if (mp_obj_is_immediate_obj(o_in)) { + static const mp_obj_type_t *const types[2] = {&mp_type_NoneType, &mp_type_bool}; + return types[MP_OBJ_IMMEDIATE_OBJ_VALUE(o_in) & 1]; + #endif + } else { + const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in); + return o->type; + } + + #endif +} + +const char *mp_obj_get_type_str(mp_const_obj_t o_in) { + return qstr_str(mp_obj_get_type(o_in)->name); +} + +void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + // There can be data structures nested too deep, or just recursive + MP_STACK_CHECK(); + #ifndef NDEBUG + if (o_in == MP_OBJ_NULL) { + mp_print_str(print, "(nil)"); + return; + } + #endif + const mp_obj_type_t *type = mp_obj_get_type(o_in); + if (MP_OBJ_TYPE_HAS_SLOT(type, print)) { + MP_OBJ_TYPE_GET_SLOT(type, print)((mp_print_t *)print, o_in, kind); + } else { + mp_printf(print, "<%q>", type->name); + } +} + +void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) { + mp_obj_print_helper(MP_PYTHON_PRINTER, o_in, kind); +} + +// helper function to print an exception with traceback +void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { + if (mp_obj_is_exception_instance(exc)) { + size_t n, *values; + mp_obj_exception_get_traceback(exc, &n, &values); + if (n > 0) { + assert(n % 3 == 0); + mp_print_str(print, "Traceback (most recent call last):\n"); + for (int i = n - 3; i >= 0; i -= 3) { + #if MICROPY_ENABLE_SOURCE_LINE + mp_printf(print, " File \"%q\", line %d", values[i], (int)values[i + 1]); + #else + mp_printf(print, " File \"%q\"", values[i]); + #endif + // the block name can be NULL if it's unknown + qstr block = values[i + 2]; + if (block == MP_QSTRnull) { + mp_print_str(print, "\n"); + } else { + mp_printf(print, ", in %q\n", block); + } + } + } + } + mp_obj_print_helper(print, exc, PRINT_EXC); + mp_print_str(print, "\n"); +} + +bool mp_obj_is_true(mp_obj_t arg) { + if (arg == mp_const_false) { + return 0; + } else if (arg == mp_const_true) { + return 1; + } else if (arg == mp_const_none) { + return 0; + } else if (mp_obj_is_small_int(arg)) { + if (arg == MP_OBJ_NEW_SMALL_INT(0)) { + return 0; + } else { + return 1; + } + } else { + const mp_obj_type_t *type = mp_obj_get_type(arg); + if (MP_OBJ_TYPE_HAS_SLOT(type, unary_op)) { + mp_obj_t result = MP_OBJ_TYPE_GET_SLOT(type, unary_op)(MP_UNARY_OP_BOOL, arg); + if (result != MP_OBJ_NULL) { + return result == mp_const_true; + } + } + + mp_obj_t len = mp_obj_len_maybe(arg); + if (len != MP_OBJ_NULL) { + // obj has a length, truth determined if len != 0 + return len != MP_OBJ_NEW_SMALL_INT(0); + } else { + // any other obj is true per Python semantics + return 1; + } + } +} + +bool mp_obj_is_callable(mp_obj_t o_in) { + const mp_call_fun_t call = MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o_in), call); + if (call != mp_obj_instance_call) { + return call != NULL; + } + return mp_obj_instance_is_callable(o_in); +} + +// This function implements the '==' and '!=' operators. +// +// From the Python language reference: +// (https://docs.python.org/3/reference/expressions.html#not-in) +// "The objects need not have the same type. If both are numbers, they are converted +// to a common type. Otherwise, the == and != operators always consider objects of +// different types to be unequal." +// +// This means that False==0 and True==1 are true expressions. +// +// Furthermore, from the v3.4.2 code for object.c: "Practical amendments: If rich +// comparison returns NotImplemented, == and != are decided by comparing the object +// pointer." +mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2) { + mp_obj_t local_true = (op == MP_BINARY_OP_NOT_EQUAL) ? mp_const_false : mp_const_true; + mp_obj_t local_false = (op == MP_BINARY_OP_NOT_EQUAL) ? mp_const_true : mp_const_false; + int pass_number = 0; + + // Shortcut for very common cases + if (o1 == o2 && + (mp_obj_is_small_int(o1) || !(mp_obj_get_type(o1)->flags & MP_TYPE_FLAG_EQ_NOT_REFLEXIVE))) { + return local_true; + } + + // fast path for strings + if (mp_obj_is_str(o1)) { + if (mp_obj_is_str(o2)) { + // both strings, use special function + return mp_obj_str_equal(o1, o2) ? local_true : local_false; + #if MICROPY_PY_STR_BYTES_CMP_WARN + } else if (mp_obj_is_type(o2, &mp_type_bytes)) { + str_bytes_cmp: + mp_warning(MP_WARN_CAT(BytesWarning), "Comparison between bytes and str"); + return local_false; + #endif + } else { + goto skip_one_pass; + } + #if MICROPY_PY_STR_BYTES_CMP_WARN + } else if (mp_obj_is_str(o2) && mp_obj_is_type(o1, &mp_type_bytes)) { + // o1 is not a string (else caught above), so the objects are not equal + goto str_bytes_cmp; + #endif + } + + // fast path for small ints + if (mp_obj_is_small_int(o1)) { + if (mp_obj_is_small_int(o2)) { + // both SMALL_INT, and not equal if we get here + return local_false; + } else { + goto skip_one_pass; + } + } + + // generic type, call binary_op(MP_BINARY_OP_EQUAL) + while (pass_number < 2) { + const mp_obj_type_t *type = mp_obj_get_type(o1); + // If a full equality test is not needed and the other object is a different + // type then we don't need to bother trying the comparison. + if (MP_OBJ_TYPE_HAS_SLOT(type, binary_op) && + ((type->flags & MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) || mp_obj_get_type(o2) == type)) { + // CPython is asymmetric: it will try __eq__ if there's no __ne__ but not the + // other way around. If the class doesn't need a full test we can skip __ne__. + if (op == MP_BINARY_OP_NOT_EQUAL && (type->flags & MP_TYPE_FLAG_EQ_HAS_NEQ_TEST)) { + mp_obj_t r = MP_OBJ_TYPE_GET_SLOT(type, binary_op)(MP_BINARY_OP_NOT_EQUAL, o1, o2); + if (r != MP_OBJ_NULL) { + return r; + } + } + + // Try calling __eq__. + mp_obj_t r = MP_OBJ_TYPE_GET_SLOT(type, binary_op)(MP_BINARY_OP_EQUAL, o1, o2); + if (r != MP_OBJ_NULL) { + if (op == MP_BINARY_OP_EQUAL) { + return r; + } else { + return mp_obj_is_true(r) ? local_true : local_false; + } + } + } + + skip_one_pass: + // Try the other way around if none of the above worked + ++pass_number; + mp_obj_t temp = o1; + o1 = o2; + o2 = temp; + } + + // equality not implemented, so fall back to pointer comparison + return (o1 == o2) ? local_true : local_false; +} + +bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) { + return mp_obj_is_true(mp_obj_equal_not_equal(MP_BINARY_OP_EQUAL, o1, o2)); +} + +mp_int_t mp_obj_get_int(mp_const_obj_t arg) { + // This function essentially performs implicit type conversion to int + // Note that Python does NOT provide implicit type conversion from + // float to int in the core expression language, try some_list[1.0]. + mp_int_t val; + if (!mp_obj_get_int_maybe(arg, &val)) { + mp_raise_TypeError_int_conversion(arg); + } + return val; +} + +mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg) { + if (mp_obj_is_int(arg)) { + return mp_obj_int_get_truncated(arg); + } else { + return mp_obj_get_int(arg); + } +} + +// returns false if arg is not of integral type +// returns true and sets *value if it is of integral type +// can throw OverflowError if arg is of integral type, but doesn't fit in a mp_int_t +bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value) { + if (arg == mp_const_false) { + *value = 0; + } else if (arg == mp_const_true) { + *value = 1; + } else if (mp_obj_is_small_int(arg)) { + *value = MP_OBJ_SMALL_INT_VALUE(arg); + } else if (mp_obj_is_exact_type(arg, &mp_type_int)) { + *value = mp_obj_int_get_checked(arg); + } else { + arg = mp_unary_op(MP_UNARY_OP_INT_MAYBE, (mp_obj_t)arg); + if (arg != MP_OBJ_NULL) { + *value = mp_obj_int_get_checked(arg); + } else { + return false; + } + } + return true; +} + +#if MICROPY_PY_BUILTINS_FLOAT +bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value) { + mp_float_t val; + + if (arg == mp_const_false) { + val = 0; + } else if (arg == mp_const_true) { + val = 1; + } else if (mp_obj_is_small_int(arg)) { + val = (mp_float_t)MP_OBJ_SMALL_INT_VALUE(arg); + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + } else if (mp_obj_is_exact_type(arg, &mp_type_int)) { + val = mp_obj_int_as_float_impl(arg); + #endif + } else if (mp_obj_is_float(arg)) { + val = mp_obj_float_get(arg); + } else { + arg = mp_unary_op(MP_UNARY_OP_FLOAT_MAYBE, (mp_obj_t)arg); + if (arg != MP_OBJ_NULL && mp_obj_is_float(arg)) { + val = mp_obj_float_get(arg); + } else { + return false; + } + } + *value = val; + return true; +} + +mp_float_t mp_obj_get_float(mp_obj_t arg) { + mp_float_t val; + + if (!mp_obj_get_float_maybe(arg, &val)) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("can't convert to float")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert %s to float"), mp_obj_get_type_str(arg)); + #endif + } + + return val; +} + +#if MICROPY_PY_BUILTINS_COMPLEX +bool mp_obj_get_complex_maybe(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { + if (mp_obj_get_float_maybe(arg, real)) { + *imag = 0; + } else if (mp_obj_is_type(arg, &mp_type_complex)) { + mp_obj_complex_get(arg, real, imag); + } else { + arg = mp_unary_op(MP_UNARY_OP_COMPLEX_MAYBE, (mp_obj_t)arg); + if (arg != MP_OBJ_NULL && mp_obj_is_type(arg, &mp_type_complex)) { + mp_obj_complex_get(arg, real, imag); + } else { + return false; + } + } + return true; +} + +void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { + if (!mp_obj_get_complex_maybe(arg, real, imag)) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("can't convert to complex")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert %s to complex"), mp_obj_get_type_str(arg)); + #endif + } +} +#endif +#endif + +// note: returned value in *items may point to the interior of a GC block +void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items) { + if (mp_obj_is_type(o, &mp_type_tuple)) { + mp_obj_tuple_get(o, len, items); + } else if (mp_obj_is_type(o, &mp_type_list)) { + mp_obj_list_get(o, len, items); + } else { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("expected tuple/list")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("object '%s' isn't a tuple or list"), mp_obj_get_type_str(o)); + #endif + } +} + +// note: returned value in *items may point to the interior of a GC block +void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items) { + size_t seq_len; + mp_obj_get_array(o, &seq_len, items); + if (seq_len != len) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_ValueError(MP_ERROR_TEXT("tuple/list has wrong length")); + #else + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("requested length %d but object has length %d"), (int)len, (int)seq_len); + #endif + } +} + +// is_slice determines whether the index is a slice index +size_t mp_get_index(const mp_obj_type_t *type, size_t len, mp_obj_t index, bool is_slice) { + mp_int_t i; + if (mp_obj_is_small_int(index)) { + i = MP_OBJ_SMALL_INT_VALUE(index); + } else if (!mp_obj_get_int_maybe(index, &i)) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("indices must be integers")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("%q indices must be integers, not %s"), + type->name, mp_obj_get_type_str(index)); + #endif + } + + if (i < 0) { + i += len; + } + if (is_slice) { + if (i < 0) { + i = 0; + } else if ((mp_uint_t)i > len) { + i = len; + } + } else { + if (i < 0 || (mp_uint_t)i >= len) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("index out of range")); + #else + mp_raise_msg_varg(&mp_type_IndexError, MP_ERROR_TEXT("%q index out of range"), type->name); + #endif + } + } + + // By this point 0 <= i <= len and so fits in a size_t + return (size_t)i; +} + +mp_obj_t mp_obj_id(mp_obj_t o_in) { + mp_int_t id = (mp_int_t)o_in; + if (!mp_obj_is_obj(o_in)) { + return mp_obj_new_int(id); + } else if (id >= 0) { + // Many OSes and CPUs have affinity for putting "user" memories + // into low half of address space, and "system" into upper half. + // We're going to take advantage of that and return small int + // (signed) for such "user" addresses. + return MP_OBJ_NEW_SMALL_INT(id); + } else { + // If that didn't work, well, let's return long int, just as + // a (big) positive value, so it will never clash with the range + // of small int returned in previous case. + return mp_obj_new_int_from_uint((mp_uint_t)id); + } +} + +// will raise a TypeError if object has no length +mp_obj_t mp_obj_len(mp_obj_t o_in) { + mp_obj_t len = mp_obj_len_maybe(o_in); + if (len == MP_OBJ_NULL) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object has no len")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("object of type '%s' has no len()"), mp_obj_get_type_str(o_in)); + #endif + } else { + return len; + } +} + +// may return MP_OBJ_NULL +mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) { + if ( + #if !MICROPY_PY_BUILTINS_STR_UNICODE + // It's simple - unicode is slow, non-unicode is fast + mp_obj_is_str(o_in) || + #endif + mp_obj_is_type(o_in, &mp_type_bytes)) { + GET_STR_LEN(o_in, l); + return MP_OBJ_NEW_SMALL_INT(l); + } else { + const mp_obj_type_t *type = mp_obj_get_type(o_in); + if (MP_OBJ_TYPE_HAS_SLOT(type, unary_op)) { + return MP_OBJ_TYPE_GET_SLOT(type, unary_op)(MP_UNARY_OP_LEN, o_in); + } else { + return MP_OBJ_NULL; + } + } +} + +mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { + const mp_obj_type_t *type = mp_obj_get_type(base); + if (MP_OBJ_TYPE_HAS_SLOT(type, subscr)) { + mp_obj_t ret = MP_OBJ_TYPE_GET_SLOT(type, subscr)(base, index, value); + if (ret != MP_OBJ_NULL) { + return ret; + } + // TODO: call base classes here? + } + if (value == MP_OBJ_NULL) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item deletion")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object doesn't support item deletion"), mp_obj_get_type_str(base)); + #endif + } else if (value == MP_OBJ_SENTINEL) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object isn't subscriptable")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object isn't subscriptable"), mp_obj_get_type_str(base)); + #endif + } else { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item assignment")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object doesn't support item assignment"), mp_obj_get_type_str(base)); + #endif + } +} + +// Return input argument. Useful as .getiter for objects which are +// their own iterators, etc. +mp_obj_t mp_identity(mp_obj_t self) { + return self; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_identity_obj, mp_identity); + +// mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf) { +// (void)iter_buf; +// return self; +// } + +bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + const mp_obj_type_t *type = mp_obj_get_type(obj); + if (MP_OBJ_TYPE_HAS_SLOT(type, buffer) + && MP_OBJ_TYPE_GET_SLOT(type, buffer)(obj, bufinfo, flags & MP_BUFFER_RW) == 0) { + return true; + } + if (flags & MP_BUFFER_RAISE_IF_UNSUPPORTED) { + mp_raise_TypeError(MP_ERROR_TEXT("object with buffer protocol required")); + } + return false; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/obj.h b/non_catalog_apps/mp_flipper/lib/micropython/py/obj.h new file mode 100644 index 00000000..9f2bb46e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/obj.h @@ -0,0 +1,1287 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJ_H +#define MICROPY_INCLUDED_PY_OBJ_H + +#include + +#include "py/mpconfig.h" +#include "py/misc.h" +#include "py/qstr.h" +#include "py/mpprint.h" +#include "py/runtime0.h" + +// This is the definition of the opaque MicroPython object type. +// All concrete objects have an encoding within this type and the +// particular encoding is specified by MICROPY_OBJ_REPR. +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +typedef uint64_t mp_obj_t; +typedef uint64_t mp_const_obj_t; +#else +typedef void *mp_obj_t; +typedef const void *mp_const_obj_t; +#endif + +// This mp_obj_type_t struct is a concrete MicroPython object which holds info +// about a type. See below for actual definition of the struct. +typedef struct _mp_obj_type_t mp_obj_type_t; + +// Anything that wants to be a concrete MicroPython object must have mp_obj_base_t +// as its first member (small ints, qstr objs and inline floats are not concrete). +struct _mp_obj_base_t { + const mp_obj_type_t *type MICROPY_OBJ_BASE_ALIGNMENT; +}; +typedef struct _mp_obj_base_t mp_obj_base_t; + +// These fake objects are used to indicate certain things in arguments or return +// values, and should only be used when explicitly allowed. +// +// - MP_OBJ_NULL : used to indicate the absence of an object, or unsupported operation. +// - MP_OBJ_STOP_ITERATION : used instead of throwing a StopIteration, for efficiency. +// - MP_OBJ_SENTINEL : used for various internal purposes where one needs +// an object which is unique from all other objects, including MP_OBJ_NULL. +// +// For debugging purposes they are all different. For non-debug mode, we alias +// as many as we can to MP_OBJ_NULL because it's cheaper to load/compare 0. + +#if MICROPY_DEBUG_MP_OBJ_SENTINELS +#define MP_OBJ_NULL (MP_OBJ_FROM_PTR((void *)0)) +#define MP_OBJ_STOP_ITERATION (MP_OBJ_FROM_PTR((void *)4)) +#define MP_OBJ_SENTINEL (MP_OBJ_FROM_PTR((void *)8)) +#else +#define MP_OBJ_NULL (MP_OBJ_FROM_PTR((void *)0)) +#define MP_OBJ_STOP_ITERATION (MP_OBJ_FROM_PTR((void *)0)) +#define MP_OBJ_SENTINEL (MP_OBJ_FROM_PTR((void *)4)) +#endif + +// These macros/inline functions operate on objects and depend on the +// particular object representation. They are used to query, pack and +// unpack small ints, qstrs and full object pointers. + +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A + +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 1) != 0; +} +#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 1) +#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1)) + +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 7) == 2; +} +#define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 3) +#define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 3) | 2)) + +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 7) == 6; +} +#define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) (((mp_uint_t)(o)) >> 3) +#define MP_OBJ_NEW_IMMEDIATE_OBJ(val) ((mp_obj_t)(((val) << 3) | 6)) + +#if MICROPY_PY_BUILTINS_FLOAT +#define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj) +#define mp_const_float_pi MP_ROM_PTR(&mp_const_float_pi_obj) +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau MP_ROM_PTR(&mp_const_float_tau_obj) +#define mp_const_float_inf MP_ROM_PTR(&mp_const_float_inf_obj) +#define mp_const_float_nan MP_ROM_PTR(&mp_const_float_nan_obj) +#endif +extern const struct _mp_obj_float_t mp_const_float_e_obj; +extern const struct _mp_obj_float_t mp_const_float_pi_obj; +#if MICROPY_PY_MATH_CONSTANTS +extern const struct _mp_obj_float_t mp_const_float_tau_obj; +extern const struct _mp_obj_float_t mp_const_float_inf_obj; +extern const struct _mp_obj_float_t mp_const_float_nan_obj; +#endif + +#define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float) +mp_float_t mp_obj_float_get(mp_obj_t self_in); +mp_obj_t mp_obj_new_float(mp_float_t value); +#endif + +static inline bool mp_obj_is_obj(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 3) == 0; +} + +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B + +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 3) == 1; +} +#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 2) +#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 2) | 1)) + +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 7) == 3; +} +#define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 3) +#define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 3) | 3)) + +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 7) == 7; +} +#define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) (((mp_uint_t)(o)) >> 3) +#define MP_OBJ_NEW_IMMEDIATE_OBJ(val) ((mp_obj_t)(((val) << 3) | 7)) + +#if MICROPY_PY_BUILTINS_FLOAT +#define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj) +#define mp_const_float_pi MP_ROM_PTR(&mp_const_float_pi_obj) +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau MP_ROM_PTR(&mp_const_float_tau_obj) +#define mp_const_float_inf MP_ROM_PTR(&mp_const_float_inf_obj) +#define mp_const_float_nan MP_ROM_PTR(&mp_const_float_nan_obj) +#endif +extern const struct _mp_obj_float_t mp_const_float_e_obj; +extern const struct _mp_obj_float_t mp_const_float_pi_obj; +#if MICROPY_PY_MATH_CONSTANTS +extern const struct _mp_obj_float_t mp_const_float_tau_obj; +extern const struct _mp_obj_float_t mp_const_float_inf_obj; +extern const struct _mp_obj_float_t mp_const_float_nan_obj; +#endif + +#define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float) +mp_float_t mp_obj_float_get(mp_obj_t self_in); +mp_obj_t mp_obj_new_float(mp_float_t value); +#endif + +static inline bool mp_obj_is_obj(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 1) == 0; +} + +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_NONE +#error "MICROPY_OBJ_REPR_C requires float to be enabled." +#endif + +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 1) != 0; +} +#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 1) +#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1)) + +#if MICROPY_PY_BUILTINS_FLOAT +#define mp_const_float_e MP_ROM_PTR((mp_obj_t)(((0x402df854 & ~3) | 2) + 0x80800000)) +#define mp_const_float_pi MP_ROM_PTR((mp_obj_t)(((0x40490fdb & ~3) | 2) + 0x80800000)) +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau MP_ROM_PTR((mp_obj_t)(((0x40c90fdb & ~3) | 2) + 0x80800000)) +#define mp_const_float_inf MP_ROM_PTR((mp_obj_t)(((0x7f800000 & ~3) | 2) + 0x80800000)) +#define mp_const_float_nan MP_ROM_PTR((mp_obj_t)(((0xffc00000 & ~3) | 2) + 0x80800000)) +#endif + +static inline bool mp_obj_is_float(mp_const_obj_t o) { + // Ensure that 32-bit arch can only use single precision. + MP_STATIC_ASSERT(sizeof(mp_float_t) <= sizeof(mp_obj_t)); + + return (((mp_uint_t)(o)) & 3) == 2 && (((mp_uint_t)(o)) & 0xff800007) != 0x00000006; +} +static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { + union { + mp_float_t f; + mp_uint_t u; + } num = {.u = ((mp_uint_t)o - 0x80800000) & ~3}; + return num.f; +} +static inline mp_obj_t mp_obj_new_float(mp_float_t f) { + union { + mp_float_t f; + mp_uint_t u; + } num = {.f = f}; + return (mp_obj_t)(((num.u & ~0x3) | 2) + 0x80800000); +} +#endif + +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { + return (((mp_uint_t)(o)) & 0xff80000f) == 0x00000006; +} +#define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 4) +#define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 4) | 0x00000006)) + +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { + return (((mp_uint_t)(o)) & 0xff80000f) == 0x0000000e; +} +#define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) (((mp_uint_t)(o)) >> 4) +#define MP_OBJ_NEW_IMMEDIATE_OBJ(val) ((mp_obj_t)(((val) << 4) | 0xe)) + +static inline bool mp_obj_is_obj(mp_const_obj_t o) { + return (((mp_int_t)(o)) & 3) == 0; +} + +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { + return (((uint64_t)(o)) & 0xffff000000000000) == 0x0001000000000000; +} +#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)((o) << 16)) >> 17) +#define MP_OBJ_NEW_SMALL_INT(small_int) (((((uint64_t)(small_int)) & 0x7fffffffffff) << 1) | 0x0001000000000001) + +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { + return (((uint64_t)(o)) & 0xffff000000000000) == 0x0002000000000000; +} +#define MP_OBJ_QSTR_VALUE(o) ((((uint32_t)(o)) >> 1) & 0xffffffff) +#define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)(((uint64_t)(((uint32_t)(qst)) << 1)) | 0x0002000000000001)) + +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { + return (((uint64_t)(o)) & 0xffff000000000000) == 0x0003000000000000; +} +#define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) ((((uint32_t)(o)) >> 46) & 3) +#define MP_OBJ_NEW_IMMEDIATE_OBJ(val) (((uint64_t)(val) << 46) | 0x0003000000000000) + +#if MICROPY_PY_BUILTINS_FLOAT + +#if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_DOUBLE +#error MICROPY_OBJ_REPR_D requires MICROPY_FLOAT_IMPL_DOUBLE +#endif + +#define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))} +#define mp_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))} +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau {((mp_obj_t)((uint64_t)0x401921fb54442d18 + 0x8004000000000000))} +#define mp_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))} +#define mp_const_float_nan {((mp_obj_t)((uint64_t)0xfff8000000000000 + 0x8004000000000000))} +#endif + +static inline bool mp_obj_is_float(mp_const_obj_t o) { + return ((uint64_t)(o) & 0xfffc000000000000) != 0; +} +static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { + union { + mp_float_t f; + uint64_t r; + } num = {.r = o - 0x8004000000000000}; + return num.f; +} +static inline mp_obj_t mp_obj_new_float(mp_float_t f) { + union { + mp_float_t f; + uint64_t r; + } num = {.f = f}; + return num.r + 0x8004000000000000; +} +#endif + +static inline bool mp_obj_is_obj(mp_const_obj_t o) { + return (((uint64_t)(o)) & 0xffff000000000000) == 0x0000000000000000; +} +#define MP_OBJ_TO_PTR(o) ((void *)(uintptr_t)(o)) +#define MP_OBJ_FROM_PTR(p) ((mp_obj_t)((uintptr_t)(p))) + +// rom object storage needs special handling to widen 32-bit pointer to 64-bits +typedef union _mp_rom_obj_t { + uint64_t u64; + struct { + const void *lo, *hi; + } u32; +} mp_rom_obj_t; +#define MP_ROM_INT(i) {MP_OBJ_NEW_SMALL_INT(i)} +#define MP_ROM_QSTR(q) {MP_OBJ_NEW_QSTR(q)} +#if MP_ENDIANNESS_LITTLE +#define MP_ROM_PTR(p) {.u32 = {.lo = (p), .hi = NULL}} +#else +#define MP_ROM_PTR(p) {.u32 = {.lo = NULL, .hi = (p)}} +#endif + +#endif + +// Macros to convert between mp_obj_t and concrete object types. +// These are identity operations in MicroPython, but ability to override +// these operations are provided to experiment with other methods of +// object representation and memory management. + +// Cast mp_obj_t to object pointer +#ifndef MP_OBJ_TO_PTR +#define MP_OBJ_TO_PTR(o) ((void *)(o)) +#endif + +// Cast object pointer to mp_obj_t +#ifndef MP_OBJ_FROM_PTR +#define MP_OBJ_FROM_PTR(p) ((mp_obj_t)(p)) +#endif + +// Macros to create objects that are stored in ROM. + +#ifndef MP_ROM_NONE +#if MICROPY_OBJ_IMMEDIATE_OBJS +#define MP_ROM_NONE mp_const_none +#else +#define MP_ROM_NONE MP_ROM_PTR(&mp_const_none_obj) +#endif +#endif + +#ifndef MP_ROM_FALSE +#if MICROPY_OBJ_IMMEDIATE_OBJS +#define MP_ROM_FALSE mp_const_false +#define MP_ROM_TRUE mp_const_true +#else +#define MP_ROM_FALSE MP_ROM_PTR(&mp_const_false_obj) +#define MP_ROM_TRUE MP_ROM_PTR(&mp_const_true_obj) +#endif +#endif + +#ifndef MP_ROM_INT +typedef mp_const_obj_t mp_rom_obj_t; +#define MP_ROM_INT(i) MP_OBJ_NEW_SMALL_INT(i) +#define MP_ROM_QSTR(q) MP_OBJ_NEW_QSTR(q) +#define MP_ROM_PTR(p) (p) +/* for testing +typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; +#define MP_ROM_INT(i) {MP_OBJ_NEW_SMALL_INT(i)} +#define MP_ROM_QSTR(q) {MP_OBJ_NEW_QSTR(q)} +#define MP_ROM_PTR(p) {.o = p} +*/ +#endif + +// These macros are used to declare and define constant function objects +// You can put "static" in front of the definitions to make them local + +#define MP_DECLARE_CONST_FUN_OBJ_0(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name +#define MP_DECLARE_CONST_FUN_OBJ_1(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name +#define MP_DECLARE_CONST_FUN_OBJ_2(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name +#define MP_DECLARE_CONST_FUN_OBJ_3(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name +#define MP_DECLARE_CONST_FUN_OBJ_VAR(obj_name) extern const mp_obj_fun_builtin_var_t obj_name +#define MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name) extern const mp_obj_fun_builtin_var_t obj_name +#define MP_DECLARE_CONST_FUN_OBJ_KW(obj_name) extern const mp_obj_fun_builtin_var_t obj_name + +#define MP_OBJ_FUN_ARGS_MAX (0xffff) // to set maximum value in n_args_max below +#define MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, takes_kw) ((uint32_t)((((uint32_t)(n_args_min)) << 17) | (((uint32_t)(n_args_max)) << 1) | ((takes_kw) ? 1 : 0))) + +#define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) \ + const mp_obj_fun_builtin_fixed_t obj_name = \ + {{&mp_type_fun_builtin_0}, .fun._0 = fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) \ + const mp_obj_fun_builtin_fixed_t obj_name = \ + {{&mp_type_fun_builtin_1}, .fun._1 = fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) \ + const mp_obj_fun_builtin_fixed_t obj_name = \ + {{&mp_type_fun_builtin_2}, .fun._2 = fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) \ + const mp_obj_fun_builtin_fixed_t obj_name = \ + {{&mp_type_fun_builtin_3}, .fun._3 = fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) \ + const mp_obj_fun_builtin_var_t obj_name = \ + {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, false), .fun.var = fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) \ + const mp_obj_fun_builtin_var_t obj_name = \ + {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, false), .fun.var = fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \ + const mp_obj_fun_builtin_var_t obj_name = \ + {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun.kw = fun_name} + +// These macros are used to define constant map/dict objects +// You can put "static" in front of the definition to make it local + +#define MP_DEFINE_CONST_MAP(map_name, table_name) \ + const mp_map_t map_name = { \ + .all_keys_are_qstrs = 1, \ + .is_fixed = 1, \ + .is_ordered = 1, \ + .used = MP_ARRAY_SIZE(table_name), \ + .alloc = MP_ARRAY_SIZE(table_name), \ + .table = (mp_map_elem_t *)(mp_rom_map_elem_t *)table_name, \ + } + +#define MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, n) \ + const mp_obj_dict_t dict_name = { \ + .base = {&mp_type_dict}, \ + .map = { \ + .all_keys_are_qstrs = 1, \ + .is_fixed = 1, \ + .is_ordered = 1, \ + .used = n, \ + .alloc = n, \ + .table = (mp_map_elem_t *)(mp_rom_map_elem_t *)table_name, \ + }, \ + } + +#define MP_DEFINE_CONST_DICT(dict_name, table_name) MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, MP_ARRAY_SIZE(table_name)) + +// These macros are used to declare and define constant staticmethod and classmethod objects +// You can put "static" in front of the definitions to make them local + +#define MP_DECLARE_CONST_STATICMETHOD_OBJ(obj_name) extern const mp_rom_obj_static_class_method_t obj_name +#define MP_DECLARE_CONST_CLASSMETHOD_OBJ(obj_name) extern const mp_rom_obj_static_class_method_t obj_name + +#define MP_DEFINE_CONST_STATICMETHOD_OBJ(obj_name, fun_name) const mp_rom_obj_static_class_method_t obj_name = {{&mp_type_staticmethod}, fun_name} +#define MP_DEFINE_CONST_CLASSMETHOD_OBJ(obj_name, fun_name) const mp_rom_obj_static_class_method_t obj_name = {{&mp_type_classmethod}, fun_name} + +#ifndef NO_QSTR + +// Declare a module as a builtin, processed by makemoduledefs.py +// param module_name: MP_QSTR_ +// param obj_module: mp_obj_module_t instance +#define MP_REGISTER_MODULE(module_name, obj_module) + +// As above, but allow this module to be extended from the filesystem. +#define MP_REGISTER_EXTENSIBLE_MODULE(module_name, obj_module) + +// Add a custom handler for a builtin module that will be called to delegate +// failed attribute lookups. +#define MP_REGISTER_MODULE_DELEGATION(obj_module, fun_name) + +// Declare a root pointer (to avoid garbage collection of a global static variable). +// param variable_declaration: a valid C variable declaration +#define MP_REGISTER_ROOT_POINTER(variable_declaration) + +#endif // NO_QSTR + +// Underlying map/hash table implementation (not dict object or map function) + +typedef struct _mp_map_elem_t { + mp_obj_t key; + mp_obj_t value; +} mp_map_elem_t; + +typedef struct _mp_rom_map_elem_t { + mp_rom_obj_t key; + mp_rom_obj_t value; +} mp_rom_map_elem_t; + +typedef struct _mp_map_t { + size_t all_keys_are_qstrs : 1; + size_t is_fixed : 1; // if set, table is fixed/read-only and can't be modified + size_t is_ordered : 1; // if set, table is an ordered array, not a hash map + size_t used : (8 * sizeof(size_t) - 3); + size_t alloc; + mp_map_elem_t *table; +} mp_map_t; + +// mp_set_lookup requires these constants to have the values they do +typedef enum _mp_map_lookup_kind_t { + MP_MAP_LOOKUP = 0, + MP_MAP_LOOKUP_ADD_IF_NOT_FOUND = 1, + MP_MAP_LOOKUP_REMOVE_IF_FOUND = 2, + MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND = 3, // only valid for mp_set_lookup +} mp_map_lookup_kind_t; + +static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) { + assert(pos < map->alloc); + return (map)->table[pos].key != MP_OBJ_NULL && (map)->table[pos].key != MP_OBJ_SENTINEL; +} + +void mp_map_init(mp_map_t *map, size_t n); +void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table); +mp_map_t *mp_map_new(size_t n); +void mp_map_deinit(mp_map_t *map); +void mp_map_free(mp_map_t *map); +mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind); +void mp_map_clear(mp_map_t *map); +void mp_map_dump(mp_map_t *map); + +// Underlying set implementation (not set object) + +typedef struct _mp_set_t { + size_t alloc; + size_t used; + mp_obj_t *table; +} mp_set_t; + +static inline bool mp_set_slot_is_filled(const mp_set_t *set, size_t pos) { + return (set)->table[pos] != MP_OBJ_NULL && (set)->table[pos] != MP_OBJ_SENTINEL; +} + +void mp_set_init(mp_set_t *set, size_t n); +mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind); +mp_obj_t mp_set_remove_first(mp_set_t *set); +void mp_set_clear(mp_set_t *set); + +// Type definitions for methods + +typedef mp_obj_t (*mp_fun_0_t)(void); +typedef mp_obj_t (*mp_fun_1_t)(mp_obj_t); +typedef mp_obj_t (*mp_fun_2_t)(mp_obj_t, mp_obj_t); +typedef mp_obj_t (*mp_fun_3_t)(mp_obj_t, mp_obj_t, mp_obj_t); +typedef mp_obj_t (*mp_fun_var_t)(size_t n, const mp_obj_t *); +// mp_fun_kw_t takes mp_map_t* (and not const mp_map_t*) to ease passing +// this arg to mp_map_lookup(). +typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); + +// Flags for type behaviour (mp_obj_type_t.flags) +// If MP_TYPE_FLAG_EQ_NOT_REFLEXIVE is clear then __eq__ is reflexive (A==A returns True). +// If MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE is clear then the type can't be equal to an +// instance of any different class that also clears this flag. If this flag is set +// then the type may check for equality against a different type. +// If MP_TYPE_FLAG_EQ_HAS_NEQ_TEST is clear then the type only implements the __eq__ +// operator and not the __ne__ operator. If it's set then __ne__ may be implemented. +// If MP_TYPE_FLAG_BINDS_SELF is set then the type as a method binds self as the first arg. +// If MP_TYPE_FLAG_BUILTIN_FUN is set then the type is a built-in function type. +// MP_TYPE_FLAG_ITER_IS_GETITER is a no-op flag that means the default behaviour for the +// iter slot and it's the getiter function. +// If MP_TYPE_FLAG_ITER_IS_ITERNEXT is set then the "iter" slot is the iternext +// function and getiter will be automatically implemented as "return self". +// If MP_TYPE_FLAG_ITER_IS_CUSTOM is set then the "iter" slot is a pointer to a +// mp_getiter_iternext_custom_t struct instance (with both .getiter and .iternext set). +// If MP_TYPE_FLAG_ITER_IS_STREAM is set then the type implicitly gets a "return self" +// getiter, and mp_stream_unbuffered_iter for iternext. +// If MP_TYPE_FLAG_INSTANCE_TYPE is set then this is an instance type (i.e. defined in Python). +#define MP_TYPE_FLAG_NONE (0x0000) +#define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001) +#define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002) +#define MP_TYPE_FLAG_EQ_NOT_REFLEXIVE (0x0004) +#define MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE (0x0008) +#define MP_TYPE_FLAG_EQ_HAS_NEQ_TEST (0x0010) +#define MP_TYPE_FLAG_BINDS_SELF (0x0020) +#define MP_TYPE_FLAG_BUILTIN_FUN (0x0040) +#define MP_TYPE_FLAG_ITER_IS_GETITER (0x0000) +#define MP_TYPE_FLAG_ITER_IS_ITERNEXT (0x0080) +#define MP_TYPE_FLAG_ITER_IS_CUSTOM (0x0100) +#define MP_TYPE_FLAG_ITER_IS_STREAM (MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_ITER_IS_CUSTOM) +#define MP_TYPE_FLAG_INSTANCE_TYPE (0x0200) + +typedef enum { + PRINT_STR = 0, + PRINT_REPR = 1, + PRINT_EXC = 2, // Special format for printing exception in unhandled exception message + PRINT_JSON = 3, + PRINT_RAW = 4, // Special format for printing bytes as an undercorated string + PRINT_EXC_SUBCLASS = 0x80, // Internal flag for printing exception subclasses +} mp_print_kind_t; + +typedef struct _mp_obj_iter_buf_t { + mp_obj_base_t base; + mp_obj_t buf[3]; +} mp_obj_iter_buf_t; + +// The number of slots that an mp_obj_iter_buf_t needs on the Python value stack. +// It's rounded up in case mp_obj_base_t is smaller than mp_obj_t (eg for OBJ_REPR_D). +#define MP_OBJ_ITER_BUF_NSLOTS ((sizeof(mp_obj_iter_buf_t) + sizeof(mp_obj_t) - 1) / sizeof(mp_obj_t)) + +typedef void (*mp_print_fun_t)(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind); +typedef mp_obj_t (*mp_make_new_fun_t)(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); +typedef mp_obj_t (*mp_call_fun_t)(mp_obj_t fun, size_t n_args, size_t n_kw, const mp_obj_t *args); +typedef mp_obj_t (*mp_unary_op_fun_t)(mp_unary_op_t op, mp_obj_t); +typedef mp_obj_t (*mp_binary_op_fun_t)(mp_binary_op_t op, mp_obj_t, mp_obj_t); +typedef void (*mp_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t *dest); +typedef mp_obj_t (*mp_subscr_fun_t)(mp_obj_t self_in, mp_obj_t index, mp_obj_t value); +typedef mp_obj_t (*mp_getiter_fun_t)(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf); +typedef mp_fun_1_t mp_iternext_fun_t; + +// For MP_TYPE_FLAG_ITER_IS_CUSTOM, the "getiter" slot points to an instance of this type. +typedef struct _mp_getiter_iternext_custom_t { + mp_getiter_fun_t getiter; + mp_iternext_fun_t iternext; +} mp_getiter_iternext_custom_t; + +// Buffer protocol + +typedef struct _mp_buffer_info_t { + void *buf; // can be NULL if len == 0 + size_t len; // in bytes + int typecode; // as per binary.h +} mp_buffer_info_t; + +#define MP_BUFFER_READ (1) +#define MP_BUFFER_WRITE (2) +#define MP_BUFFER_RW (MP_BUFFER_READ | MP_BUFFER_WRITE) +#define MP_BUFFER_RAISE_IF_UNSUPPORTED (4) + +typedef mp_int_t (*mp_buffer_fun_t)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); + +bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); + +static inline void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mp_get_buffer(obj, bufinfo, flags | MP_BUFFER_RAISE_IF_UNSUPPORTED); +} + +// This struct will be updated to become a variable sized struct. In order to +// use this as a member, or allocate dynamically, use the mp_obj_empty_type_t +// or mp_obj_full_type_t structs below (which must be kept in sync). +struct _mp_obj_type_t { + // A type is an object so must start with this entry, which points to mp_type_type. + mp_obj_base_t base; + + // Flags associated with this type. + uint16_t flags; + + // The name of this type, a qstr. + uint16_t name; + + // Slots: For the rest of the fields, the slot index points to the + // relevant function in the variable-length "slots" field. Ideally these + // would be only 4 bits, but the extra overhead of accessing them adds + // more code, and we also need to be able to take the address of them for + // mp_obj_class_lookup. + + // Corresponds to __new__ and __init__ special methods, to make an instance of the type. + uint8_t slot_index_make_new; + + // Corresponds to __repr__ and __str__ special methods. + uint8_t slot_index_print; + + // Corresponds to __call__ special method, ie T(...). + uint8_t slot_index_call; + + // Implements unary and binary operations. + // Can return MP_OBJ_NULL if the operation is not supported. + uint8_t slot_index_unary_op; + uint8_t slot_index_binary_op; + + // Implements load, store and delete attribute. + // + // dest[0] = MP_OBJ_NULL means load + // return: for fail, do nothing + // for fail but continue lookup in locals_dict, dest[1] = MP_OBJ_SENTINEL + // for attr, dest[0] = value + // for method, dest[0] = method, dest[1] = self + // + // dest[0,1] = {MP_OBJ_SENTINEL, MP_OBJ_NULL} means delete + // dest[0,1] = {MP_OBJ_SENTINEL, object} means store + // return: for fail, do nothing + // for success set dest[0] = MP_OBJ_NULL + uint8_t slot_index_attr; + + // Implements load, store and delete subscripting: + // - value = MP_OBJ_SENTINEL means load + // - value = MP_OBJ_NULL means delete + // - all other values mean store the value + // Can return MP_OBJ_NULL if operation not supported. + uint8_t slot_index_subscr; + + // This slot's behaviour depends on the MP_TYPE_FLAG_ITER_IS_* flags above. + // - If MP_TYPE_FLAG_ITER_IS_GETITER flag is set, then this corresponds to the __iter__ + // special method (of type mp_getiter_fun_t). Can use the given mp_obj_iter_buf_t + // to store the iterator object, otherwise can return a pointer to an object on the heap. + // - If MP_TYPE_FLAG_ITER_IS_ITERNEXT is set, then this corresponds to __next__ special method. + // May return MP_OBJ_STOP_ITERATION as an optimisation instead of raising StopIteration() + // with no args. The type will implicitly implement getiter as "return self". + // - If MP_TYPE_FLAG_ITER_IS_CUSTOM is set, then this slot must point to an + // mp_getiter_iternext_custom_t instance with both the getiter and iternext fields set. + // - If MP_TYPE_FLAG_ITER_IS_STREAM is set, this this slot should be unset. + uint8_t slot_index_iter; + + // Implements the buffer protocol if supported by this type. + uint8_t slot_index_buffer; + + // One of disjoint protocols (interfaces), like mp_stream_p_t, etc. + uint8_t slot_index_protocol; + + // A pointer to the parents of this type: + // - 0 parents: pointer is NULL (object is implicitly the single parent) + // - 1 parent: a pointer to the type of that parent + // - 2 or more parents: pointer to a tuple object containing the parent types + uint8_t slot_index_parent; + + // A dict mapping qstrs to objects local methods/constants/etc. + uint8_t slot_index_locals_dict; + + const void *slots[]; +}; + +// Non-variable sized versions of mp_obj_type_t to be used as a member +// in other structs or for dynamic allocation. The fields are exactly +// as in mp_obj_type_t, but with a fixed size for the flexible array +// members. +typedef struct _mp_obj_empty_type_t { + mp_obj_base_t base; + uint16_t flags; + uint16_t name; + + uint8_t slot_index_make_new; + uint8_t slot_index_print; + uint8_t slot_index_call; + uint8_t slot_index_unary_op; + uint8_t slot_index_binary_op; + uint8_t slot_index_attr; + uint8_t slot_index_subscr; + uint8_t slot_index_iter; + uint8_t slot_index_buffer; + uint8_t slot_index_protocol; + uint8_t slot_index_parent; + uint8_t slot_index_locals_dict; + + // No slots member. +} mp_obj_empty_type_t; + +typedef struct _mp_obj_full_type_t { + mp_obj_base_t base; + uint16_t flags; + uint16_t name; + + uint8_t slot_index_make_new; + uint8_t slot_index_print; + uint8_t slot_index_call; + uint8_t slot_index_unary_op; + uint8_t slot_index_binary_op; + uint8_t slot_index_attr; + uint8_t slot_index_subscr; + uint8_t slot_index_iter; + uint8_t slot_index_buffer; + uint8_t slot_index_protocol; + uint8_t slot_index_parent; + uint8_t slot_index_locals_dict; + + // Explicitly add 12 slots. + const void *slots[11]; +} mp_obj_full_type_t; + +#define _MP_OBJ_TYPE_SLOT_TYPE_make_new (mp_make_new_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_print (mp_print_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_call (mp_call_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_unary_op (mp_unary_op_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_binary_op (mp_binary_op_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_attr (mp_attr_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_subscr (mp_subscr_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_iter (const void *) +#define _MP_OBJ_TYPE_SLOT_TYPE_buffer (mp_buffer_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_protocol (const void *) +#define _MP_OBJ_TYPE_SLOT_TYPE_parent (const void *) +#define _MP_OBJ_TYPE_SLOT_TYPE_locals_dict (struct _mp_obj_dict_t *) + +// Implementation of MP_DEFINE_CONST_OBJ_TYPE for each number of arguments. +// Do not use these directly, instead use MP_DEFINE_CONST_OBJ_TYPE. +// Generated with: +// for i in range(13): +// print(f"#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_{i}(_struct_type, _typename, _name, _flags{''.join(f', f{j+1}, v{j+1}' for j in range(i))}) const _struct_type _typename = {{ .base = {{ &mp_type_type }}, .name = _name, .flags = _flags{''.join(f', .slot_index_##f{j+1} = {j+1}' for j in range(i))}{', .slots = { ' + ''.join(f'v{j+1}, ' for j in range(i)) + '}' if i else '' } }}") +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_0(_struct_type, _typename, _name, _flags) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_1(_struct_type, _typename, _name, _flags, f1, v1) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slots = { v1, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_2(_struct_type, _typename, _name, _flags, f1, v1, f2, v2) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slots = { v1, v2, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_3(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slots = { v1, v2, v3, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_4(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slots = { v1, v2, v3, v4, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_5(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slots = { v1, v2, v3, v4, v5, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_6(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slots = { v1, v2, v3, v4, v5, v6, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_7(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slots = { v1, v2, v3, v4, v5, v6, v7, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_8(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_9(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_10(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slot_index_##f10 = 10, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_11(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10, f11, v11) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slot_index_##f10 = 10, .slot_index_##f11 = 11, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_12(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10, f11, v11, f12, v12) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slot_index_##f10 = 10, .slot_index_##f11 = 11, .slot_index_##f12 = 12, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, } } + +// Because the mp_obj_type_t instances are in (zero-initialised) ROM, we take +// slot_index_foo=0 to mean that the slot is unset. This also simplifies checking +// if the slot is set. That means that we need to store index+1 in slot_index_foo +// though and then access it as slots[slot_index_foo - 1]. This is an implementation +// detail, the user of these macros doesn't need to be aware of it, and when using +// MP_OBJ_TYPE_OFFSETOF_SLOT you should use zero-based indexing. +#define MP_OBJ_TYPE_HAS_SLOT(t, f) ((t)->slot_index_##f) +#define MP_OBJ_TYPE_GET_SLOT(t, f) (_MP_OBJ_TYPE_SLOT_TYPE_##f(t)->slots[(t)->slot_index_##f - 1]) +#define MP_OBJ_TYPE_GET_SLOT_OR_NULL(t, f) (_MP_OBJ_TYPE_SLOT_TYPE_##f(MP_OBJ_TYPE_HAS_SLOT(t, f) ? MP_OBJ_TYPE_GET_SLOT(t, f) : NULL)) +#define MP_OBJ_TYPE_SET_SLOT(t, f, v, n) ((t)->slot_index_##f = (n) + 1, (t)->slots[(n)] = (void *)v) +#define MP_OBJ_TYPE_OFFSETOF_SLOT(f) (offsetof(mp_obj_type_t, slot_index_##f)) +#define MP_OBJ_TYPE_HAS_SLOT_BY_OFFSET(t, offset) (*(uint8_t *)((char *)(t) + (offset)) != 0) + +// Workaround for https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview?view=msvc-160#macro-arguments-are-unpacked +#define MP_DEFINE_CONST_OBJ_TYPE_EXPAND(x) x + +// This macro evaluates to MP_DEFINE_CONST_OBJ_TYPE_NARGS_##N, where N is the value +// of the 29th argument (29 is 13*2 + 3). +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, N, ...) MP_DEFINE_CONST_OBJ_TYPE_NARGS_##N + +// This macros is used to define a object type in ROM. +// Invoke as MP_DEFINE_CONST_OBJ_TYPE(_typename, _name, _flags, _make_new [, slot, func]*) +// It uses the number of arguments to select which MP_DEFINE_CONST_OBJ_TYPE_* +// macro to use based on the number of arguments. It works by shifting the +// numeric values 12, 11, ... 0 by the number of arguments, such that the +// 29th argument ends up being the number to use. The _INV values are +// placeholders because the slot arguments come in pairs. +#define MP_DEFINE_CONST_OBJ_TYPE(...) MP_DEFINE_CONST_OBJ_TYPE_EXPAND(MP_DEFINE_CONST_OBJ_TYPE_NARGS(__VA_ARGS__, _INV, 12, _INV, 11, _INV, 10, _INV, 9, _INV, 8, _INV, 7, _INV, 6, _INV, 5, _INV, 4, _INV, 3, _INV, 2, _INV, 1, _INV, 0)(mp_obj_type_t, __VA_ARGS__)) + +// Constant types, globally accessible +extern const mp_obj_type_t mp_type_type; +extern const mp_obj_type_t mp_type_object; +extern const mp_obj_type_t mp_type_NoneType; +extern const mp_obj_type_t mp_type_bool; +extern const mp_obj_type_t mp_type_int; +extern const mp_obj_type_t mp_type_str; +extern const mp_obj_type_t mp_type_bytes; +extern const mp_obj_type_t mp_type_bytearray; +extern const mp_obj_type_t mp_type_memoryview; +extern const mp_obj_type_t mp_type_float; +extern const mp_obj_type_t mp_type_complex; +extern const mp_obj_type_t mp_type_tuple; +extern const mp_obj_type_t mp_type_list; +extern const mp_obj_type_t mp_type_map; // map (the python builtin, not the dict implementation detail) +extern const mp_obj_type_t mp_type_enumerate; +extern const mp_obj_type_t mp_type_filter; +extern const mp_obj_type_t mp_type_deque; +extern const mp_obj_type_t mp_type_dict; +extern const mp_obj_type_t mp_type_ordereddict; +extern const mp_obj_type_t mp_type_range; +extern const mp_obj_type_t mp_type_set; +extern const mp_obj_type_t mp_type_frozenset; +extern const mp_obj_type_t mp_type_slice; +extern const mp_obj_type_t mp_type_zip; +extern const mp_obj_type_t mp_type_array; +extern const mp_obj_type_t mp_type_super; +extern const mp_obj_type_t mp_type_gen_wrap; +extern const mp_obj_type_t mp_type_native_gen_wrap; +extern const mp_obj_type_t mp_type_gen_instance; +extern const mp_obj_type_t mp_type_fun_builtin_0; +extern const mp_obj_type_t mp_type_fun_builtin_1; +extern const mp_obj_type_t mp_type_fun_builtin_2; +extern const mp_obj_type_t mp_type_fun_builtin_3; +extern const mp_obj_type_t mp_type_fun_builtin_var; +extern const mp_obj_type_t mp_type_fun_bc; +extern const mp_obj_type_t mp_type_fun_native; +extern const mp_obj_type_t mp_type_fun_viper; +extern const mp_obj_type_t mp_type_fun_asm; +extern const mp_obj_type_t mp_type_module; +extern const mp_obj_type_t mp_type_staticmethod; +extern const mp_obj_type_t mp_type_classmethod; +extern const mp_obj_type_t mp_type_bound_meth; +extern const mp_obj_type_t mp_type_property; +extern const mp_obj_type_t mp_type_stringio; +extern const mp_obj_type_t mp_type_bytesio; +extern const mp_obj_type_t mp_type_reversed; +extern const mp_obj_type_t mp_type_polymorph_iter; +#if MICROPY_ENABLE_FINALISER +extern const mp_obj_type_t mp_type_polymorph_iter_with_finaliser; +#endif + +// Exceptions +extern const mp_obj_type_t mp_type_BaseException; +extern const mp_obj_type_t mp_type_ArithmeticError; +extern const mp_obj_type_t mp_type_AssertionError; +extern const mp_obj_type_t mp_type_AttributeError; +extern const mp_obj_type_t mp_type_EOFError; +extern const mp_obj_type_t mp_type_Exception; +extern const mp_obj_type_t mp_type_GeneratorExit; +extern const mp_obj_type_t mp_type_ImportError; +extern const mp_obj_type_t mp_type_IndentationError; +extern const mp_obj_type_t mp_type_IndexError; +extern const mp_obj_type_t mp_type_KeyboardInterrupt; +extern const mp_obj_type_t mp_type_KeyError; +extern const mp_obj_type_t mp_type_LookupError; +extern const mp_obj_type_t mp_type_MemoryError; +extern const mp_obj_type_t mp_type_NameError; +extern const mp_obj_type_t mp_type_NotImplementedError; +extern const mp_obj_type_t mp_type_OSError; +extern const mp_obj_type_t mp_type_OverflowError; +extern const mp_obj_type_t mp_type_RuntimeError; +extern const mp_obj_type_t mp_type_StopAsyncIteration; +extern const mp_obj_type_t mp_type_StopIteration; +extern const mp_obj_type_t mp_type_SyntaxError; +extern const mp_obj_type_t mp_type_SystemExit; +extern const mp_obj_type_t mp_type_TypeError; +extern const mp_obj_type_t mp_type_UnicodeError; +extern const mp_obj_type_t mp_type_ValueError; +extern const mp_obj_type_t mp_type_ViperTypeError; +extern const mp_obj_type_t mp_type_ZeroDivisionError; + +// Constant objects, globally accessible: None, False, True +// These should always be accessed via the below macros. +#if MICROPY_OBJ_IMMEDIATE_OBJS +// None is even while False/True are odd so their types can be distinguished with 1 bit. +#define mp_const_none MP_OBJ_NEW_IMMEDIATE_OBJ(0) +#define mp_const_false MP_OBJ_NEW_IMMEDIATE_OBJ(1) +#define mp_const_true MP_OBJ_NEW_IMMEDIATE_OBJ(3) +#else +#define mp_const_none (MP_OBJ_FROM_PTR(&mp_const_none_obj)) +#define mp_const_false (MP_OBJ_FROM_PTR(&mp_const_false_obj)) +#define mp_const_true (MP_OBJ_FROM_PTR(&mp_const_true_obj)) +extern const struct _mp_obj_none_t mp_const_none_obj; +extern const struct _mp_obj_bool_t mp_const_false_obj; +extern const struct _mp_obj_bool_t mp_const_true_obj; +#endif + +// Constant objects, globally accessible: b'', (), {}, Ellipsis, NotImplemented, GeneratorExit() +// The below macros are for convenience only. +#define mp_const_empty_bytes (MP_OBJ_FROM_PTR(&mp_const_empty_bytes_obj)) +#define mp_const_empty_tuple (MP_OBJ_FROM_PTR(&mp_const_empty_tuple_obj)) +#define mp_const_notimplemented (MP_OBJ_FROM_PTR(&mp_const_notimplemented_obj)) +extern const struct _mp_obj_str_t mp_const_empty_bytes_obj; +extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj; +extern const struct _mp_obj_dict_t mp_const_empty_dict_obj; +extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj; +extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj; +extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj; + +// Fixed empty map. Useful when calling keyword-receiving functions +// without any keywords from C, etc. +#define mp_const_empty_map (mp_const_empty_dict_obj.map) + +// General API for objects + +// Helper versions of m_new_obj when you need to immediately set base.type. +// Implementing this as a call rather than inline saves 8 bytes per usage. +#define mp_obj_malloc(struct_type, obj_type) ((struct_type *)mp_obj_malloc_helper(sizeof(struct_type), obj_type)) +#define mp_obj_malloc_var(struct_type, var_field, var_type, var_num, obj_type) ((struct_type *)mp_obj_malloc_helper(offsetof(struct_type, var_field) + sizeof(var_type) * (var_num), obj_type)) +void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); + +// Object allocation macros for allocating objects that have a finaliser. +#if MICROPY_ENABLE_FINALISER +#define mp_obj_malloc_with_finaliser(struct_type, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(sizeof(struct_type), obj_type)) +#define mp_obj_malloc_var_with_finaliser(struct_type, var_type, var_num, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(sizeof(struct_type) + sizeof(var_type) * (var_num), obj_type)) +void *mp_obj_malloc_with_finaliser_helper(size_t num_bytes, const mp_obj_type_t *type); +#else +#define mp_obj_malloc_with_finaliser(struct_type, obj_type) mp_obj_malloc(struct_type, obj_type) +#define mp_obj_malloc_var_with_finaliser(struct_type, var_type, var_num, obj_type) mp_obj_malloc_var(struct_type, var_type, var_num, obj_type) +#endif + +// These macros are derived from more primitive ones and are used to +// check for more specific object types. +// Note: these are kept as macros because inline functions sometimes use much +// more code space than the equivalent macros, depending on the compiler. +// don't use mp_obj_is_exact_type directly; use mp_obj_is_type which provides additional safety checks. +// use the former only if you need to bypass these checks (because you've already checked everything else) +#define mp_obj_is_exact_type(o, t) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type == (t))) + +// Type checks are split to a separate, constant result macro. This is so it doesn't hinder the compilers's +// optimizations (other tricks like using ({ expr; exper; }) or (exp, expr, expr) in mp_obj_is_type() result +// in missed optimizations) +#define mp_type_assert_not_bool_int_str_nonetype(t) ( \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_bool), assert((t) != &mp_type_bool), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_int), assert((t) != &mp_type_int), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_str), assert((t) != &mp_type_str), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_NoneType), assert((t) != &mp_type_NoneType), \ + 1) + +#define mp_obj_is_type(o, t) (mp_type_assert_not_bool_int_str_nonetype(t) && mp_obj_is_exact_type(o, t)) +#if MICROPY_OBJ_IMMEDIATE_OBJS +// bool's are immediates, not real objects, so test for the 2 possible values. +#define mp_obj_is_bool(o) ((o) == mp_const_false || (o) == mp_const_true) +#else +#define mp_obj_is_bool(o) mp_obj_is_exact_type(o, &mp_type_bool) +#endif +#define mp_obj_is_int(o) (mp_obj_is_small_int(o) || mp_obj_is_exact_type(o, &mp_type_int)) +#define mp_obj_is_str(o) (mp_obj_is_qstr(o) || mp_obj_is_exact_type(o, &mp_type_str)) +#define mp_obj_is_str_or_bytes(o) (mp_obj_is_qstr(o) || (mp_obj_is_obj(o) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type, binary_op) == mp_obj_str_binary_op)) +bool mp_obj_is_dict_or_ordereddict(mp_obj_t o); +#define mp_obj_is_fun(o) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->name == MP_QSTR_function)) + +mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); +static inline mp_obj_t mp_obj_new_bool(mp_int_t x) { + return x ? mp_const_true : mp_const_false; +} +mp_obj_t mp_obj_new_cell(mp_obj_t obj); +mp_obj_t mp_obj_new_int(mp_int_t value); +mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value); +mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base); +mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception) +mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception) +mp_obj_t mp_obj_new_str(const char *data, size_t len); // will check utf-8 (raises UnicodeError) +mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len); // input data must be valid utf-8 +mp_obj_t mp_obj_new_str_from_vstr(vstr_t *vstr); // will check utf-8 (raises UnicodeError) +#if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK +mp_obj_t mp_obj_new_str_from_utf8_vstr(vstr_t *vstr); // input data must be valid utf-8 +#else +#define mp_obj_new_str_from_utf8_vstr mp_obj_new_str_from_vstr +#endif +mp_obj_t mp_obj_new_bytes_from_vstr(vstr_t *vstr); +mp_obj_t mp_obj_new_bytes(const byte *data, size_t len); +mp_obj_t mp_obj_new_bytearray(size_t n, const void *items); +mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items); +#if MICROPY_PY_BUILTINS_FLOAT +mp_obj_t mp_obj_new_int_from_float(mp_float_t val); +mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag); +#endif +mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type); +mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args); +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE +#define mp_obj_new_exception_msg(exc_type, msg) mp_obj_new_exception(exc_type) +#define mp_obj_new_exception_msg_varg(exc_type, ...) mp_obj_new_exception(exc_type) +#else +mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); +mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) +#endif +#ifdef va_start +mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list arg); // same fmt restrictions as above +#endif +mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun); +mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed, const mp_obj_t *closed); +mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items); +mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items); +mp_obj_t mp_obj_new_dict(size_t n_args); +mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items); +mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step); +mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self); +mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args, mp_obj_iter_buf_t *iter_buf); +mp_obj_t mp_obj_new_module(qstr module_name); +mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items); + +const mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in); +const char *mp_obj_get_type_str(mp_const_obj_t o_in); +bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo); // arguments should be type objects +mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type); + +void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); +void mp_obj_print(mp_obj_t o, mp_print_kind_t kind); +void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc); + +bool mp_obj_is_true(mp_obj_t arg); +bool mp_obj_is_callable(mp_obj_t o_in); +mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2); +bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2); + +// returns true if o is bool, small int or long int +static inline bool mp_obj_is_integer(mp_const_obj_t o) { + return mp_obj_is_int(o) || mp_obj_is_bool(o); +} + +mp_int_t mp_obj_get_int(mp_const_obj_t arg); +mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg); +bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value); +#if MICROPY_PY_BUILTINS_FLOAT +mp_float_t mp_obj_get_float(mp_obj_t self_in); +bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value); +void mp_obj_get_complex(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); +bool mp_obj_get_complex_maybe(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); +#endif +void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items); // *items may point inside a GC block +void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items); // *items may point inside a GC block +size_t mp_get_index(const mp_obj_type_t *type, size_t len, mp_obj_t index, bool is_slice); +mp_obj_t mp_obj_id(mp_obj_t o_in); +mp_obj_t mp_obj_len(mp_obj_t o_in); +mp_obj_t mp_obj_len_maybe(mp_obj_t o_in); // may return MP_OBJ_NULL +mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val); + +// cell + +typedef struct _mp_obj_cell_t { + mp_obj_base_t base; + mp_obj_t obj; +} mp_obj_cell_t; + +static inline mp_obj_t mp_obj_cell_get(mp_obj_t self_in) { + mp_obj_cell_t *self = (mp_obj_cell_t *)MP_OBJ_TO_PTR(self_in); + return self->obj; +} + +static inline void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj) { + mp_obj_cell_t *self = (mp_obj_cell_t *)MP_OBJ_TO_PTR(self_in); + self->obj = obj; +} + +// int +// For long int, returns value truncated to mp_int_t +mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in); +// Will raise exception if value doesn't fit into mp_int_t +mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in); +// Will raise exception if value is negative or doesn't fit into mp_uint_t +mp_uint_t mp_obj_int_get_uint_checked(mp_const_obj_t self_in); + +// exception +bool mp_obj_is_native_exception_instance(mp_obj_t self_in); +bool mp_obj_is_exception_type(mp_obj_t self_in); +bool mp_obj_is_exception_instance(mp_obj_t self_in); +bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type); +void mp_obj_exception_clear_traceback(mp_obj_t self_in); +void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block); +void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values); +mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in); +mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); +mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in); +void mp_init_emergency_exception_buf(void); +static inline mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); + return mp_obj_exception_make_new(exc_type, 1, 0, &arg); +} + +// str +bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2); +qstr mp_obj_str_get_qstr(mp_obj_t self_in); // use this if you will anyway convert the string to a qstr +const char *mp_obj_str_get_str(mp_obj_t self_in); // use this only if you need the string to be null terminated +const char *mp_obj_str_get_data(mp_obj_t self_in, size_t *len); +mp_obj_t mp_obj_str_intern(mp_obj_t str); +mp_obj_t mp_obj_str_intern_checked(mp_obj_t obj); +void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t str_len, bool is_bytes); + +#if MICROPY_PY_BUILTINS_FLOAT +// float +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +static inline float mp_obj_get_float_to_f(mp_obj_t o) { + return mp_obj_get_float(o); +} + +static inline double mp_obj_get_float_to_d(mp_obj_t o) { + return (double)mp_obj_get_float(o); +} + +static inline mp_obj_t mp_obj_new_float_from_f(float o) { + return mp_obj_new_float(o); +} + +static inline mp_obj_t mp_obj_new_float_from_d(double o) { + return mp_obj_new_float((mp_float_t)o); +} +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +static inline float mp_obj_get_float_to_f(mp_obj_t o) { + return (float)mp_obj_get_float(o); +} + +static inline double mp_obj_get_float_to_d(mp_obj_t o) { + return mp_obj_get_float(o); +} + +static inline mp_obj_t mp_obj_new_float_from_f(float o) { + return mp_obj_new_float((mp_float_t)o); +} + +static inline mp_obj_t mp_obj_new_float_from_d(double o) { + return mp_obj_new_float(o); +} +#endif +#if MICROPY_FLOAT_HIGH_QUALITY_HASH +mp_int_t mp_float_hash(mp_float_t val); +#else +static inline mp_int_t mp_float_hash(mp_float_t val) { + return (mp_int_t)val; +} +#endif +mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t rhs); // can return MP_OBJ_NULL if op not supported + +// complex +void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); +mp_obj_t mp_obj_complex_binary_op(mp_binary_op_t op, mp_float_t lhs_real, mp_float_t lhs_imag, mp_obj_t rhs_in); // can return MP_OBJ_NULL if op not supported +#else +#define mp_obj_is_float(o) (false) +#endif + +// tuple +void mp_obj_tuple_get(mp_obj_t self_in, size_t *len, mp_obj_t **items); +void mp_obj_tuple_del(mp_obj_t self_in); +mp_int_t mp_obj_tuple_hash(mp_obj_t self_in); + +// list +mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg); +mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value); +void mp_obj_list_get(mp_obj_t self_in, size_t *len, mp_obj_t **items); +void mp_obj_list_set_len(mp_obj_t self_in, size_t len); +void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value); +mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); + +// dict +typedef struct _mp_obj_dict_t { + mp_obj_base_t base; + mp_map_t map; +} mp_obj_dict_t; +mp_obj_t mp_obj_dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); +void mp_obj_dict_init(mp_obj_dict_t *dict, size_t n_args); +size_t mp_obj_dict_len(mp_obj_t self_in); +mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index); +mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); +mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key); +mp_obj_t mp_obj_dict_copy(mp_obj_t self_in); +static inline mp_map_t *mp_obj_dict_get_map(mp_obj_t dict) { + return &((mp_obj_dict_t *)MP_OBJ_TO_PTR(dict))->map; +} + +// set +void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item); + +// slice indexes resolved to particular sequence +typedef struct { + mp_int_t start; + mp_int_t stop; + mp_int_t step; +} mp_bound_slice_t; + +// slice +typedef struct _mp_obj_slice_t { + mp_obj_base_t base; + mp_obj_t start; + mp_obj_t stop; + mp_obj_t step; +} mp_obj_slice_t; +void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *result); + +// functions + +typedef struct _mp_obj_fun_builtin_fixed_t { + mp_obj_base_t base; + union { + mp_fun_0_t _0; + mp_fun_1_t _1; + mp_fun_2_t _2; + mp_fun_3_t _3; + } fun; +} mp_obj_fun_builtin_fixed_t; + +typedef struct _mp_obj_fun_builtin_var_t { + mp_obj_base_t base; + uint32_t sig; // see MP_OBJ_FUN_MAKE_SIG + union { + mp_fun_var_t var; + mp_fun_kw_t kw; + } fun; +} mp_obj_fun_builtin_var_t; + +qstr mp_obj_fun_get_name(mp_const_obj_t fun); + +mp_obj_t mp_identity(mp_obj_t self); +MP_DECLARE_CONST_FUN_OBJ_1(mp_identity_obj); + +// module +typedef struct _mp_obj_module_t { + mp_obj_base_t base; + mp_obj_dict_t *globals; +} mp_obj_module_t; +static inline mp_obj_dict_t *mp_obj_module_get_globals(mp_obj_t module) { + return ((mp_obj_module_t *)MP_OBJ_TO_PTR(module))->globals; +} + +// staticmethod and classmethod types; defined here so we can make const versions +// this structure is used for instances of both staticmethod and classmethod +typedef struct _mp_obj_static_class_method_t { + mp_obj_base_t base; + mp_obj_t fun; +} mp_obj_static_class_method_t; +typedef struct _mp_rom_obj_static_class_method_t { + mp_obj_base_t base; + mp_rom_obj_t fun; +} mp_rom_obj_static_class_method_t; + +// property +const mp_obj_t *mp_obj_property_get(mp_obj_t self_in); + +// sequence helpers + +void mp_seq_multiply(const void *items, size_t item_sz, size_t len, size_t times, void *dest); +#if MICROPY_PY_BUILTINS_SLICE +bool mp_seq_get_fast_slice_indexes(mp_uint_t len, mp_obj_t slice, mp_bound_slice_t *indexes); +#endif +#define mp_seq_copy(dest, src, len, item_t) memcpy(dest, src, len * sizeof(item_t)) +#define mp_seq_cat(dest, src1, len1, src2, len2, item_t) { memcpy(dest, src1, (len1) * sizeof(item_t)); memcpy(dest + (len1), src2, (len2) * sizeof(item_t)); } +bool mp_seq_cmp_bytes(mp_uint_t op, const byte *data1, size_t len1, const byte *data2, size_t len2); +bool mp_seq_cmp_objs(mp_uint_t op, const mp_obj_t *items1, size_t len1, const mp_obj_t *items2, size_t len2); +mp_obj_t mp_seq_index_obj(const mp_obj_t *items, size_t len, size_t n_args, const mp_obj_t *args); +mp_obj_t mp_seq_count_obj(const mp_obj_t *items, size_t len, mp_obj_t value); +mp_obj_t mp_seq_extract_slice(size_t len, const mp_obj_t *seq, mp_bound_slice_t *indexes); + +// Helper to clear stale pointers from allocated, but unused memory, to preclude GC problems +#define mp_seq_clear(start, len, alloc_len, item_sz) memset((byte *)(start) + (len) * (item_sz), 0, ((alloc_len) - (len)) * (item_sz)) + +// Note: dest and slice regions may overlap +#define mp_seq_replace_slice_no_grow(dest, dest_len, beg, end, slice, slice_len, item_sz) \ + memmove(((char *)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); \ + memmove(((char *)dest) + (beg + slice_len) * (item_sz), ((char *)dest) + (end) * (item_sz), (dest_len - end) * (item_sz)); + +// Note: dest and slice regions may overlap +#define mp_seq_replace_slice_grow_inplace(dest, dest_len, beg, end, slice, slice_len, len_adj, item_sz) \ + memmove(((char *)dest) + (beg + slice_len) * (item_sz), ((char *)dest) + (end) * (item_sz), ((dest_len) + (len_adj) - ((beg) + (slice_len))) * (item_sz)); \ + memmove(((char *)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); + +// Provide translation for legacy API +#define MP_OBJ_IS_SMALL_INT mp_obj_is_small_int +#define MP_OBJ_IS_QSTR mp_obj_is_qstr +#define MP_OBJ_IS_OBJ mp_obj_is_obj +#define MP_OBJ_IS_INT mp_obj_is_int +#define MP_OBJ_IS_TYPE mp_obj_is_type +#define MP_OBJ_IS_STR mp_obj_is_str +#define MP_OBJ_IS_STR_OR_BYTES mp_obj_is_str_or_bytes +#define MP_OBJ_IS_FUN mp_obj_is_fun +#define MP_MAP_SLOT_IS_FILLED mp_map_slot_is_filled +#define MP_SET_SLOT_IS_FILLED mp_set_slot_is_filled + +#endif // MICROPY_INCLUDED_PY_OBJ_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objarray.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objarray.c new file mode 100644 index 00000000..1fff2348 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objarray.c @@ -0,0 +1,717 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/runtime.h" +#include "py/binary.h" +#include "py/objstr.h" +#include "py/objarray.h" + +#if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW + +// About memoryview object: We want to reuse as much code as possible from +// array, and keep the memoryview object 4 words in size so it fits in 1 GC +// block. Also, memoryview must keep a pointer to the base of the buffer so +// that the buffer is not GC'd if the original parent object is no longer +// around (we are assuming that all memoryview'able objects return a pointer +// which points to the start of a GC chunk). Given the above constraints we +// do the following: +// - typecode high bit is set if the buffer is read-write (else read-only) +// - free is the offset in elements to the first item in the memoryview +// - len is the length in elements +// - items points to the start of the original buffer +// Note that we don't handle the case where the original buffer might change +// size due to a resize of the original parent object. + +#if MICROPY_PY_BUILTINS_MEMORYVIEW +#define TYPECODE_MASK (0x7f) +#define memview_offset free +#define memview_offset_max ((1LL << MP_OBJ_ARRAY_FREE_SIZE_BITS) - 1) +#else +// make (& TYPECODE_MASK) a null operation if memorview not enabled +#define TYPECODE_MASK (~(size_t)0) +// memview_offset should not be accessed if memoryview is not enabled, +// so not defined to catch errors +#endif + +static mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf); +static mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg); +static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in); +static mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); + +/******************************************************************************/ +// array + +#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY +static void array_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in); + if (o->typecode == BYTEARRAY_TYPECODE) { + mp_print_str(print, "bytearray(b"); + mp_str_print_quoted(print, o->items, o->len, true); + } else { + mp_printf(print, "array('%c'", o->typecode); + if (o->len > 0) { + mp_print_str(print, ", ["); + for (size_t i = 0; i < o->len; i++) { + if (i > 0) { + mp_print_str(print, ", "); + } + mp_obj_print_helper(print, mp_binary_get_val_array(o->typecode, o->items, i), PRINT_REPR); + } + mp_print_str(print, "]"); + } + } + mp_print_str(print, ")"); +} +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY +static mp_obj_array_t *array_new(char typecode, size_t n) { + int typecode_size = mp_binary_get_size('@', typecode, NULL); + mp_obj_array_t *o = m_new_obj(mp_obj_array_t); + #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_PY_ARRAY + o->base.type = (typecode == BYTEARRAY_TYPECODE) ? &mp_type_bytearray : &mp_type_array; + #elif MICROPY_PY_BUILTINS_BYTEARRAY + o->base.type = &mp_type_bytearray; + #else + o->base.type = &mp_type_array; + #endif + o->typecode = typecode; + o->free = 0; + o->len = n; + o->items = m_new(byte, typecode_size * o->len); + return o; +} +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY +static mp_obj_t array_construct(char typecode, mp_obj_t initializer) { + // bytearrays can be raw-initialised from anything with the buffer protocol + // other arrays can only be raw-initialised from bytes and bytearray objects + mp_buffer_info_t bufinfo; + if (((MICROPY_PY_BUILTINS_BYTEARRAY + && typecode == BYTEARRAY_TYPECODE) + || (MICROPY_PY_ARRAY + && (mp_obj_is_type(initializer, &mp_type_bytes) + || (MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(initializer, &mp_type_bytearray))))) + && mp_get_buffer(initializer, &bufinfo, MP_BUFFER_READ)) { + // construct array from raw bytes + // we round-down the len to make it a multiple of sz (CPython raises error) + size_t sz = mp_binary_get_size('@', typecode, NULL); + size_t len = bufinfo.len / sz; + mp_obj_array_t *o = array_new(typecode, len); + memcpy(o->items, bufinfo.buf, len * sz); + return MP_OBJ_FROM_PTR(o); + } + + size_t len; + // Try to create array of exact len if initializer len is known + mp_obj_t len_in = mp_obj_len_maybe(initializer); + if (len_in == MP_OBJ_NULL) { + len = 0; + } else { + len = MP_OBJ_SMALL_INT_VALUE(len_in); + } + + mp_obj_array_t *array = array_new(typecode, len); + + mp_obj_t iterable = mp_getiter(initializer, NULL); + mp_obj_t item; + size_t i = 0; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (len == 0) { + array_append(MP_OBJ_FROM_PTR(array), item); + } else { + mp_binary_set_val_array(typecode, array->items, i++, item); + } + } + + return MP_OBJ_FROM_PTR(array); +} +#endif + +#if MICROPY_PY_ARRAY +static mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 1, 2, false); + + // get typecode + const char *typecode = mp_obj_str_get_str(args[0]); + + if (n_args == 1) { + // 1 arg: make an empty array + return MP_OBJ_FROM_PTR(array_new(*typecode, 0)); + } else { + // 2 args: construct the array from the given object + return array_construct(*typecode, args[1]); + } +} +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY +static mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + // Can take 2nd/3rd arg if constructs from str + mp_arg_check_num(n_args, n_kw, 0, 3, false); + + if (n_args == 0) { + // no args: construct an empty bytearray + return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0)); + } else if (mp_obj_is_int(args[0])) { + // 1 arg, an integer: construct a blank bytearray of that length + mp_uint_t len = mp_obj_get_int(args[0]); + mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len); + memset(o->items, 0, len); + return MP_OBJ_FROM_PTR(o); + } else { + // 1 arg: construct the bytearray from that + if (mp_obj_is_str(args[0]) && n_args == 1) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // Match bytes_make_new. + mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments")); + #else + mp_raise_TypeError(MP_ERROR_TEXT("string argument without an encoding")); + #endif + } + return array_construct(BYTEARRAY_TYPECODE, args[0]); + } +} +#endif + +#if MICROPY_PY_BUILTINS_MEMORYVIEW + +mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) { + mp_obj_array_t *self = m_new_obj(mp_obj_array_t); + mp_obj_memoryview_init(self, typecode, 0, nitems, items); + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + + // TODO possibly allow memoryview constructor to take start/stop so that one + // can do memoryview(b, 4, 8) instead of memoryview(b)[4:8] (uses less RAM) + + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + + mp_obj_array_t *self = MP_OBJ_TO_PTR(mp_obj_new_memoryview(bufinfo.typecode, + bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL), + bufinfo.buf)); + + // If the input object is a memoryview then need to point the items of the + // new memoryview to the start of the buffer so the GC can trace it. + if (mp_obj_get_type(args[0]) == &mp_type_memoryview) { + mp_obj_array_t *other = MP_OBJ_TO_PTR(args[0]); + self->memview_offset = other->memview_offset; + self->items = other->items; + } + + // test if the object can be written to + if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) { + self->typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW; // indicate writable buffer + } + + return MP_OBJ_FROM_PTR(self); +} + +#if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE +static void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + return; + } + if (attr == MP_QSTR_itemsize) { + mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); + dest[0] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL)); + } + #if MICROPY_PY_BUILTINS_BYTES_HEX + else { + // Need to forward to locals dict. + dest[1] = MP_OBJ_SENTINEL; + } + #endif +} +#endif + +#endif + +static mp_obj_t array_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(o->len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(o->len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +static int typecode_for_comparison(int typecode, bool *is_unsigned) { + if (typecode == BYTEARRAY_TYPECODE) { + typecode = 'B'; + } + if (typecode <= 'Z') { + typecode += 32; // to lowercase + *is_unsigned = true; + } + return typecode; +} + +static mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + mp_obj_array_t *lhs = MP_OBJ_TO_PTR(lhs_in); + switch (op) { + case MP_BINARY_OP_ADD: { + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (lhs->base.type == &mp_type_memoryview) { + return MP_OBJ_NULL; // op not supported + } + #endif + + // allow to add anything that has the buffer protocol (extension to CPython) + mp_buffer_info_t lhs_bufinfo; + mp_buffer_info_t rhs_bufinfo; + array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ); + mp_get_buffer_raise(rhs_in, &rhs_bufinfo, MP_BUFFER_READ); + + size_t sz = mp_binary_get_size('@', lhs_bufinfo.typecode, NULL); + + // convert byte count to element count (in case rhs is not multiple of sz) + size_t rhs_len = rhs_bufinfo.len / sz; + + // note: lhs->len is element count of lhs, lhs_bufinfo.len is byte count + mp_obj_array_t *res = array_new(lhs_bufinfo.typecode, lhs->len + rhs_len); + mp_seq_cat((byte *)res->items, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_len * sz, byte); + return MP_OBJ_FROM_PTR(res); + } + + case MP_BINARY_OP_INPLACE_ADD: { + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (lhs->base.type == &mp_type_memoryview) { + return MP_OBJ_NULL; // op not supported + } + #endif + array_extend(lhs_in, rhs_in); + return lhs_in; + } + + case MP_BINARY_OP_CONTAINS: { + #if MICROPY_PY_BUILTINS_BYTEARRAY + // Can search string only in bytearray + mp_buffer_info_t lhs_bufinfo; + mp_buffer_info_t rhs_bufinfo; + if (mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) { + if (!mp_obj_is_type(lhs_in, &mp_type_bytearray)) { + return mp_const_false; + } + array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ); + return mp_obj_new_bool( + find_subbytes(lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len, 1) != NULL); + } + #endif + + // Otherwise, can only look for a scalar numeric value in an array + if (mp_obj_is_int(rhs_in) || mp_obj_is_float(rhs_in)) { + mp_raise_NotImplementedError(NULL); + } + + return mp_const_false; + } + + case MP_BINARY_OP_EQUAL: + case MP_BINARY_OP_LESS: + case MP_BINARY_OP_LESS_EQUAL: + case MP_BINARY_OP_MORE: + case MP_BINARY_OP_MORE_EQUAL: { + mp_buffer_info_t lhs_bufinfo; + mp_buffer_info_t rhs_bufinfo; + array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ); + if (!mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) { + return mp_const_false; + } + // mp_seq_cmp_bytes is used so only compatible representations can be correctly compared. + // The type doesn't matter: array/bytearray/str/bytes all have the same buffer layout, so + // just check if the typecodes are compatible; for testing equality the types should have the + // same code except for signedness, and not be floating point because nan never equals nan. + // For > and < the types should be the same and unsigned. + // Note that typecode_for_comparison always returns lowercase letters to save code size. + // No need for (& TYPECODE_MASK) here: xxx_get_buffer already takes care of that. + bool is_unsigned = false; + const int lhs_code = typecode_for_comparison(lhs_bufinfo.typecode, &is_unsigned); + const int rhs_code = typecode_for_comparison(rhs_bufinfo.typecode, &is_unsigned); + if (lhs_code == rhs_code && lhs_code != 'f' && lhs_code != 'd' && (op == MP_BINARY_OP_EQUAL || is_unsigned)) { + return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len)); + } + // mp_obj_equal_not_equal treats returning MP_OBJ_NULL as 'fall back to pointer comparison' + // for MP_BINARY_OP_EQUAL but that is incompatible with CPython. + mp_raise_NotImplementedError(NULL); + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY +static mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg) { + // self is not a memoryview, so we don't need to use (& TYPECODE_MASK) + assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray)) + || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array))); + mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->free == 0) { + size_t item_sz = mp_binary_get_size('@', self->typecode, NULL); + // TODO: alloc policy + self->free = 8; + self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + self->free)); + mp_seq_clear(self->items, self->len + 1, self->len + self->free, item_sz); + } + mp_binary_set_val_array(self->typecode, self->items, self->len, arg); + // only update length/free if set succeeded + self->len++; + self->free--; + return mp_const_none; // return None, as per CPython +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_append_obj, array_append); + +static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) { + // self is not a memoryview, so we don't need to use (& TYPECODE_MASK) + assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray)) + || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array))); + mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); + + // allow to extend by anything that has the buffer protocol (extension to CPython) + mp_buffer_info_t arg_bufinfo; + mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ); + + size_t sz = mp_binary_get_size('@', self->typecode, NULL); + + // convert byte count to element count + size_t len = arg_bufinfo.len / sz; + + // make sure we have enough room to extend + // TODO: alloc policy; at the moment we go conservative + if (self->free < len) { + self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz); + self->free = 0; + } else { + self->free -= len; + } + + // extend + mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte); + self->len += len; + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj, array_extend); +#endif + +static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete item + // TODO implement + // TODO: confirmed that both bytearray and array.array support + // slice deletion + return MP_OBJ_NULL; // op not supported + } else { + mp_obj_array_t *o = MP_OBJ_TO_PTR(self_in); + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index_in, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(o->len, index_in, &slice)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported")); + } + if (value != MP_OBJ_SENTINEL) { + #if MICROPY_PY_ARRAY_SLICE_ASSIGN + // Assign + size_t src_len; + void *src_items; + size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL); + if (mp_obj_is_obj(value) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type, subscr) == array_subscr) { + // value is array, bytearray or memoryview + mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value); + if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) { + compat_error: + mp_raise_ValueError(MP_ERROR_TEXT("lhs and rhs should be compatible")); + } + src_len = src_slice->len; + src_items = src_slice->items; + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (mp_obj_is_type(value, &mp_type_memoryview)) { + src_items = (uint8_t *)src_items + (src_slice->memview_offset * item_sz); + } + #endif + } else if (mp_obj_is_type(value, &mp_type_bytes)) { + if (item_sz != 1) { + goto compat_error; + } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ); + src_len = bufinfo.len; + src_items = bufinfo.buf; + } else { + mp_raise_NotImplementedError(MP_ERROR_TEXT("array/bytes required on right side")); + } + + // TODO: check src/dst compat + mp_int_t len_adj = src_len - (slice.stop - slice.start); + uint8_t *dest_items = o->items; + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (o->base.type == &mp_type_memoryview) { + if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) { + // store to read-only memoryview not allowed + return MP_OBJ_NULL; + } + if (len_adj != 0) { + goto compat_error; + } + dest_items += o->memview_offset * item_sz; + } + #endif + if (len_adj > 0) { + if ((size_t)len_adj > o->free) { + // TODO: alloc policy; at the moment we go conservative + o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz); + o->free = len_adj; + dest_items = o->items; + } + mp_seq_replace_slice_grow_inplace(dest_items, o->len, + slice.start, slice.stop, src_items, src_len, len_adj, item_sz); + } else { + mp_seq_replace_slice_no_grow(dest_items, o->len, + slice.start, slice.stop, src_items, src_len, item_sz); + // Clear "freed" elements at the end of list + // TODO: This is actually only needed for typecode=='O' + mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz); + // TODO: alloc policy after shrinking + } + o->free -= len_adj; + o->len += len_adj; + return mp_const_none; + #else + return MP_OBJ_NULL; // op not supported + #endif + } + + mp_obj_array_t *res; + size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL); + assert(sz > 0); + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (o->base.type == &mp_type_memoryview) { + if (slice.start > memview_offset_max) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("memoryview offset too large")); + } + res = m_new_obj(mp_obj_array_t); + *res = *o; + res->memview_offset += slice.start; + res->len = slice.stop - slice.start; + } else + #endif + { + res = array_new(o->typecode, slice.stop - slice.start); + memcpy(res->items, (uint8_t *)o->items + slice.start * sz, (slice.stop - slice.start) * sz); + } + return MP_OBJ_FROM_PTR(res); + } else + #endif + { + size_t index = mp_get_index(o->base.type, o->len, index_in, false); + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (o->base.type == &mp_type_memoryview) { + index += o->memview_offset; + if (value != MP_OBJ_SENTINEL && !(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) { + // store to read-only memoryview + return MP_OBJ_NULL; + } + } + #endif + if (value == MP_OBJ_SENTINEL) { + // load + return mp_binary_get_val_array(o->typecode & TYPECODE_MASK, o->items, index); + } else { + // store + mp_binary_set_val_array(o->typecode & TYPECODE_MASK, o->items, index, value); + return mp_const_none; + } + } + } +} + +static mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in); + size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL); + bufinfo->buf = o->items; + bufinfo->len = o->len * sz; + bufinfo->typecode = o->typecode & TYPECODE_MASK; + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (o->base.type == &mp_type_memoryview) { + if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW) && (flags & MP_BUFFER_WRITE)) { + // read-only memoryview + return 1; + } + bufinfo->buf = (uint8_t *)bufinfo->buf + (size_t)o->memview_offset * sz; + } + #else + (void)flags; + #endif + return 0; +} + +#if MICROPY_PY_ARRAY +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_array, + MP_QSTR_array, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, array_make_new, + print, array_print, + iter, array_iterator_new, + unary_op, array_unary_op, + binary_op, array_binary_op, + subscr, array_subscr, + buffer, array_get_buffer, + locals_dict, &mp_obj_array_locals_dict + ); +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bytearray, + MP_QSTR_bytearray, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, bytearray_make_new, + print, array_print, + iter, array_iterator_new, + unary_op, array_unary_op, + binary_op, array_binary_op, + subscr, array_subscr, + buffer, array_get_buffer, + locals_dict, &mp_obj_bytearray_locals_dict + ); +#endif + +#if MICROPY_PY_BUILTINS_MEMORYVIEW +#if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE +#define MEMORYVIEW_TYPE_ATTR attr, memoryview_attr, +#else +#define MEMORYVIEW_TYPE_ATTR +#endif + +#if MICROPY_PY_BUILTINS_BYTES_HEX +#define MEMORYVIEW_TYPE_LOCALS_DICT locals_dict, &mp_obj_memoryview_locals_dict, +#else +#define MEMORYVIEW_TYPE_LOCALS_DICT +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_memoryview, + MP_QSTR_memoryview, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, memoryview_make_new, + iter, array_iterator_new, + unary_op, array_unary_op, + binary_op, array_binary_op, + MEMORYVIEW_TYPE_LOCALS_DICT + MEMORYVIEW_TYPE_ATTR + subscr, array_subscr, + buffer, array_get_buffer + ); +#endif // MICROPY_PY_BUILTINS_MEMORYVIEW + +/* unused +size_t mp_obj_array_len(mp_obj_t self_in) { + return ((mp_obj_array_t *)self_in)->len; +} +*/ + +#if MICROPY_PY_BUILTINS_BYTEARRAY +mp_obj_t mp_obj_new_bytearray(size_t n, const void *items) { + mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n); + memcpy(o->items, items, n); + return MP_OBJ_FROM_PTR(o); +} + +// Create bytearray which references specified memory area +mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) { + mp_obj_array_t *o = mp_obj_malloc(mp_obj_array_t, &mp_type_bytearray); + o->typecode = BYTEARRAY_TYPECODE; + o->free = 0; + o->len = n; + o->items = items; + return MP_OBJ_FROM_PTR(o); +} +#endif + +/******************************************************************************/ +// array iterator + +typedef struct _mp_obj_array_it_t { + mp_obj_base_t base; + mp_obj_array_t *array; + size_t offset; + size_t cur; +} mp_obj_array_it_t; + +static mp_obj_t array_it_iternext(mp_obj_t self_in) { + mp_obj_array_it_t *self = MP_OBJ_TO_PTR(self_in); + if (self->cur < self->array->len) { + return mp_binary_get_val_array(self->array->typecode & TYPECODE_MASK, self->array->items, self->offset + self->cur++); + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_array_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, array_it_iternext + ); + +static mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_array_t *array = MP_OBJ_TO_PTR(array_in); + mp_obj_array_it_t *o = (mp_obj_array_it_t *)iter_buf; + o->base.type = &mp_type_array_it; + o->array = array; + o->offset = 0; + o->cur = 0; + #if MICROPY_PY_BUILTINS_MEMORYVIEW + if (array->base.type == &mp_type_memoryview) { + o->offset = array->memview_offset; + } + #endif + return MP_OBJ_FROM_PTR(o); +} + +#endif // MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objarray.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objarray.h new file mode 100644 index 00000000..4a0e8a98 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objarray.h @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJARRAY_H +#define MICROPY_INCLUDED_PY_OBJARRAY_H + +#include "py/obj.h" + +// Used only for memoryview types, set in "typecode" to indicate a writable memoryview +#define MP_OBJ_ARRAY_TYPECODE_FLAG_RW (0x80) + +// Bit size used for mp_obj_array_t.free member. +#define MP_OBJ_ARRAY_FREE_SIZE_BITS (8 * sizeof(size_t) - 8) + +// This structure is used for all of bytearray, array.array, memoryview +// objects. Note that memoryview has different meaning for some fields, +// see comment at the beginning of objarray.c. +typedef struct _mp_obj_array_t { + mp_obj_base_t base; + size_t typecode : 8; + // free is number of unused elements after len used elements + // alloc size = len + free + // But for memoryview, 'free' is reused as offset (in elements) into the + // parent object. (Union is not used to not go into a complication of + // union-of-bitfields with different toolchains). See comments in + // objarray.c. + size_t free : MP_OBJ_ARRAY_FREE_SIZE_BITS; + size_t len; // in elements + void *items; +} mp_obj_array_t; + +#if MICROPY_PY_BUILTINS_MEMORYVIEW +static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, size_t offset, size_t len, void *items) { + self->base.type = &mp_type_memoryview; + self->typecode = typecode; + self->free = offset; + self->len = len; + self->items = items; +} +#endif + +#if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY +MP_DECLARE_CONST_FUN_OBJ_2(mp_obj_array_append_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj); +#endif + +#endif // MICROPY_INCLUDED_PY_OBJARRAY_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objattrtuple.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objattrtuple.c new file mode 100644 index 00000000..1280e330 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objattrtuple.c @@ -0,0 +1,96 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Damien P. George + * + * 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 "py/objtuple.h" + +#if MICROPY_PY_ATTRTUPLE || MICROPY_PY_COLLECTIONS + +// this helper function is used by collections.namedtuple +#if !MICROPY_PY_COLLECTIONS +static +#endif +void mp_obj_attrtuple_print_helper(const mp_print_t *print, const qstr *fields, mp_obj_tuple_t *o) { + mp_print_str(print, "("); + for (size_t i = 0; i < o->len; i++) { + if (i > 0) { + mp_print_str(print, ", "); + } + mp_printf(print, "%q=", fields[i]); + mp_obj_print_helper(print, o->items[i], PRINT_REPR); + } + mp_print_str(print, ")"); +} + +#endif + +#if MICROPY_PY_ATTRTUPLE + +static void mp_obj_attrtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_tuple_t *o = MP_OBJ_TO_PTR(o_in); + const qstr *fields = (const qstr *)MP_OBJ_TO_PTR(o->items[o->len]); + mp_obj_attrtuple_print_helper(print, fields, o); +} + +static void mp_obj_attrtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] == MP_OBJ_NULL) { + // load attribute + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + size_t len = self->len; + const qstr *fields = (const qstr *)MP_OBJ_TO_PTR(self->items[len]); + for (size_t i = 0; i < len; i++) { + if (fields[i] == attr) { + dest[0] = self->items[i]; + return; + } + } + } +} + +mp_obj_t mp_obj_new_attrtuple(const qstr *fields, size_t n, const mp_obj_t *items) { + mp_obj_tuple_t *o = mp_obj_malloc_var(mp_obj_tuple_t, items, mp_obj_t, n + 1, &mp_type_attrtuple); + o->len = n; + for (size_t i = 0; i < n; i++) { + o->items[i] = items[i]; + } + o->items[n] = MP_OBJ_FROM_PTR(fields); + return MP_OBJ_FROM_PTR(o); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_attrtuple, + MP_QSTR_tuple, + MP_TYPE_FLAG_ITER_IS_GETITER, + // reuse tuple to save on a qstr + print, mp_obj_attrtuple_print, + unary_op, mp_obj_tuple_unary_op, + binary_op, mp_obj_tuple_binary_op, + attr, mp_obj_attrtuple_attr, + subscr, mp_obj_tuple_subscr, + iter, mp_obj_tuple_getiter + ); + +#endif // MICROPY_PY_ATTRTUPLE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objbool.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objbool.c new file mode 100644 index 00000000..5b3e3660 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objbool.c @@ -0,0 +1,101 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +#if MICROPY_OBJ_IMMEDIATE_OBJS + +#define BOOL_VALUE(o) ((o) == mp_const_false ? 0 : 1) + +#else + +#define BOOL_VALUE(o) (((mp_obj_bool_t *)MP_OBJ_TO_PTR(o))->value) + +typedef struct _mp_obj_bool_t { + mp_obj_base_t base; + bool value; +} mp_obj_bool_t; + +#endif + +static void bool_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bool value = BOOL_VALUE(self_in); + if (MICROPY_PY_JSON && kind == PRINT_JSON) { + if (value) { + mp_print_str(print, "true"); + } else { + mp_print_str(print, "false"); + } + } else { + if (value) { + mp_print_str(print, "True"); + } else { + mp_print_str(print, "False"); + } + } +} + +static mp_obj_t bool_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + if (n_args == 0) { + return mp_const_false; + } else { + return mp_obj_new_bool(mp_obj_is_true(args[0])); + } +} + +static mp_obj_t bool_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + if (op == MP_UNARY_OP_LEN) { + return MP_OBJ_NULL; + } + bool value = BOOL_VALUE(o_in); + return mp_unary_op(op, MP_OBJ_NEW_SMALL_INT(value)); +} + +static mp_obj_t bool_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + bool value = BOOL_VALUE(lhs_in); + return mp_binary_op(op, MP_OBJ_NEW_SMALL_INT(value), rhs_in); +} + +MP_DEFINE_CONST_OBJ_TYPE( + // can match all numeric types + mp_type_bool, + MP_QSTR_bool, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, + make_new, bool_make_new, + print, bool_print, + unary_op, bool_unary_op, + binary_op, bool_binary_op + ); + +#if !MICROPY_OBJ_IMMEDIATE_OBJS +const mp_obj_bool_t mp_const_false_obj = {{&mp_type_bool}, false}; +const mp_obj_bool_t mp_const_true_obj = {{&mp_type_bool}, true}; +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objboundmeth.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objboundmeth.c new file mode 100644 index 00000000..e3503ff1 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objboundmeth.c @@ -0,0 +1,148 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" +#include "py/runtime.h" + +typedef struct _mp_obj_bound_meth_t { + mp_obj_base_t base; + mp_obj_t meth; + mp_obj_t self; +} mp_obj_bound_meth_t; + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +static void bound_meth_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_bound_meth_t *o = MP_OBJ_TO_PTR(o_in); + mp_printf(print, "self, PRINT_REPR); + mp_print_str(print, "."); + mp_obj_print_helper(print, o->meth, PRINT_REPR); + mp_print_str(print, ">"); +} +#endif + +mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // need to insert self before all other args and then call meth + size_t n_total = n_args + 2 * n_kw; + mp_obj_t *args2 = NULL; + #if MICROPY_ENABLE_PYSTACK + args2 = mp_pystack_alloc(sizeof(mp_obj_t) * (1 + n_total)); + #else + mp_obj_t *free_args2 = NULL; + if (n_total > 4) { + // try to use heap to allocate temporary args array + args2 = m_new_maybe(mp_obj_t, 1 + n_total); + free_args2 = args2; + } + if (args2 == NULL) { + // (fallback to) use stack to allocate temporary args array + args2 = alloca(sizeof(mp_obj_t) * (1 + n_total)); + } + #endif + args2[0] = self; + memcpy(args2 + 1, args, n_total * sizeof(mp_obj_t)); + mp_obj_t res = mp_call_function_n_kw(meth, n_args + 1, n_kw, args2); + #if MICROPY_ENABLE_PYSTACK + mp_pystack_free(args2); + #else + if (free_args2 != NULL) { + m_del(mp_obj_t, free_args2, 1 + n_total); + } + #endif + return res; +} + +static mp_obj_t bound_meth_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in); + return mp_call_method_self_n_kw(self->meth, self->self, n_args, n_kw, args); +} + +static mp_obj_t bound_meth_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_HASH: + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)self->self ^ (mp_uint_t)self->meth); + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t bound_meth_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + // The MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE flag is clear for this type, so if this + // function is called with MP_BINARY_OP_EQUAL then lhs_in and rhs_in must have the + // same type, which is mp_type_bound_meth. + if (op != MP_BINARY_OP_EQUAL) { + return MP_OBJ_NULL; // op not supported + } + mp_obj_bound_meth_t *lhs = MP_OBJ_TO_PTR(lhs_in); + mp_obj_bound_meth_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool(lhs->self == rhs->self && lhs->meth == rhs->meth); +} + +#if MICROPY_PY_FUNCTION_ATTRS +static void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + // Delegate the load to the method object + mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in); + mp_load_method_maybe(self->meth, attr, dest); +} +#endif + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +#define BOUND_METH_TYPE_PRINT print, bound_meth_print, +#else +#define BOUND_METH_TYPE_PRINT +#endif + +#if MICROPY_PY_FUNCTION_ATTRS +#define BOUND_METH_TYPE_ATTR attr, bound_meth_attr, +#else +#define BOUND_METH_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bound_meth, + MP_QSTR_bound_method, + MP_TYPE_FLAG_NONE, + BOUND_METH_TYPE_PRINT + BOUND_METH_TYPE_ATTR + call, bound_meth_call, + unary_op, bound_meth_unary_op, + binary_op, bound_meth_binary_op + ); + +mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self) { + mp_obj_bound_meth_t *o = mp_obj_malloc(mp_obj_bound_meth_t, &mp_type_bound_meth); + o->meth = meth; + o->self = self; + return MP_OBJ_FROM_PTR(o); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objcell.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objcell.c new file mode 100644 index 00000000..95966c79 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objcell.c @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +static void cell_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_cell_t *o = MP_OBJ_TO_PTR(o_in); + mp_printf(print, "obj); + if (o->obj == MP_OBJ_NULL) { + mp_print_str(print, "(nil)"); + } else { + mp_obj_print_helper(print, o->obj, PRINT_REPR); + } + mp_print_str(print, ">"); +} +#endif + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +#define CELL_TYPE_PRINT , print, cell_print +#else +#define CELL_TYPE_PRINT +#endif + +static MP_DEFINE_CONST_OBJ_TYPE( + // cell representation is just value in < > + mp_type_cell, MP_QSTR_, MP_TYPE_FLAG_NONE + CELL_TYPE_PRINT + ); + +mp_obj_t mp_obj_new_cell(mp_obj_t obj) { + mp_obj_cell_t *o = mp_obj_malloc(mp_obj_cell_t, &mp_type_cell); + o->obj = obj; + return MP_OBJ_FROM_PTR(o); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objclosure.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objclosure.c new file mode 100644 index 00000000..3ba507b9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objclosure.c @@ -0,0 +1,113 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" +#include "py/runtime.h" + +typedef struct _mp_obj_closure_t { + mp_obj_base_t base; + mp_obj_t fun; + size_t n_closed; + mp_obj_t closed[]; +} mp_obj_closure_t; + +static mp_obj_t closure_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_obj_closure_t *self = MP_OBJ_TO_PTR(self_in); + + // need to concatenate closed-over-vars and args + + size_t n_total = self->n_closed + n_args + 2 * n_kw; + if (n_total <= 5) { + // use stack to allocate temporary args array + mp_obj_t args2[5]; + memcpy(args2, self->closed, self->n_closed * sizeof(mp_obj_t)); + memcpy(args2 + self->n_closed, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t)); + return mp_call_function_n_kw(self->fun, self->n_closed + n_args, n_kw, args2); + } else { + // use heap to allocate temporary args array + mp_obj_t *args2 = m_new(mp_obj_t, n_total); + memcpy(args2, self->closed, self->n_closed * sizeof(mp_obj_t)); + memcpy(args2 + self->n_closed, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t)); + mp_obj_t res = mp_call_function_n_kw(self->fun, self->n_closed + n_args, n_kw, args2); + m_del(mp_obj_t, args2, n_total); + return res; + } +} + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +static void closure_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_closure_t *o = MP_OBJ_TO_PTR(o_in); + mp_print_str(print, "fun, PRINT_REPR); + mp_printf(print, " at %p, n_closed=%u ", o, (int)o->n_closed); + for (size_t i = 0; i < o->n_closed; i++) { + if (o->closed[i] == MP_OBJ_NULL) { + mp_print_str(print, "(nil)"); + } else { + mp_obj_print_helper(print, o->closed[i], PRINT_REPR); + } + mp_print_str(print, " "); + } + mp_print_str(print, ">"); +} +#endif + +#if MICROPY_PY_FUNCTION_ATTRS +static void mp_obj_closure_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + // forward to self_in->fun + mp_obj_closure_t *o = MP_OBJ_TO_PTR(self_in); + mp_load_method_maybe(o->fun, attr, dest); +} +#define CLOSURE_TYPE_ATTR attr, mp_obj_closure_attr, +#else +#define CLOSURE_TYPE_ATTR +#endif + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +#define CLOSURE_TYPE_PRINT print, closure_print, +#else +#define CLOSURE_TYPE_PRINT +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_closure, + MP_QSTR_closure, + MP_TYPE_FLAG_BINDS_SELF, + CLOSURE_TYPE_ATTR + CLOSURE_TYPE_PRINT + call, closure_call + ); + +mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed_over, const mp_obj_t *closed) { + mp_obj_closure_t *o = mp_obj_malloc_var(mp_obj_closure_t, closed, mp_obj_t, n_closed_over, &mp_type_closure); + o->fun = fun; + o->n_closed = n_closed_over; + memcpy(o->closed, closed, n_closed_over * sizeof(mp_obj_t)); + return MP_OBJ_FROM_PTR(o); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objcomplex.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objcomplex.c new file mode 100644 index 00000000..85b58528 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objcomplex.c @@ -0,0 +1,262 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/parsenum.h" +#include "py/runtime.h" + +#if MICROPY_PY_BUILTINS_COMPLEX + +#include +#include "py/formatfloat.h" + +typedef struct _mp_obj_complex_t { + mp_obj_base_t base; + mp_float_t real; + mp_float_t imag; +} mp_obj_complex_t; + +static void complex_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_complex_t *o = MP_OBJ_TO_PTR(o_in); + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + char buf[16]; + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + const int precision = 6; + #else + const int precision = 7; + #endif + #else + char buf[32]; + const int precision = 16; + #endif + if (o->real == 0) { + mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0'); + mp_printf(print, "%sj", buf); + } else { + mp_format_float(o->real, buf, sizeof(buf), 'g', precision, '\0'); + mp_printf(print, "(%s", buf); + if (o->imag >= 0 || isnan(o->imag)) { + mp_print_str(print, "+"); + } + mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0'); + mp_printf(print, "%sj)", buf); + } +} + +static mp_obj_t complex_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 0, 2, false); + + switch (n_args) { + case 0: + return mp_obj_new_complex(0, 0); + + case 1: + if (mp_obj_is_str(args[0])) { + // a string, parse it + size_t l; + const char *s = mp_obj_str_get_data(args[0], &l); + return mp_parse_num_complex(s, l, NULL); + } else if (mp_obj_is_type(args[0], &mp_type_complex)) { + // a complex, just return it + return args[0]; + } else { + mp_float_t real, imag; + mp_obj_get_complex(args[0], &real, &imag); + return mp_obj_new_complex(real, imag); + } + + case 2: + default: { + mp_float_t real, imag; + if (mp_obj_is_type(args[0], &mp_type_complex)) { + mp_obj_complex_get(args[0], &real, &imag); + } else { + real = mp_obj_get_float(args[0]); + imag = 0; + } + if (mp_obj_is_type(args[1], &mp_type_complex)) { + mp_float_t real2, imag2; + mp_obj_complex_get(args[1], &real2, &imag2); + real -= imag2; + imag += real2; + } else { + imag += mp_obj_get_float(args[1]); + } + return mp_obj_new_complex(real, imag); + } + } +} + +static mp_obj_t complex_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + mp_obj_complex_t *o = MP_OBJ_TO_PTR(o_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(o->real != 0 || o->imag != 0); + case MP_UNARY_OP_HASH: + return MP_OBJ_NEW_SMALL_INT(mp_float_hash(o->real) ^ mp_float_hash(o->imag)); + case MP_UNARY_OP_POSITIVE: + return o_in; + case MP_UNARY_OP_NEGATIVE: + return mp_obj_new_complex(-o->real, -o->imag); + case MP_UNARY_OP_ABS: + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(o->real * o->real + o->imag * o->imag)); + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t complex_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + mp_obj_complex_t *lhs = MP_OBJ_TO_PTR(lhs_in); + return mp_obj_complex_binary_op(op, lhs->real, lhs->imag, rhs_in); +} + +static void complex_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + mp_obj_complex_t *self = MP_OBJ_TO_PTR(self_in); + if (attr == MP_QSTR_real) { + dest[0] = mp_obj_new_float(self->real); + } else if (attr == MP_QSTR_imag) { + dest[0] = mp_obj_new_float(self->imag); + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_complex, MP_QSTR_complex, MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, + make_new, complex_make_new, + print, complex_print, + unary_op, complex_unary_op, + binary_op, complex_binary_op, + attr, complex_attr + ); + +mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) { + mp_obj_complex_t *o = mp_obj_malloc(mp_obj_complex_t, &mp_type_complex); + o->real = real; + o->imag = imag; + return MP_OBJ_FROM_PTR(o); +} + +void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag) { + assert(mp_obj_is_type(self_in, &mp_type_complex)); + mp_obj_complex_t *self = MP_OBJ_TO_PTR(self_in); + *real = self->real; + *imag = self->imag; +} + +mp_obj_t mp_obj_complex_binary_op(mp_binary_op_t op, mp_float_t lhs_real, mp_float_t lhs_imag, mp_obj_t rhs_in) { + mp_float_t rhs_real, rhs_imag; + if (!mp_obj_get_complex_maybe(rhs_in, &rhs_real, &rhs_imag)) { + return MP_OBJ_NULL; // op not supported + } + + switch (op) { + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_INPLACE_ADD: + lhs_real += rhs_real; + lhs_imag += rhs_imag; + break; + case MP_BINARY_OP_SUBTRACT: + case MP_BINARY_OP_INPLACE_SUBTRACT: + lhs_real -= rhs_real; + lhs_imag -= rhs_imag; + break; + case MP_BINARY_OP_MULTIPLY: + case MP_BINARY_OP_INPLACE_MULTIPLY: { + mp_float_t real; + multiply: + real = lhs_real * rhs_real - lhs_imag * rhs_imag; + lhs_imag = lhs_real * rhs_imag + lhs_imag * rhs_real; + lhs_real = real; + break; + } + case MP_BINARY_OP_FLOOR_DIVIDE: + case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: + mp_raise_TypeError(MP_ERROR_TEXT("can't truncate-divide a complex number")); + + case MP_BINARY_OP_TRUE_DIVIDE: + case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: + if (rhs_imag == 0) { + if (rhs_real == 0) { + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("complex divide by zero")); + } + lhs_real /= rhs_real; + lhs_imag /= rhs_real; + } else if (rhs_real == 0) { + mp_float_t real = lhs_imag / rhs_imag; + lhs_imag = -lhs_real / rhs_imag; + lhs_real = real; + } else { + mp_float_t rhs_len_sq = rhs_real * rhs_real + rhs_imag * rhs_imag; + rhs_real /= rhs_len_sq; + rhs_imag /= -rhs_len_sq; + goto multiply; + } + break; + + case MP_BINARY_OP_POWER: + case MP_BINARY_OP_INPLACE_POWER: { + // z1**z2 = exp(z2*ln(z1)) + // = exp(z2*(ln(|z1|)+i*arg(z1))) + // = exp( (x2*ln1 - y2*arg1) + i*(y2*ln1 + x2*arg1) ) + // = exp(x3 + i*y3) + // = exp(x3)*(cos(y3) + i*sin(y3)) + mp_float_t abs1 = MICROPY_FLOAT_C_FUN(sqrt)(lhs_real * lhs_real + lhs_imag * lhs_imag); + if (abs1 == 0) { + if (rhs_imag == 0 && rhs_real >= 0) { + lhs_real = (rhs_real == 0); + } else { + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("0.0 to a complex power")); + } + } else { + mp_float_t ln1 = MICROPY_FLOAT_C_FUN(log)(abs1); + mp_float_t arg1 = MICROPY_FLOAT_C_FUN(atan2)(lhs_imag, lhs_real); + mp_float_t x3 = rhs_real * ln1 - rhs_imag * arg1; + mp_float_t y3 = rhs_imag * ln1 + rhs_real * arg1; + mp_float_t exp_x3 = MICROPY_FLOAT_C_FUN(exp)(x3); + lhs_real = exp_x3 * MICROPY_FLOAT_C_FUN(cos)(y3); + lhs_imag = exp_x3 * MICROPY_FLOAT_C_FUN(sin)(y3); + } + break; + } + + case MP_BINARY_OP_EQUAL: + return mp_obj_new_bool(lhs_real == rhs_real && lhs_imag == rhs_imag); + + default: + return MP_OBJ_NULL; // op not supported + } + return mp_obj_new_complex(lhs_real, lhs_imag); +} + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objdeque.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objdeque.c new file mode 100644 index 00000000..58353701 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objdeque.c @@ -0,0 +1,314 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Paul Sokolovsky + * + * 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 // for ssize_t + +#include "py/runtime.h" + +#if MICROPY_PY_COLLECTIONS_DEQUE + +typedef struct _mp_obj_deque_t { + mp_obj_base_t base; + size_t alloc; + size_t i_get; + size_t i_put; + mp_obj_t *items; + uint32_t flags; + #define FLAG_CHECK_OVERFLOW 1 +} mp_obj_deque_t; + +static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg); +static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in); +#if MICROPY_PY_COLLECTIONS_DEQUE_ITER +static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf); +#endif + +static mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 2, 3, false); + + // Protect against -1 leading to zero-length allocation and bad array access + mp_int_t maxlen = mp_obj_get_int(args[1]); + if (maxlen < 0) { + mp_raise_ValueError(NULL); + } + + mp_obj_deque_t *o = mp_obj_malloc(mp_obj_deque_t, type); + o->alloc = maxlen + 1; + o->i_get = o->i_put = 0; + o->items = m_new0(mp_obj_t, o->alloc); + + if (n_args > 2) { + o->flags = mp_obj_get_int(args[2]); + } + + mp_obj_deque_extend(MP_OBJ_FROM_PTR(o), args[0]); + + return MP_OBJ_FROM_PTR(o); +} + +static size_t deque_len(mp_obj_deque_t *self) { + ssize_t len = self->i_put - self->i_get; + if (len < 0) { + len += self->alloc; + } + return len; +} + +static mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(self->i_get != self->i_put); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(deque_len(self)); + + #if MICROPY_PY_SYS_GETSIZEOF + case MP_UNARY_OP_SIZEOF: { + size_t sz = sizeof(*self) + sizeof(mp_obj_t) * self->alloc; + return MP_OBJ_NEW_SMALL_INT(sz); + } + #endif + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg) { + mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + + size_t new_i_put = self->i_put + 1; + if (new_i_put == self->alloc) { + new_i_put = 0; + } + + if (self->flags & FLAG_CHECK_OVERFLOW && new_i_put == self->i_get) { + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("full")); + } + + self->items[self->i_put] = arg; + self->i_put = new_i_put; + + if (self->i_get == new_i_put) { + if (++self->i_get == self->alloc) { + self->i_get = 0; + } + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(deque_append_obj, mp_obj_deque_append); + +static mp_obj_t mp_obj_deque_appendleft(mp_obj_t self_in, mp_obj_t arg) { + mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + + size_t new_i_get = self->i_get - 1; + if (self->i_get == 0) { + new_i_get = self->alloc - 1; + } + + if (self->flags & FLAG_CHECK_OVERFLOW && new_i_get == self->i_put) { + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("full")); + } + + self->i_get = new_i_get; + self->items[self->i_get] = arg; + + // overwriting first element in deque + if (self->i_put == new_i_get) { + if (self->i_put == 0) { + self->i_put = self->alloc - 1; + } else { + self->i_put--; + } + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(deque_appendleft_obj, mp_obj_deque_appendleft); + +static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iter = mp_getiter(arg_in, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + mp_obj_deque_append(self_in, item); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(deque_extend_obj, mp_obj_deque_extend); + +static mp_obj_t deque_popleft(mp_obj_t self_in) { + mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->i_get == self->i_put) { + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty")); + } + + mp_obj_t ret = self->items[self->i_get]; + self->items[self->i_get] = MP_OBJ_NULL; + + if (++self->i_get == self->alloc) { + self->i_get = 0; + } + + return ret; +} +static MP_DEFINE_CONST_FUN_OBJ_1(deque_popleft_obj, deque_popleft); + +static mp_obj_t deque_pop(mp_obj_t self_in) { + mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->i_get == self->i_put) { + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty")); + } + + if (self->i_put == 0) { + self->i_put = self->alloc - 1; + } else { + self->i_put--; + } + + mp_obj_t ret = self->items[self->i_put]; + self->items[self->i_put] = MP_OBJ_NULL; + + return ret; +} +static MP_DEFINE_CONST_FUN_OBJ_1(deque_pop_obj, deque_pop); + +#if MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR +static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete not supported, fall back to mp_obj_subscr() error message + return MP_OBJ_NULL; + } + mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + + size_t offset = mp_get_index(self->base.type, deque_len(self), index, false); + size_t index_val = self->i_get + offset; + if (index_val > self->alloc) { + index_val -= self->alloc; + } + + if (value == MP_OBJ_SENTINEL) { + // load + return self->items[index_val]; + } else { + // store into deque + self->items[index_val] = value; + return mp_const_none; + } +} +#endif + +#if 0 +static mp_obj_t deque_clear(mp_obj_t self_in) { + mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + self->i_get = self->i_put = 0; + mp_seq_clear(self->items, 0, self->alloc, sizeof(*self->items)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(deque_clear_obj, deque_clear); +#endif + +static const mp_rom_map_elem_t deque_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&deque_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_appendleft), MP_ROM_PTR(&deque_appendleft_obj) }, + { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&deque_extend_obj) }, + #if 0 + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&deque_clear_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&deque_pop_obj) }, + { MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&deque_popleft_obj) }, +}; + +static MP_DEFINE_CONST_DICT(deque_locals_dict, deque_locals_dict_table); + +#if MICROPY_PY_COLLECTIONS_DEQUE_ITER +#define DEQUE_TYPE_FLAGS MP_TYPE_FLAG_ITER_IS_GETITER +#define DEQUE_TYPE_ITER iter, mp_obj_new_deque_it, +#else +#define DEQUE_TYPE_FLAGS MP_TYPE_FLAG_NONE +#define DEQUE_TYPE_ITER +#endif + +#if MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR +#define DEQUE_TYPE_SUBSCR subscr, deque_subscr, +#else +#define DEQUE_TYPE_SUBSCR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_deque, + MP_QSTR_deque, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, deque_make_new, + unary_op, deque_unary_op, + DEQUE_TYPE_SUBSCR + DEQUE_TYPE_ITER + locals_dict, &deque_locals_dict + ); + +/******************************************************************************/ +/* deque iterator */ + +#if MICROPY_PY_COLLECTIONS_DEQUE_ITER + +typedef struct _mp_obj_deque_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_t deque; + size_t cur; +} mp_obj_deque_it_t; + +static mp_obj_t deque_it_iternext(mp_obj_t self_in) { + mp_obj_deque_it_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_deque_t *deque = MP_OBJ_TO_PTR(self->deque); + if (self->cur != deque->i_put) { + mp_obj_t o_out = deque->items[self->cur]; + if (++self->cur == deque->alloc) { + self->cur = 0; + } + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf) { + mp_obj_deque_t *deque_ = MP_OBJ_TO_PTR(deque); + size_t i_get = deque_->i_get; + assert(sizeof(mp_obj_deque_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_deque_it_t *o = (mp_obj_deque_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = deque_it_iternext; + o->deque = deque; + o->cur = i_get; + return MP_OBJ_FROM_PTR(o); +} + +#endif + +#endif // MICROPY_PY_COLLECTIONS_DEQUE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objdict.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objdict.c new file mode 100644 index 00000000..cf64fa95 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objdict.c @@ -0,0 +1,671 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2017 Paul Sokolovsky + * + * 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 "py/runtime.h" +#include "py/builtin.h" +#include "py/objtype.h" +#include "py/objstr.h" + +bool mp_obj_is_dict_or_ordereddict(mp_obj_t o) { + return mp_obj_is_obj(o) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type, make_new) == mp_obj_dict_make_new; +} + +const mp_obj_dict_t mp_const_empty_dict_obj = { + .base = { .type = &mp_type_dict }, + .map = { + .all_keys_are_qstrs = 0, + .is_fixed = 1, + .is_ordered = 1, + .used = 0, + .alloc = 0, + .table = NULL, + } +}; + +static mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); + +// This is a helper function to iterate through a dictionary. The state of +// the iteration is held in *cur and should be initialised with zero for the +// first call. Will return NULL when no more elements are available. +static mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, size_t *cur) { + size_t max = dict->map.alloc; + mp_map_t *map = &dict->map; + + size_t i = *cur; + for (; i < max; i++) { + if (mp_map_slot_is_filled(map, i)) { + *cur = i + 1; + return &(map->table[i]); + } + } + + assert(map->used == 0 || i == max); + return NULL; +} + +static void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + bool first = true; + const char *item_separator = ", "; + const char *key_separator = ": "; + if (!(MICROPY_PY_JSON && kind == PRINT_JSON)) { + kind = PRINT_REPR; + } else { + #if MICROPY_PY_JSON_SEPARATORS + item_separator = MP_PRINT_GET_EXT(print)->item_separator; + key_separator = MP_PRINT_GET_EXT(print)->key_separator; + #endif + } + if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) { + mp_printf(print, "%q(", self->base.type->name); + } + mp_print_str(print, "{"); + size_t cur = 0; + mp_map_elem_t *next = NULL; + while ((next = dict_iter_next(self, &cur)) != NULL) { + if (!first) { + mp_print_str(print, item_separator); + } + first = false; + bool add_quote = MICROPY_PY_JSON && kind == PRINT_JSON && !mp_obj_is_str_or_bytes(next->key); + if (add_quote) { + mp_print_str(print, "\""); + } + mp_obj_print_helper(print, next->key, kind); + if (add_quote) { + mp_print_str(print, "\""); + } + mp_print_str(print, key_separator); + mp_obj_print_helper(print, next->value, kind); + } + mp_print_str(print, "}"); + if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) { + mp_print_str(print, ")"); + } +} + +mp_obj_t mp_obj_dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_obj_t dict_out = mp_obj_new_dict(0); + mp_obj_dict_t *dict = MP_OBJ_TO_PTR(dict_out); + dict->base.type = type; + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT + if (type == &mp_type_ordereddict) { + dict->map.is_ordered = 1; + } + #endif + if (n_args > 0 || n_kw > 0) { + mp_obj_t args2[2] = {dict_out, args[0]}; // args[0] is always valid, even if it's not a positional arg + mp_map_t kwargs; + mp_map_init_fixed_table(&kwargs, n_kw, args + n_args); + dict_update(n_args + 1, args2, &kwargs); // dict_update will check that n_args + 1 == 1 or 2 + } + return dict_out; +} + +static mp_obj_t dict_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(self->map.used != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(self->map.used); + #if MICROPY_PY_SYS_GETSIZEOF + case MP_UNARY_OP_SIZEOF: { + size_t sz = sizeof(*self) + sizeof(*self->map.table) * self->map.alloc; + return MP_OBJ_NEW_SMALL_INT(sz); + } + #endif + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + mp_obj_dict_t *o = MP_OBJ_TO_PTR(lhs_in); + switch (op) { + case MP_BINARY_OP_CONTAINS: { + mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP); + return mp_obj_new_bool(elem != NULL); + } + case MP_BINARY_OP_EQUAL: { + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT + if (MP_UNLIKELY(mp_obj_is_type(lhs_in, &mp_type_ordereddict) && mp_obj_is_type(rhs_in, &mp_type_ordereddict))) { + // Iterate through both dictionaries simultaneously and compare keys and values. + mp_obj_dict_t *rhs = MP_OBJ_TO_PTR(rhs_in); + size_t c1 = 0, c2 = 0; + mp_map_elem_t *e1 = dict_iter_next(o, &c1), *e2 = dict_iter_next(rhs, &c2); + for (; e1 != NULL && e2 != NULL; e1 = dict_iter_next(o, &c1), e2 = dict_iter_next(rhs, &c2)) { + if (!mp_obj_equal(e1->key, e2->key) || !mp_obj_equal(e1->value, e2->value)) { + return mp_const_false; + } + } + return e1 == NULL && e2 == NULL ? mp_const_true : mp_const_false; + } + #endif + + if (mp_obj_is_type(rhs_in, &mp_type_dict)) { + mp_obj_dict_t *rhs = MP_OBJ_TO_PTR(rhs_in); + if (o->map.used != rhs->map.used) { + return mp_const_false; + } + + size_t cur = 0; + mp_map_elem_t *next = NULL; + while ((next = dict_iter_next(o, &cur)) != NULL) { + mp_map_elem_t *elem = mp_map_lookup(&rhs->map, next->key, MP_MAP_LOOKUP); + if (elem == NULL || !mp_obj_equal(next->value, elem->value)) { + return mp_const_false; + } + } + return mp_const_true; + } else { + // dict is not equal to instance of any other type + return mp_const_false; + } + } + #if MICROPY_CPYTHON_COMPAT + case MP_BINARY_OP_INPLACE_OR: + case MP_BINARY_OP_OR: { + if (op == MP_BINARY_OP_OR) { + lhs_in = mp_obj_dict_copy(lhs_in); + } + mp_obj_t dicts[2] = {lhs_in, rhs_in}; + dict_update(2, dicts, (mp_map_t *)&mp_const_empty_map); + return lhs_in; + } + #endif + default: + // op not supported + return MP_OBJ_NULL; + } +} + +// Note: Make sure this is inlined in load part of dict_subscr() below. +mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) { + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP); + if (elem == NULL) { + mp_raise_type_arg(&mp_type_KeyError, index); + } else { + return elem->value; + } +} + +static mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete + mp_obj_dict_delete(self_in, index); + return mp_const_none; + } else if (value == MP_OBJ_SENTINEL) { + // load + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP); + if (elem == NULL) { + mp_raise_type_arg(&mp_type_KeyError, index); + } else { + return elem->value; + } + } else { + // store + mp_obj_dict_store(self_in, index, value); + return mp_const_none; + } +} + +/******************************************************************************/ +/* dict methods */ + +static void mp_ensure_not_fixed(const mp_obj_dict_t *dict) { + if (dict->map.is_fixed) { + mp_raise_TypeError(NULL); + } +} + +static mp_obj_t dict_clear(mp_obj_t self_in) { + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_ensure_not_fixed(self); + + mp_map_clear(&self->map); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); + +mp_obj_t mp_obj_dict_copy(mp_obj_t self_in) { + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t other_out = mp_obj_new_dict(self->map.alloc); + mp_obj_dict_t *other = MP_OBJ_TO_PTR(other_out); + other->base.type = self->base.type; + other->map.used = self->map.used; + other->map.all_keys_are_qstrs = self->map.all_keys_are_qstrs; + other->map.is_fixed = 0; + other->map.is_ordered = self->map.is_ordered; + memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); + return other_out; +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, mp_obj_dict_copy); + +#if MICROPY_PY_BUILTINS_DICT_FROMKEYS +// this is a classmethod +static mp_obj_t dict_fromkeys(size_t n_args, const mp_obj_t *args) { + mp_obj_t iter = mp_getiter(args[1], NULL); + mp_obj_t value = mp_const_none; + mp_obj_t next = MP_OBJ_NULL; + + if (n_args > 2) { + value = args[2]; + } + + // optimisation to allocate result based on len of argument + mp_obj_t self_out; + mp_obj_t len = mp_obj_len_maybe(args[1]); + if (len == MP_OBJ_NULL) { + /* object's type doesn't have a __len__ slot */ + self_out = mp_obj_new_dict(0); + } else { + self_out = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len)); + } + + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_out); + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; + } + + return self_out; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_fun_obj, 2, 3, dict_fromkeys); +static MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, MP_ROM_PTR(&dict_fromkeys_fun_obj)); +#endif + +static mp_obj_t dict_get_helper(size_t n_args, const mp_obj_t *args, mp_map_lookup_kind_t lookup_kind) { + mp_check_self(mp_obj_is_dict_or_ordereddict(args[0])); + mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]); + if (lookup_kind != MP_MAP_LOOKUP) { + mp_ensure_not_fixed(self); + } + mp_map_elem_t *elem = mp_map_lookup(&self->map, args[1], lookup_kind); + mp_obj_t value; + if (elem == NULL || elem->value == MP_OBJ_NULL) { + if (n_args == 2) { + if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) { + mp_raise_type_arg(&mp_type_KeyError, args[1]); + } else { + value = mp_const_none; + } + } else { + value = args[2]; + } + if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + elem->value = value; + } + } else { + value = elem->value; + if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) { + elem->value = MP_OBJ_NULL; // so that GC can collect the deleted value + } + } + return value; +} + +static mp_obj_t dict_get(size_t n_args, const mp_obj_t *args) { + return dict_get_helper(n_args, args, MP_MAP_LOOKUP); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_get_obj, 2, 3, dict_get); + +static mp_obj_t dict_pop(size_t n_args, const mp_obj_t *args) { + return dict_get_helper(n_args, args, MP_MAP_LOOKUP_REMOVE_IF_FOUND); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_pop_obj, 2, 3, dict_pop); + +static mp_obj_t dict_setdefault(size_t n_args, const mp_obj_t *args) { + return dict_get_helper(n_args, args, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setdefault); + +static mp_obj_t dict_popitem(mp_obj_t self_in) { + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_ensure_not_fixed(self); + if (self->map.used == 0) { + mp_raise_msg(&mp_type_KeyError, MP_ERROR_TEXT("popitem(): dictionary is empty")); + } + size_t cur = 0; + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT + if (self->map.is_ordered) { + cur = self->map.used - 1; + } + #endif + mp_map_elem_t *next = dict_iter_next(self, &cur); + assert(next); + self->map.used--; + mp_obj_t items[] = {next->key, next->value}; + next->key = MP_OBJ_SENTINEL; // must mark key as sentinel to indicate that it was deleted + next->value = MP_OBJ_NULL; + mp_obj_t tuple = mp_obj_new_tuple(2, items); + + return tuple; +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem); + +static mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + mp_check_self(mp_obj_is_dict_or_ordereddict(args[0])); + mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]); + mp_ensure_not_fixed(self); + + mp_arg_check_num(n_args, kwargs->used, 1, 2, true); + + if (n_args == 2) { + // given a positional argument + + if (mp_obj_is_dict_or_ordereddict(args[1])) { + // update from other dictionary (make sure other is not self) + if (args[1] != args[0]) { + size_t cur = 0; + mp_map_elem_t *elem = NULL; + while ((elem = dict_iter_next((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[1]), &cur)) != NULL) { + mp_map_lookup(&self->map, elem->key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = elem->value; + } + } + } else { + // update from a generic iterable of pairs + mp_obj_t iter = mp_getiter(args[1], NULL); + mp_obj_t next = MP_OBJ_NULL; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + mp_obj_t inneriter = mp_getiter(next, NULL); + mp_obj_t key = mp_iternext(inneriter); + mp_obj_t value = mp_iternext(inneriter); + mp_obj_t stop = mp_iternext(inneriter); + if (key == MP_OBJ_STOP_ITERATION + || value == MP_OBJ_STOP_ITERATION + || stop != MP_OBJ_STOP_ITERATION) { + mp_raise_ValueError(MP_ERROR_TEXT("dict update sequence has wrong length")); + } else { + mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; + } + } + } + } + + // update the dict with any keyword args + for (size_t i = 0; i < kwargs->alloc; i++) { + if (mp_map_slot_is_filled(kwargs, i)) { + mp_map_lookup(&self->map, kwargs->table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = kwargs->table[i].value; + } + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(dict_update_obj, 1, dict_update); + + +/******************************************************************************/ +/* dict views */ + +static const mp_obj_type_t mp_type_dict_view; +static const mp_obj_type_t mp_type_dict_view_it; + +typedef enum _mp_dict_view_kind_t { + MP_DICT_VIEW_ITEMS, + MP_DICT_VIEW_KEYS, + MP_DICT_VIEW_VALUES, +} mp_dict_view_kind_t; + +static const char *const mp_dict_view_names[] = {"dict_items", "dict_keys", "dict_values"}; + +typedef struct _mp_obj_dict_view_it_t { + mp_obj_base_t base; + mp_dict_view_kind_t kind; + mp_obj_t dict; + size_t cur; +} mp_obj_dict_view_it_t; + +typedef struct _mp_obj_dict_view_t { + mp_obj_base_t base; + mp_obj_t dict; + mp_dict_view_kind_t kind; +} mp_obj_dict_view_t; + +static mp_obj_t dict_view_it_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_dict_view_it)); + mp_obj_dict_view_it_t *self = MP_OBJ_TO_PTR(self_in); + mp_map_elem_t *next = dict_iter_next(MP_OBJ_TO_PTR(self->dict), &self->cur); + + if (next == NULL) { + return MP_OBJ_STOP_ITERATION; + } else { + switch (self->kind) { + case MP_DICT_VIEW_ITEMS: + default: { + mp_obj_t items[] = {next->key, next->value}; + return mp_obj_new_tuple(2, items); + } + case MP_DICT_VIEW_KEYS: + return next->key; + case MP_DICT_VIEW_VALUES: + return next->value; + } + } +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_dict_view_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, dict_view_it_iternext + ); + +static mp_obj_t dict_view_getiter(mp_obj_t view_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_dict_view_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_check_self(mp_obj_is_type(view_in, &mp_type_dict_view)); + mp_obj_dict_view_t *view = MP_OBJ_TO_PTR(view_in); + mp_obj_dict_view_it_t *o = (mp_obj_dict_view_it_t *)iter_buf; + o->base.type = &mp_type_dict_view_it; + o->kind = view->kind; + o->dict = view->dict; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} + +static void dict_view_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_check_self(mp_obj_is_type(self_in, &mp_type_dict_view)); + mp_obj_dict_view_t *self = MP_OBJ_TO_PTR(self_in); + bool first = true; + mp_print_str(print, mp_dict_view_names[self->kind]); + mp_print_str(print, "(["); + mp_obj_iter_buf_t iter_buf; + mp_obj_t self_iter = dict_view_getiter(self_in, &iter_buf); + mp_obj_t next = MP_OBJ_NULL; + while ((next = dict_view_it_iternext(self_iter)) != MP_OBJ_STOP_ITERATION) { + if (!first) { + mp_print_str(print, ", "); + } + first = false; + mp_obj_print_helper(print, next, PRINT_REPR); + } + mp_print_str(print, "])"); +} + +static mp_obj_t dict_view_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + mp_obj_dict_view_t *o = MP_OBJ_TO_PTR(o_in); + // only dict.values() supports __hash__. + if (op == MP_UNARY_OP_HASH && o->kind == MP_DICT_VIEW_VALUES) { + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)o_in); + } + return MP_OBJ_NULL; +} + +static mp_obj_t dict_view_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + // only supported for the 'keys' kind until sets and dicts are refactored + mp_obj_dict_view_t *o = MP_OBJ_TO_PTR(lhs_in); + if (o->kind != MP_DICT_VIEW_KEYS) { + return MP_OBJ_NULL; // op not supported + } + if (op != MP_BINARY_OP_CONTAINS) { + return MP_OBJ_NULL; // op not supported + } + return dict_binary_op(op, o->dict, rhs_in); +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_dict_view, + MP_QSTR_dict_view, + MP_TYPE_FLAG_ITER_IS_GETITER, + print, dict_view_print, + unary_op, dict_view_unary_op, + binary_op, dict_view_binary_op, + iter, dict_view_getiter + ); + +static mp_obj_t mp_obj_new_dict_view(mp_obj_t dict, mp_dict_view_kind_t kind) { + mp_obj_dict_view_t *o = mp_obj_malloc(mp_obj_dict_view_t, &mp_type_dict_view); + o->dict = dict; + o->kind = kind; + return MP_OBJ_FROM_PTR(o); +} + +static mp_obj_t dict_view(mp_obj_t self_in, mp_dict_view_kind_t kind) { + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + return mp_obj_new_dict_view(self_in, kind); +} + +static mp_obj_t dict_items(mp_obj_t self_in) { + return dict_view(self_in, MP_DICT_VIEW_ITEMS); +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_items_obj, dict_items); + +static mp_obj_t dict_keys(mp_obj_t self_in) { + return dict_view(self_in, MP_DICT_VIEW_KEYS); +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_keys_obj, dict_keys); + +static mp_obj_t dict_values(mp_obj_t self_in) { + return dict_view(self_in, MP_DICT_VIEW_VALUES); +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values); + +/******************************************************************************/ +/* dict iterator */ + +static mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_dict_view_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + mp_obj_dict_view_it_t *o = (mp_obj_dict_view_it_t *)iter_buf; + o->base.type = &mp_type_dict_view_it; + o->kind = MP_DICT_VIEW_KEYS; + o->dict = self_in; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +/* dict constructors & public C API */ + +static const mp_rom_map_elem_t dict_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&dict_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&dict_copy_obj) }, + #if MICROPY_PY_BUILTINS_DICT_FROMKEYS + { MP_ROM_QSTR(MP_QSTR_fromkeys), MP_ROM_PTR(&dict_fromkeys_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&dict_get_obj) }, + { MP_ROM_QSTR(MP_QSTR_items), MP_ROM_PTR(&dict_items_obj) }, + { MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&dict_keys_obj) }, + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&dict_pop_obj) }, + { MP_ROM_QSTR(MP_QSTR_popitem), MP_ROM_PTR(&dict_popitem_obj) }, + { MP_ROM_QSTR(MP_QSTR_setdefault), MP_ROM_PTR(&dict_setdefault_obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&dict_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_values), MP_ROM_PTR(&dict_values_obj) }, + { MP_ROM_QSTR(MP_QSTR___getitem__), MP_ROM_PTR(&mp_op_getitem_obj) }, + { MP_ROM_QSTR(MP_QSTR___setitem__), MP_ROM_PTR(&mp_op_setitem_obj) }, + { MP_ROM_QSTR(MP_QSTR___delitem__), MP_ROM_PTR(&mp_op_delitem_obj) }, +}; + +static MP_DEFINE_CONST_DICT(dict_locals_dict, dict_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_dict, + MP_QSTR_dict, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_dict_make_new, + print, dict_print, + unary_op, dict_unary_op, + binary_op, dict_binary_op, + subscr, dict_subscr, + iter, dict_getiter, + locals_dict, &dict_locals_dict + ); + +#if MICROPY_PY_COLLECTIONS_ORDEREDDICT +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_ordereddict, + MP_QSTR_OrderedDict, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_dict_make_new, + print, dict_print, + unary_op, dict_unary_op, + binary_op, dict_binary_op, + subscr, dict_subscr, + iter, dict_getiter, + parent, &mp_type_dict, + locals_dict, &dict_locals_dict + ); +#endif + +void mp_obj_dict_init(mp_obj_dict_t *dict, size_t n_args) { + dict->base.type = &mp_type_dict; + mp_map_init(&dict->map, n_args); +} + +mp_obj_t mp_obj_new_dict(size_t n_args) { + mp_obj_dict_t *o = m_new_obj(mp_obj_dict_t); + mp_obj_dict_init(o, n_args); + return MP_OBJ_FROM_PTR(o); +} + +size_t mp_obj_dict_len(mp_obj_t self_in) { + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + return self->map.used; +} + +mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_ensure_not_fixed(self); + mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; + return self_in; +} + +mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key) { + mp_obj_t args[2] = {self_in, key}; + dict_get_helper(2, args, MP_MAP_LOOKUP_REMOVE_IF_FOUND); + return self_in; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objenumerate.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objenumerate.c new file mode 100644 index 00000000..8217a0d4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objenumerate.c @@ -0,0 +1,90 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +#if MICROPY_PY_BUILTINS_ENUMERATE + +typedef struct _mp_obj_enumerate_t { + mp_obj_base_t base; + mp_obj_t iter; + mp_int_t cur; +} mp_obj_enumerate_t; + +static mp_obj_t enumerate_iternext(mp_obj_t self_in); + +static mp_obj_t enumerate_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + #if MICROPY_CPYTHON_COMPAT + static const mp_arg_t allowed_args[] = { + { MP_QSTR_iterable, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_start, MP_ARG_INT, {.u_int = 0} }, + }; + + // parse args + struct { + mp_arg_val_t iterable, start; + } arg_vals; + mp_arg_parse_all_kw_array(n_args, n_kw, args, + MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&arg_vals); + + // create enumerate object + mp_obj_enumerate_t *o = mp_obj_malloc(mp_obj_enumerate_t, type); + o->iter = mp_getiter(arg_vals.iterable.u_obj, NULL); + o->cur = arg_vals.start.u_int; + #else + mp_arg_check_num(n_args, n_kw, 1, 2, false); + mp_obj_enumerate_t *o = mp_obj_malloc(mp_obj_enumerate_t, type); + o->iter = mp_getiter(args[0], NULL); + o->cur = n_args > 1 ? mp_obj_get_int(args[1]) : 0; + #endif + + return MP_OBJ_FROM_PTR(o); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_enumerate, + MP_QSTR_enumerate, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, enumerate_make_new, + iter, enumerate_iternext + ); + +static mp_obj_t enumerate_iternext(mp_obj_t self_in) { + assert(mp_obj_is_type(self_in, &mp_type_enumerate)); + mp_obj_enumerate_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t next = mp_iternext(self->iter); + if (next == MP_OBJ_STOP_ITERATION) { + return MP_OBJ_STOP_ITERATION; + } else { + mp_obj_t items[] = {MP_OBJ_NEW_SMALL_INT(self->cur++), next}; + return mp_obj_new_tuple(2, items); + } +} + +#endif // MICROPY_PY_BUILTINS_ENUMERATE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objexcept.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objexcept.c new file mode 100644 index 00000000..5bf4e672 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objexcept.c @@ -0,0 +1,652 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2016 Paul Sokolovsky + * + * 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 "py/objlist.h" +#include "py/objstr.h" +#include "py/objtuple.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mperrno.h" + +#if MICROPY_ROM_TEXT_COMPRESSION && !defined(NO_QSTR) +// Extract the MP_MAX_UNCOMPRESSED_TEXT_LEN macro from "genhdr/compressed.data.h". +// Only need this if compression enabled and in a regular build (i.e. not during QSTR extraction). +#define MP_MATCH_COMPRESSED(...) // Ignore +#define MP_COMPRESSED_DATA(...) // Ignore +#include "genhdr/compressed.data.h" +#undef MP_MATCH_COMPRESSED +#undef MP_COMPRESSED_DATA +#endif + +// Number of items per traceback entry (file, line, block) +#define TRACEBACK_ENTRY_LEN (3) + +// Optionally allocated buffer for storing some traceback, the tuple argument, +// and possible string object and data, for when the heap is locked. +#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + +// When used the layout of the emergency exception buffer is: +// - traceback entry (file, line, block) +// - traceback entry (file, line, block) +// - mp_obj_tuple_t object +// - n_args * mp_obj_t for tuple +// - mp_obj_str_t object +// - string data +#define EMG_BUF_TRACEBACK_OFFSET (0) +#define EMG_BUF_TRACEBACK_SIZE (2 * TRACEBACK_ENTRY_LEN * sizeof(size_t)) +#define EMG_BUF_TUPLE_OFFSET (EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE) +#define EMG_BUF_TUPLE_SIZE(n_args) (sizeof(mp_obj_tuple_t) + n_args * sizeof(mp_obj_t)) +#define EMG_BUF_STR_OFFSET (EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(1)) +#define EMG_BUF_STR_BUF_OFFSET (EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t)) + +#if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0 +#define mp_emergency_exception_buf_size MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE + +void mp_init_emergency_exception_buf(void) { + // Nothing to do since the buffer was declared statically. We put this + // definition here so that the calling code can call this function + // regardless of how its configured (makes the calling code a bit cleaner). +} + +#else +#define mp_emergency_exception_buf_size MP_STATE_VM(mp_emergency_exception_buf_size) + +#include "py/mphal.h" // for MICROPY_BEGIN_ATOMIC_SECTION/MICROPY_END_ATOMIC_SECTION + +void mp_init_emergency_exception_buf(void) { + mp_emergency_exception_buf_size = 0; + MP_STATE_VM(mp_emergency_exception_buf) = NULL; +} + +mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { + mp_int_t size = mp_obj_get_int(size_in); + void *buf = NULL; + if (size > 0) { + buf = m_new(byte, size); + } + + int old_size = mp_emergency_exception_buf_size; + void *old_buf = MP_STATE_VM(mp_emergency_exception_buf); + + // Update the 2 variables atomically so that an interrupt can't occur + // between the assignments. + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_emergency_exception_buf_size = size; + MP_STATE_VM(mp_emergency_exception_buf) = buf; + MICROPY_END_ATOMIC_SECTION(atomic_state); + + if (old_buf != NULL) { + m_del(byte, old_buf, old_size); + } + return mp_const_none; +} +#endif +#endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + +bool mp_obj_is_native_exception_instance(mp_obj_t self_in) { + return MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(self_in), make_new) == mp_obj_exception_make_new; +} + +static mp_obj_exception_t *get_native_exception(mp_obj_t self_in) { + assert(mp_obj_is_exception_instance(self_in)); + if (mp_obj_is_native_exception_instance(self_in)) { + return MP_OBJ_TO_PTR(self_in); + } else { + return MP_OBJ_TO_PTR(((mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in))->subobj[0]); + } +} + +static void decompress_error_text_maybe(mp_obj_exception_t *o) { + #if MICROPY_ROM_TEXT_COMPRESSION + if (o->args->len == 1 && mp_obj_is_exact_type(o->args->items[0], &mp_type_str)) { + mp_obj_str_t *o_str = MP_OBJ_TO_PTR(o->args->items[0]); + if (MP_IS_COMPRESSED_ROM_STRING(o_str->data)) { + byte *buf = m_new_maybe(byte, MP_MAX_UNCOMPRESSED_TEXT_LEN + 1); + if (!buf) { + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + // Try and use the emergency exception buf if enough space is available. + buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET); + size_t avail = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - buf; + if (avail < MP_MAX_UNCOMPRESSED_TEXT_LEN + 1) { + // No way to decompress, fallback to no message text. + o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + return; + } + #else + o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + return; + #endif + } + mp_decompress_rom_string(buf, (mp_rom_error_text_t)o_str->data); + o_str->data = buf; + o_str->len = strlen((const char *)buf); + o_str->hash = 0; + } + // Lazily compute the string hash. + if (o_str->hash == 0) { + o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + } + } + #endif +} + +void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in); + mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; + bool is_subclass = kind & PRINT_EXC_SUBCLASS; + if (!is_subclass && (k == PRINT_REPR || k == PRINT_EXC)) { + mp_print_str(print, qstr_str(o->base.type->name)); + } + + if (k == PRINT_EXC) { + mp_print_str(print, ": "); + } + + decompress_error_text_maybe(o); + + if (k == PRINT_STR || k == PRINT_EXC) { + if (o->args == NULL || o->args->len == 0) { + mp_print_str(print, ""); + return; + } + + #if MICROPY_PY_ERRNO + // try to provide a nice OSError error message + if (o->base.type == &mp_type_OSError && o->args->len > 0 && o->args->len < 3 && mp_obj_is_small_int(o->args->items[0])) { + qstr qst = mp_errno_to_str(o->args->items[0]); + if (qst != MP_QSTRnull) { + mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst); + if (o->args->len > 1) { + mp_print_str(print, ": "); + mp_obj_print_helper(print, o->args->items[1], PRINT_STR); + } + return; + } + } + #endif + + if (o->args->len == 1) { + mp_obj_print_helper(print, o->args->items[0], PRINT_STR); + return; + } + } + + mp_obj_tuple_print(print, MP_OBJ_FROM_PTR(o->args), kind); +} + +mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, false); + + // Try to allocate memory for the exception, with fallback to emergency exception object + mp_obj_exception_t *o_exc = m_new_obj_maybe(mp_obj_exception_t); + if (o_exc == NULL) { + o_exc = &MP_STATE_VM(mp_emergency_exception_obj); + } + + // Populate the exception object + o_exc->base.type = type; + o_exc->traceback_data = NULL; + + mp_obj_tuple_t *o_tuple; + if (n_args == 0) { + // No args, can use the empty tuple straight away + o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + } else { + // Try to allocate memory for the tuple containing the args + o_tuple = m_new_obj_var_maybe(mp_obj_tuple_t, items, mp_obj_t, n_args); + + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + // If we are called by mp_obj_new_exception_msg_varg then it will have + // reserved room (after the traceback data) for a tuple with 1 element. + // Otherwise we are free to use the whole buffer after the traceback data. + if (o_tuple == NULL && mp_emergency_exception_buf_size >= + (mp_int_t)(EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args))) { + o_tuple = (mp_obj_tuple_t *) + ((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TUPLE_OFFSET); + } + #endif + + if (o_tuple == NULL) { + // No memory for a tuple, fallback to an empty tuple + o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + } else { + // Have memory for a tuple so populate it + o_tuple->base.type = &mp_type_tuple; + o_tuple->len = n_args; + memcpy(o_tuple->items, args, n_args * sizeof(mp_obj_t)); + } + } + + // Store the tuple of args in the exception object + o_exc->args = o_tuple; + + return MP_OBJ_FROM_PTR(o_exc); +} + +// Get exception "value" - that is, first argument, or None +mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) { + mp_obj_exception_t *self = get_native_exception(self_in); + if (self->args->len == 0) { + return mp_const_none; + } else { + decompress_error_text_maybe(self); + return self->args->items[0]; + } +} + +void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in); + if (dest[0] != MP_OBJ_NULL) { + // store/delete attribute + if (attr == MP_QSTR___traceback__ && dest[1] == mp_const_none) { + // We allow 'exc.__traceback__ = None' assignment as low-level + // optimization of pre-allocating exception instance and raising + // it repeatedly - this avoids memory allocation during raise. + // However, uPy will keep adding traceback entries to such + // exception instance, so before throwing it, traceback should + // be cleared like above. + self->traceback_len = 0; + dest[0] = MP_OBJ_NULL; // indicate success + } + return; + } + if (attr == MP_QSTR_args) { + decompress_error_text_maybe(self); + dest[0] = MP_OBJ_FROM_PTR(self->args); + } else if (attr == MP_QSTR_value || attr == MP_QSTR_errno) { + // These are aliases for args[0]: .value for StopIteration and .errno for OSError. + // For efficiency let these attributes apply to all exception instances. + dest[0] = mp_obj_exception_get_value(self_in); + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_BaseException, + MP_QSTR_BaseException, + MP_TYPE_FLAG_NONE, + make_new, mp_obj_exception_make_new, + print, mp_obj_exception_print, + attr, mp_obj_exception_attr + ); + +// *FORMAT-OFF* + +// List of all exceptions, arranged as in the table at: +// http://docs.python.org/3/library/exceptions.html +MP_DEFINE_EXCEPTION(SystemExit, BaseException) +MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException) +MP_DEFINE_EXCEPTION(GeneratorExit, BaseException) +MP_DEFINE_EXCEPTION(Exception, BaseException) + #if MICROPY_PY_ASYNC_AWAIT + MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception) + #endif + MP_DEFINE_EXCEPTION(StopIteration, Exception) + MP_DEFINE_EXCEPTION(ArithmeticError, Exception) + //MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError) + MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError) + MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError) + MP_DEFINE_EXCEPTION(AssertionError, Exception) + MP_DEFINE_EXCEPTION(AttributeError, Exception) + //MP_DEFINE_EXCEPTION(BufferError, Exception) + MP_DEFINE_EXCEPTION(EOFError, Exception) + MP_DEFINE_EXCEPTION(ImportError, Exception) + MP_DEFINE_EXCEPTION(LookupError, Exception) + MP_DEFINE_EXCEPTION(IndexError, LookupError) + MP_DEFINE_EXCEPTION(KeyError, LookupError) + MP_DEFINE_EXCEPTION(MemoryError, Exception) + MP_DEFINE_EXCEPTION(NameError, Exception) + /* + MP_DEFINE_EXCEPTION(UnboundLocalError, NameError) + */ + MP_DEFINE_EXCEPTION(OSError, Exception) + /* + MP_DEFINE_EXCEPTION(BlockingIOError, OSError) + MP_DEFINE_EXCEPTION(ChildProcessError, OSError) + MP_DEFINE_EXCEPTION(ConnectionError, OSError) + MP_DEFINE_EXCEPTION(BrokenPipeError, ConnectionError) + MP_DEFINE_EXCEPTION(ConnectionAbortedError, ConnectionError) + MP_DEFINE_EXCEPTION(ConnectionRefusedError, ConnectionError) + MP_DEFINE_EXCEPTION(ConnectionResetError, ConnectionError) + MP_DEFINE_EXCEPTION(InterruptedError, OSError) + MP_DEFINE_EXCEPTION(IsADirectoryError, OSError) + MP_DEFINE_EXCEPTION(NotADirectoryError, OSError) + MP_DEFINE_EXCEPTION(PermissionError, OSError) + MP_DEFINE_EXCEPTION(ProcessLookupError, OSError) + MP_DEFINE_EXCEPTION(TimeoutError, OSError) + MP_DEFINE_EXCEPTION(FileExistsError, OSError) + MP_DEFINE_EXCEPTION(FileNotFoundError, OSError) + MP_DEFINE_EXCEPTION(ReferenceError, Exception) + */ + MP_DEFINE_EXCEPTION(RuntimeError, Exception) + MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError) + MP_DEFINE_EXCEPTION(SyntaxError, Exception) + MP_DEFINE_EXCEPTION(IndentationError, SyntaxError) + /* + MP_DEFINE_EXCEPTION(TabError, IndentationError) + */ + //MP_DEFINE_EXCEPTION(SystemError, Exception) + MP_DEFINE_EXCEPTION(TypeError, Exception) +#if MICROPY_EMIT_NATIVE + MP_DEFINE_EXCEPTION(ViperTypeError, TypeError) +#endif + MP_DEFINE_EXCEPTION(ValueError, Exception) +#if MICROPY_PY_BUILTINS_STR_UNICODE + MP_DEFINE_EXCEPTION(UnicodeError, ValueError) + //TODO: Implement more UnicodeError subclasses which take arguments +#endif + /* + MP_DEFINE_EXCEPTION(Warning, Exception) + MP_DEFINE_EXCEPTION(DeprecationWarning, Warning) + MP_DEFINE_EXCEPTION(PendingDeprecationWarning, Warning) + MP_DEFINE_EXCEPTION(RuntimeWarning, Warning) + MP_DEFINE_EXCEPTION(SyntaxWarning, Warning) + MP_DEFINE_EXCEPTION(UserWarning, Warning) + MP_DEFINE_EXCEPTION(FutureWarning, Warning) + MP_DEFINE_EXCEPTION(ImportWarning, Warning) + MP_DEFINE_EXCEPTION(UnicodeWarning, Warning) + MP_DEFINE_EXCEPTION(BytesWarning, Warning) + MP_DEFINE_EXCEPTION(ResourceWarning, Warning) + */ + +// *FORMAT-ON* + +mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type) { + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); + return mp_obj_exception_make_new(exc_type, 0, 0, NULL); +} + +mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args) { + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); + return mp_obj_exception_make_new(exc_type, n_args, 0, args); +} + +#if MICROPY_ERROR_REPORTING != MICROPY_ERROR_REPORTING_NONE + +mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { + // Check that the given type is an exception type + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); + + // Try to allocate memory for the message + mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); + + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + // If memory allocation failed and there is an emergency buffer then try to use + // that buffer to store the string object, reserving room at the start for the + // traceback and 1-tuple. + if (o_str == NULL + && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t))) { + o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + + EMG_BUF_STR_OFFSET); + } + #endif + + if (o_str == NULL) { + // No memory for the string object so create the exception with no args + return mp_obj_exception_make_new(exc_type, 0, 0, NULL); + } + + // Create the string object and call mp_obj_exception_make_new to create the exception + o_str->base.type = &mp_type_str; + o_str->len = strlen((const char *)msg); + o_str->data = (const byte *)msg; + #if MICROPY_ROM_TEXT_COMPRESSION + o_str->hash = 0; // will be computed only if string object is accessed + #else + o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + #endif + mp_obj_t arg = MP_OBJ_FROM_PTR(o_str); + return mp_obj_exception_make_new(exc_type, 1, 0, &arg); +} + +// The following struct and function implement a simple printer that conservatively +// allocates memory and truncates the output data if no more memory can be obtained. +// It leaves room for a null byte at the end of the buffer. + +struct _exc_printer_t { + bool allow_realloc; + size_t alloc; + size_t len; + byte *buf; +}; + +static void exc_add_strn(void *data, const char *str, size_t len) { + struct _exc_printer_t *pr = data; + if (pr->len + len >= pr->alloc) { + // Not enough room for data plus a null byte so try to grow the buffer + if (pr->allow_realloc) { + size_t new_alloc = pr->alloc + len + 16; + byte *new_buf = m_renew_maybe(byte, pr->buf, pr->alloc, new_alloc, true); + if (new_buf == NULL) { + pr->allow_realloc = false; + len = pr->alloc - pr->len - 1; + } else { + pr->alloc = new_alloc; + pr->buf = new_buf; + } + } else { + len = pr->alloc - pr->len - 1; + } + } + memcpy(pr->buf + pr->len, str, len); + pr->len += len; +} + +mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { + va_list args; + va_start(args, fmt); + mp_obj_t exc = mp_obj_new_exception_msg_vlist(exc_type, fmt, args); + va_end(args); + return exc; +} + +mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list args) { + assert(fmt != NULL); + + // Check that the given type is an exception type + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); + + // Try to allocate memory for the message + mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); + size_t o_str_alloc = strlen((const char *)fmt) + 1; + byte *o_str_buf = m_new_maybe(byte, o_str_alloc); + + bool used_emg_buf = false; + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + // If memory allocation failed and there is an emergency buffer then try to use + // that buffer to store the string object and its data (at least 16 bytes for + // the string data), reserving room at the start for the traceback and 1-tuple. + if ((o_str == NULL || o_str_buf == NULL) + && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16)) { + used_emg_buf = true; + o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_OFFSET); + o_str_buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET); + o_str_alloc = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - o_str_buf; + } + #endif + + if (o_str == NULL) { + // No memory for the string object so create the exception with no args. + // The exception will only have a type and no message (compression is irrelevant). + return mp_obj_exception_make_new(exc_type, 0, 0, NULL); + } + + if (o_str_buf == NULL) { + // No memory for the string buffer: assume that the fmt string is in ROM + // and use that data as the data of the string. + // The string will point directly to the compressed data -- will need to be decompressed + // prior to display (this case is identical to mp_obj_new_exception_msg above). + o_str->len = o_str_alloc - 1; // will be equal to strlen(fmt) + o_str->data = (const byte *)fmt; + } else { + // We have some memory to format the string. + // TODO: Optimise this to format-while-decompressing (and not require the temp stack space). + struct _exc_printer_t exc_pr = {!used_emg_buf, o_str_alloc, 0, o_str_buf}; + mp_print_t print = {&exc_pr, exc_add_strn}; + const char *fmt2 = (const char *)fmt; + #if MICROPY_ROM_TEXT_COMPRESSION + byte decompressed[MP_MAX_UNCOMPRESSED_TEXT_LEN]; + if (MP_IS_COMPRESSED_ROM_STRING(fmt)) { + mp_decompress_rom_string(decompressed, fmt); + fmt2 = (const char *)decompressed; + } + #endif + mp_vprintf(&print, fmt2, args); + exc_pr.buf[exc_pr.len] = '\0'; + o_str->len = exc_pr.len; + o_str->data = exc_pr.buf; + } + + // Create the string object and call mp_obj_exception_make_new to create the exception + o_str->base.type = &mp_type_str; + #if MICROPY_ROM_TEXT_COMPRESSION + o_str->hash = 0; // will be computed only if string object is accessed + #else + o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + #endif + mp_obj_t arg = MP_OBJ_FROM_PTR(o_str); + return mp_obj_exception_make_new(exc_type, 1, 0, &arg); +} + +#endif + +// return true if the given object is an exception type +bool mp_obj_is_exception_type(mp_obj_t self_in) { + if (mp_obj_is_type(self_in, &mp_type_type)) { + // optimisation when self_in is a builtin exception + mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); + if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(self, make_new) == mp_obj_exception_make_new) { + return true; + } + } + return mp_obj_is_subclass_fast(self_in, MP_OBJ_FROM_PTR(&mp_type_BaseException)); +} + +// return true if the given object is an instance of an exception type +bool mp_obj_is_exception_instance(mp_obj_t self_in) { + return mp_obj_is_exception_type(MP_OBJ_FROM_PTR(mp_obj_get_type(self_in))); +} + +// Return true if exception (type or instance) is a subclass of given +// exception type. Assumes exc_type is a subclass of BaseException, as +// defined by mp_obj_is_exception_type(exc_type). +bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type) { + // if exc is an instance of an exception, then extract and use its type + if (mp_obj_is_exception_instance(exc)) { + exc = MP_OBJ_FROM_PTR(mp_obj_get_type(exc)); + } + return mp_obj_is_subclass_fast(exc, exc_type); +} + +// traceback handling functions + +void mp_obj_exception_clear_traceback(mp_obj_t self_in) { + mp_obj_exception_t *self = get_native_exception(self_in); + // just set the traceback to the null object + // we don't want to call any memory management functions here + self->traceback_data = NULL; +} + +void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block) { + mp_obj_exception_t *self = get_native_exception(self_in); + + // append this traceback info to traceback data + // if memory allocation fails (eg because gc is locked), just return + + #if MICROPY_PY_SYS_TRACEBACKLIMIT + mp_int_t max_traceback = MP_OBJ_SMALL_INT_VALUE(MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT])); + if (max_traceback <= 0) { + return; + } else if (self->traceback_data != NULL && self->traceback_len >= max_traceback * TRACEBACK_ENTRY_LEN) { + self->traceback_len -= TRACEBACK_ENTRY_LEN; + memmove(self->traceback_data, self->traceback_data + TRACEBACK_ENTRY_LEN, self->traceback_len * sizeof(self->traceback_data[0])); + } + #endif + + if (self->traceback_data == NULL) { + self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN); + if (self->traceback_data == NULL) { + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + if (mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)) { + // There is room in the emergency buffer for traceback data + size_t *tb = (size_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + + EMG_BUF_TRACEBACK_OFFSET); + self->traceback_data = tb; + self->traceback_alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t); + } else { + // Can't allocate and no room in emergency buffer + return; + } + #else + // Can't allocate + return; + #endif + } else { + // Allocated the traceback data on the heap + self->traceback_alloc = TRACEBACK_ENTRY_LEN; + } + self->traceback_len = 0; + } else if (self->traceback_len + TRACEBACK_ENTRY_LEN > self->traceback_alloc) { + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + if (self->traceback_data == (size_t *)MP_STATE_VM(mp_emergency_exception_buf)) { + // Can't resize the emergency buffer + return; + } + #endif + // be conservative with growing traceback data + size_t *tb_data = m_renew_maybe(size_t, self->traceback_data, self->traceback_alloc, + self->traceback_alloc + TRACEBACK_ENTRY_LEN, true); + if (tb_data == NULL) { + return; + } + self->traceback_data = tb_data; + self->traceback_alloc += TRACEBACK_ENTRY_LEN; + } + + size_t *tb_data = &self->traceback_data[self->traceback_len]; + self->traceback_len += TRACEBACK_ENTRY_LEN; + tb_data[0] = file; + tb_data[1] = line; + tb_data[2] = block; +} + +void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values) { + mp_obj_exception_t *self = get_native_exception(self_in); + + if (self->traceback_data == NULL) { + *n = 0; + *values = NULL; + } else { + *n = self->traceback_len; + *values = self->traceback_data; + } +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objexcept.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objexcept.h new file mode 100644 index 00000000..d532f666 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objexcept.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJEXCEPT_H +#define MICROPY_INCLUDED_PY_OBJEXCEPT_H + +#include "py/obj.h" +#include "py/objtuple.h" + +typedef struct _mp_obj_exception_t { + mp_obj_base_t base; + size_t traceback_alloc : (8 * sizeof(size_t) / 2); + size_t traceback_len : (8 * sizeof(size_t) / 2); + size_t *traceback_data; + mp_obj_tuple_t *args; +} mp_obj_exception_t; + +void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); +void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); + +#define MP_DEFINE_EXCEPTION(exc_name, base_name) \ + MP_DEFINE_CONST_OBJ_TYPE(mp_type_##exc_name, MP_QSTR_##exc_name, MP_TYPE_FLAG_NONE, \ + make_new, mp_obj_exception_make_new, \ + print, mp_obj_exception_print, \ + attr, mp_obj_exception_attr, \ + parent, &mp_type_##base_name \ + ); + +#endif // MICROPY_INCLUDED_PY_OBJEXCEPT_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objfilter.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objfilter.c new file mode 100644 index 00000000..7f1f700f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objfilter.c @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +#if MICROPY_PY_BUILTINS_FILTER + +typedef struct _mp_obj_filter_t { + mp_obj_base_t base; + mp_obj_t fun; + mp_obj_t iter; +} mp_obj_filter_t; + +static mp_obj_t filter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 2, 2, false); + mp_obj_filter_t *o = mp_obj_malloc(mp_obj_filter_t, type); + o->fun = args[0]; + o->iter = mp_getiter(args[1], NULL); + return MP_OBJ_FROM_PTR(o); +} + +static mp_obj_t filter_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_filter)); + mp_obj_filter_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t next; + while ((next = mp_iternext(self->iter)) != MP_OBJ_STOP_ITERATION) { + mp_obj_t val; + if (self->fun != mp_const_none) { + val = mp_call_function_n_kw(self->fun, 1, 0, &next); + } else { + val = next; + } + if (mp_obj_is_true(val)) { + return next; + } + } + return MP_OBJ_STOP_ITERATION; +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_filter, + MP_QSTR_filter, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, filter_make_new, + iter, filter_iternext + ); + +#endif // MICROPY_PY_BUILTINS_FILTER diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objfloat.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objfloat.c new file mode 100644 index 00000000..5c90b149 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objfloat.c @@ -0,0 +1,346 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/parsenum.h" +#include "py/runtime.h" + +#if MICROPY_PY_BUILTINS_FLOAT + +#include +#include "py/formatfloat.h" + +#if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D + +// M_E and M_PI are not part of the math.h standard and may not be defined +#ifndef M_E +#define M_E (2.7182818284590452354) +#endif +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +typedef struct _mp_obj_float_t { + mp_obj_base_t base; + mp_float_t value; +} mp_obj_float_t; + +const mp_obj_float_t mp_const_float_e_obj = {{&mp_type_float}, (mp_float_t)M_E}; +const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, (mp_float_t)M_PI}; +#if MICROPY_PY_MATH_CONSTANTS +#ifndef NAN +#error NAN macro is not defined +#endif +const mp_obj_float_t mp_const_float_tau_obj = {{&mp_type_float}, (mp_float_t)(2.0 * M_PI)}; +const mp_obj_float_t mp_const_float_inf_obj = {{&mp_type_float}, (mp_float_t)INFINITY}; +const mp_obj_float_t mp_const_float_nan_obj = {{&mp_type_float}, (mp_float_t)NAN}; +#endif + +#endif + +#define MICROPY_FLOAT_ZERO MICROPY_FLOAT_CONST(0.0) + +#if MICROPY_FLOAT_HIGH_QUALITY_HASH +// must return actual integer value if it fits in mp_int_t +mp_int_t mp_float_hash(mp_float_t src) { + mp_float_union_t u = {.f = src}; + mp_int_t val; + const int adj_exp = (int)u.p.exp - MP_FLOAT_EXP_BIAS; + if (adj_exp < 0) { + // value < 1; must be sure to handle 0.0 correctly (ie return 0) + val = u.i; + } else { + // if adj_exp is max then: u.p.frc==0 indicates inf, else NaN + // else: 1 <= value + mp_float_uint_t frc = u.p.frc | ((mp_float_uint_t)1 << MP_FLOAT_FRAC_BITS); + + if (adj_exp <= MP_FLOAT_FRAC_BITS) { + // number may have a fraction; xor the integer part with the fractional part + val = (frc >> (MP_FLOAT_FRAC_BITS - adj_exp)) + ^ (frc & (((mp_float_uint_t)1 << (MP_FLOAT_FRAC_BITS - adj_exp)) - 1)); + } else if ((unsigned int)adj_exp < MP_BITS_PER_BYTE * sizeof(mp_int_t) - 1) { + // the number is a (big) whole integer and will fit in val's signed-width + val = (mp_int_t)frc << (adj_exp - MP_FLOAT_FRAC_BITS); + } else { + // integer part will overflow val's width so just use what bits we can + val = frc; + } + } + + if (u.p.sgn) { + val = -(mp_uint_t)val; + } + + return val; +} +#endif + +static void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_float_t o_val = mp_obj_float_get(o_in); + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + char buf[16]; + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + const int precision = 6; + #else + const int precision = 7; + #endif + #else + char buf[32]; + const int precision = 16; + #endif + mp_format_float(o_val, buf, sizeof(buf), 'g', precision, '\0'); + mp_print_str(print, buf); + if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) { + // Python floats always have decimal point (unless inf or nan) + mp_print_str(print, ".0"); + } +} + +static mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + switch (n_args) { + case 0: + return mp_obj_new_float(0); + + case 1: + default: { + mp_buffer_info_t bufinfo; + if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { + // a textual representation, parse it + return mp_parse_num_float(bufinfo.buf, bufinfo.len, false, NULL); + } else if (mp_obj_is_float(args[0])) { + // a float, just return it + return args[0]; + } else { + // something else, try to cast it to a float + return mp_obj_new_float(mp_obj_get_float(args[0])); + } + } + } +} + +static mp_obj_t float_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + mp_float_t val = mp_obj_float_get(o_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(val != 0); + case MP_UNARY_OP_HASH: + return MP_OBJ_NEW_SMALL_INT(mp_float_hash(val)); + case MP_UNARY_OP_POSITIVE: + return o_in; + case MP_UNARY_OP_NEGATIVE: + return mp_obj_new_float(-val); + case MP_UNARY_OP_ABS: { + if (signbit(val)) { + return mp_obj_new_float(-val); + } else { + return o_in; + } + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t float_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + mp_float_t lhs_val = mp_obj_float_get(lhs_in); + #if MICROPY_PY_BUILTINS_COMPLEX + if (mp_obj_is_type(rhs_in, &mp_type_complex)) { + return mp_obj_complex_binary_op(op, lhs_val, 0, rhs_in); + } + #endif + return mp_obj_float_binary_op(op, lhs_val, rhs_in); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_float, MP_QSTR_float, MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, + make_new, float_make_new, + print, float_print, + unary_op, float_unary_op, + binary_op, float_binary_op + ); + +#if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D + +mp_obj_t mp_obj_new_float(mp_float_t value) { + // Don't use mp_obj_malloc here to avoid extra function call overhead. + mp_obj_float_t *o = m_new_obj(mp_obj_float_t); + o->base.type = &mp_type_float; + o->value = value; + return MP_OBJ_FROM_PTR(o); +} + +mp_float_t mp_obj_float_get(mp_obj_t self_in) { + assert(mp_obj_is_float(self_in)); + mp_obj_float_t *self = MP_OBJ_TO_PTR(self_in); + return self->value; +} + +#endif + +static void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) { + // logic here follows that of CPython + // https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations + // x == (x//y)*y + (x%y) + // divmod(x, y) == (x//y, x%y) + mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y); + mp_float_t div = (*x - mod) / *y; + + // Python specs require that mod has same sign as second operand + if (mod == MICROPY_FLOAT_ZERO) { + mod = MICROPY_FLOAT_C_FUN(copysign)(MICROPY_FLOAT_ZERO, *y); + } else { + if ((mod < MICROPY_FLOAT_ZERO) != (*y < MICROPY_FLOAT_ZERO)) { + mod += *y; + div -= MICROPY_FLOAT_CONST(1.0); + } + } + + mp_float_t floordiv; + if (div == MICROPY_FLOAT_ZERO) { + // if division is zero, take the correct sign of zero + floordiv = MICROPY_FLOAT_C_FUN(copysign)(MICROPY_FLOAT_ZERO, *x / *y); + } else { + // Python specs require that x == (x//y)*y + (x%y) + floordiv = MICROPY_FLOAT_C_FUN(floor)(div); + if (div - floordiv > MICROPY_FLOAT_CONST(0.5)) { + floordiv += MICROPY_FLOAT_CONST(1.0); + } + } + + // return results + *x = floordiv; + *y = mod; +} + +mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t rhs_in) { + mp_float_t rhs_val; + if (!mp_obj_get_float_maybe(rhs_in, &rhs_val)) { + return MP_OBJ_NULL; // op not supported + } + + switch (op) { + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_INPLACE_ADD: + lhs_val += rhs_val; + break; + case MP_BINARY_OP_SUBTRACT: + case MP_BINARY_OP_INPLACE_SUBTRACT: + lhs_val -= rhs_val; + break; + case MP_BINARY_OP_MULTIPLY: + case MP_BINARY_OP_INPLACE_MULTIPLY: + lhs_val *= rhs_val; + break; + case MP_BINARY_OP_FLOOR_DIVIDE: + case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: + if (rhs_val == 0) { + zero_division_error: + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); + } + // Python specs require that x == (x//y)*y + (x%y) so we must + // call divmod to compute the correct floor division, which + // returns the floor divide in lhs_val. + mp_obj_float_divmod(&lhs_val, &rhs_val); + break; + case MP_BINARY_OP_TRUE_DIVIDE: + case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: + if (rhs_val == 0) { + goto zero_division_error; + } + lhs_val /= rhs_val; + break; + case MP_BINARY_OP_MODULO: + case MP_BINARY_OP_INPLACE_MODULO: + if (rhs_val == MICROPY_FLOAT_ZERO) { + goto zero_division_error; + } + lhs_val = MICROPY_FLOAT_C_FUN(fmod)(lhs_val, rhs_val); + // Python specs require that mod has same sign as second operand + if (lhs_val == MICROPY_FLOAT_ZERO) { + lhs_val = MICROPY_FLOAT_C_FUN(copysign)(0.0, rhs_val); + } else { + if ((lhs_val < MICROPY_FLOAT_ZERO) != (rhs_val < MICROPY_FLOAT_ZERO)) { + lhs_val += rhs_val; + } + } + break; + case MP_BINARY_OP_POWER: + case MP_BINARY_OP_INPLACE_POWER: + if (lhs_val == 0 && rhs_val < 0 && !isinf(rhs_val)) { + goto zero_division_error; + } + if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val) && !isnan(rhs_val)) { + #if MICROPY_PY_BUILTINS_COMPLEX + return mp_obj_complex_binary_op(MP_BINARY_OP_POWER, lhs_val, 0, rhs_in); + #else + mp_raise_ValueError(MP_ERROR_TEXT("complex values not supported")); + #endif + } + #if MICROPY_PY_MATH_POW_FIX_NAN // Also see modmath.c. + if (lhs_val == MICROPY_FLOAT_CONST(1.0) || rhs_val == MICROPY_FLOAT_CONST(0.0)) { + lhs_val = MICROPY_FLOAT_CONST(1.0); + break; + } + #endif + lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); + break; + case MP_BINARY_OP_DIVMOD: { + if (rhs_val == 0) { + goto zero_division_error; + } + mp_obj_float_divmod(&lhs_val, &rhs_val); + mp_obj_t tuple[2] = { + mp_obj_new_float(lhs_val), + mp_obj_new_float(rhs_val), + }; + return mp_obj_new_tuple(2, tuple); + } + case MP_BINARY_OP_LESS: + return mp_obj_new_bool(lhs_val < rhs_val); + case MP_BINARY_OP_MORE: + return mp_obj_new_bool(lhs_val > rhs_val); + case MP_BINARY_OP_EQUAL: + return mp_obj_new_bool(lhs_val == rhs_val); + case MP_BINARY_OP_LESS_EQUAL: + return mp_obj_new_bool(lhs_val <= rhs_val); + case MP_BINARY_OP_MORE_EQUAL: + return mp_obj_new_bool(lhs_val >= rhs_val); + + default: + return MP_OBJ_NULL; // op not supported + } + return mp_obj_new_float(lhs_val); +} + +#endif // MICROPY_PY_BUILTINS_FLOAT diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objfun.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objfun.c new file mode 100644 index 00000000..1ebfa3d5 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objfun.c @@ -0,0 +1,541 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/objtuple.h" +#include "py/objfun.h" +#include "py/runtime.h" +#include "py/bc.h" +#include "py/stackctrl.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + +// Note: the "name" entry in mp_obj_type_t for a function type must be +// MP_QSTR_function because it is used to determine if an object is of generic +// function type. + +/******************************************************************************/ +/* builtin functions */ + +static mp_obj_t fun_builtin_0_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)args; + assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_0)); + mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); + mp_arg_check_num(n_args, n_kw, 0, 0, false); + return self->fun._0(); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_0, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_0_call + ); + +static mp_obj_t fun_builtin_1_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_1)); + mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); + mp_arg_check_num(n_args, n_kw, 1, 1, false); + return self->fun._1(args[0]); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_1, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_1_call + ); + +static mp_obj_t fun_builtin_2_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_2)); + mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); + mp_arg_check_num(n_args, n_kw, 2, 2, false); + return self->fun._2(args[0], args[1]); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_2, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_2_call + ); + +static mp_obj_t fun_builtin_3_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_3)); + mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); + mp_arg_check_num(n_args, n_kw, 3, 3, false); + return self->fun._3(args[0], args[1], args[2]); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_3, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_3_call + ); + +static mp_obj_t fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_var)); + mp_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in); + + // check number of arguments + mp_arg_check_num_sig(n_args, n_kw, self->sig); + + if (self->sig & 1) { + // function allows keywords + + // we create a map directly from the given args array + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + + return self->fun.kw(n_args, args, &kw_args); + + } else { + // function takes a variable number of arguments, but no keywords + + return self->fun.var(n_args, args); + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_var, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_var_call + ); + +/******************************************************************************/ +/* byte code functions */ + +qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) { + const mp_obj_fun_bc_t *fun = MP_OBJ_TO_PTR(fun_in); + const byte *bc = fun->bytecode; + + #if MICROPY_EMIT_NATIVE + if (fun->base.type == &mp_type_fun_native || fun->base.type == &mp_type_native_gen_wrap) { + bc = mp_obj_fun_native_get_prelude_ptr(fun); + } + #endif + + MP_BC_PRELUDE_SIG_DECODE(bc); + MP_BC_PRELUDE_SIZE_DECODE(bc); + + mp_uint_t name = mp_decode_uint_value(bc); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + name = fun->context->constants.qstr_table[name]; + #endif + + return name; +} + +#if MICROPY_CPYTHON_COMPAT +static void fun_bc_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(o_in); + mp_printf(print, "", mp_obj_fun_get_name(o_in), o); +} +#endif + +#if DEBUG_PRINT +static void dump_args(const mp_obj_t *a, size_t sz) { + DEBUG_printf("%p: ", a); + for (size_t i = 0; i < sz; i++) { + DEBUG_printf("%p ", a[i]); + } + DEBUG_printf("\n"); +} +#else +#define dump_args(...) (void)0 +#endif + +// With this macro you can tune the maximum number of function state bytes +// that will be allocated on the stack. Any function that needs more +// than this will try to use the heap, with fallback to stack allocation. +#define VM_MAX_STATE_ON_STACK (sizeof(mp_uint_t) * 11) + +#define DECODE_CODESTATE_SIZE(bytecode, n_state_out_var, state_size_out_var) \ + { \ + const uint8_t *ip = bytecode; \ + size_t n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args; \ + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state_out_var, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args); \ + (void)scope_flags; (void)n_pos_args; (void)n_kwonly_args; (void)n_def_args; \ + \ + /* state size in bytes */ \ + state_size_out_var = n_state_out_var * sizeof(mp_obj_t) \ + + n_exc_stack * sizeof(mp_exc_stack_t); \ + } + +#define INIT_CODESTATE(code_state, _fun_bc, _n_state, n_args, n_kw, args) \ + code_state->fun_bc = _fun_bc; \ + code_state->n_state = _n_state; \ + mp_setup_code_state(code_state, n_args, n_kw, args); \ + code_state->old_globals = mp_globals_get(); + +#if MICROPY_STACKLESS +mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + MP_STACK_CHECK(); + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + + size_t n_state, state_size; + DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size); + + mp_code_state_t *code_state; + #if MICROPY_ENABLE_PYSTACK + code_state = mp_pystack_alloc(sizeof(mp_code_state_t) + state_size); + #else + // If we use m_new_obj_var(), then on no memory, MemoryError will be + // raised. But this is not correct exception for a function call, + // RuntimeError should be raised instead. So, we use m_new_obj_var_maybe(), + // return NULL, then vm.c takes the needed action (either raise + // RuntimeError or fallback to stack allocation). + code_state = m_new_obj_var_maybe(mp_code_state_t, state, byte, state_size); + if (!code_state) { + return NULL; + } + #endif + + INIT_CODESTATE(code_state, self, n_state, n_args, n_kw, args); + + // execute the byte code with the correct globals context + mp_globals_set(self->context->module.globals); + + return code_state; +} +#endif + +static mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + MP_STACK_CHECK(); + + DEBUG_printf("Input n_args: " UINT_FMT ", n_kw: " UINT_FMT "\n", n_args, n_kw); + DEBUG_printf("Input pos args: "); + dump_args(args, n_args); + DEBUG_printf("Input kw args: "); + dump_args(args + n_args, n_kw * 2); + + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + + size_t n_state, state_size; + DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size); + + // allocate state for locals and stack + mp_code_state_t *code_state = NULL; + #if MICROPY_ENABLE_PYSTACK + code_state = mp_pystack_alloc(offsetof(mp_code_state_t, state) + state_size); + #else + if (state_size > VM_MAX_STATE_ON_STACK) { + code_state = m_new_obj_var_maybe(mp_code_state_t, state, byte, state_size); + #if MICROPY_DEBUG_VM_STACK_OVERFLOW + if (code_state != NULL) { + memset(code_state->state, 0, state_size); + } + #endif + } + if (code_state == NULL) { + code_state = alloca(offsetof(mp_code_state_t, state) + state_size); + #if MICROPY_DEBUG_VM_STACK_OVERFLOW + memset(code_state->state, 0, state_size); + #endif + state_size = 0; // indicate that we allocated using alloca + } + #endif + + INIT_CODESTATE(code_state, self, n_state, n_args, n_kw, args); + + // execute the byte code with the correct globals context + mp_globals_set(self->context->module.globals); + mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL); + mp_globals_set(code_state->old_globals); + + #if MICROPY_DEBUG_VM_STACK_OVERFLOW + if (vm_return_kind == MP_VM_RETURN_NORMAL) { + if (code_state->sp < code_state->state) { + mp_printf(MICROPY_DEBUG_PRINTER, "VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state); + assert(0); + } + } + const byte *bytecode_ptr = self->bytecode; + size_t n_state_unused, n_exc_stack_unused, scope_flags_unused; + size_t n_pos_args, n_kwonly_args, n_def_args_unused; + MP_BC_PRELUDE_SIG_DECODE_INTO(bytecode_ptr, n_state_unused, n_exc_stack_unused, + scope_flags_unused, n_pos_args, n_kwonly_args, n_def_args_unused); + // We can't check the case when an exception is returned in state[0] + // and there are no arguments, because in this case our detection slot may have + // been overwritten by the returned exception (which is allowed). + if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_pos_args + n_kwonly_args == 0)) { + // Just check to see that we have at least 1 null object left in the state. + bool overflow = true; + for (size_t i = 0; i < n_state - n_pos_args - n_kwonly_args; ++i) { + if (code_state->state[i] == MP_OBJ_NULL) { + overflow = false; + break; + } + } + if (overflow) { + mp_printf(MICROPY_DEBUG_PRINTER, "VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state); + assert(0); + } + } + #endif + + mp_obj_t result; + if (vm_return_kind == MP_VM_RETURN_NORMAL) { + // return value is in *sp + result = *code_state->sp; + } else { + // must be an exception because normal functions can't yield + assert(vm_return_kind == MP_VM_RETURN_EXCEPTION); + // returned exception is in state[0] + result = code_state->state[0]; + } + + #if MICROPY_ENABLE_PYSTACK + mp_pystack_free(code_state); + #else + // free the state if it was allocated on the heap + if (state_size != 0) { + m_del_var(mp_code_state_t, state, byte, state_size, code_state); + } + #endif + + if (vm_return_kind == MP_VM_RETURN_NORMAL) { + return result; + } else { // MP_VM_RETURN_EXCEPTION + nlr_raise(result); + } +} + +#if MICROPY_PY_FUNCTION_ATTRS +void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + if (attr == MP_QSTR___name__) { + dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(self_in)); + } + if (attr == MP_QSTR___globals__) { + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + dest[0] = MP_OBJ_FROM_PTR(self->context->module.globals); + } +} +#endif + +#if MICROPY_CPYTHON_COMPAT +#define FUN_BC_TYPE_PRINT print, fun_bc_print, +#else +#define FUN_BC_TYPE_PRINT +#endif + +#if MICROPY_PY_FUNCTION_ATTRS +#define FUN_BC_TYPE_ATTR attr, mp_obj_fun_bc_attr, +#else +#define FUN_BC_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_bc, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + FUN_BC_TYPE_PRINT + FUN_BC_TYPE_ATTR + call, fun_bc_call + ); + +mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_module_context_t *context, struct _mp_raw_code_t *const *child_table) { + size_t n_def_args = 0; + size_t n_extra_args = 0; + mp_obj_tuple_t *def_pos_args = NULL; + mp_obj_t def_kw_args = MP_OBJ_NULL; + if (def_args != NULL && def_args[0] != MP_OBJ_NULL) { + assert(mp_obj_is_type(def_args[0], &mp_type_tuple)); + def_pos_args = MP_OBJ_TO_PTR(def_args[0]); + n_def_args = def_pos_args->len; + n_extra_args = def_pos_args->len; + } + if (def_args != NULL && def_args[1] != MP_OBJ_NULL) { + assert(mp_obj_is_type(def_args[1], &mp_type_dict)); + def_kw_args = def_args[1]; + n_extra_args += 1; + } + mp_obj_fun_bc_t *o = mp_obj_malloc_var(mp_obj_fun_bc_t, extra_args, mp_obj_t, n_extra_args, &mp_type_fun_bc); + o->bytecode = code; + o->context = context; + o->child_table = child_table; + if (def_pos_args != NULL) { + memcpy(o->extra_args, def_pos_args->items, n_def_args * sizeof(mp_obj_t)); + } + if (def_kw_args != MP_OBJ_NULL) { + o->extra_args[n_def_args] = def_kw_args; + } + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +/* native functions */ + +#if MICROPY_EMIT_NATIVE + +static mp_obj_t fun_native_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + MP_STACK_CHECK(); + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + mp_call_fun_t fun = mp_obj_fun_native_get_function_start(self); + return fun(self_in, n_args, n_kw, args); +} + +#if MICROPY_CPYTHON_COMPAT +#define FUN_BC_TYPE_PRINT print, fun_bc_print, +#else +#define FUN_BC_TYPE_PRINT +#endif +#if MICROPY_PY_FUNCTION_ATTRS +#define FUN_BC_TYPE_ATTR attr, mp_obj_fun_bc_attr, +#else +#define FUN_BC_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_native, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + FUN_BC_TYPE_PRINT + FUN_BC_TYPE_ATTR + call, fun_native_call + ); + +#endif // MICROPY_EMIT_NATIVE + +/******************************************************************************/ +/* viper functions */ + +#if MICROPY_EMIT_NATIVE + +static mp_obj_t fun_viper_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + MP_STACK_CHECK(); + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + mp_call_fun_t fun = MICROPY_MAKE_POINTER_CALLABLE((void *)self->bytecode); + return fun(self_in, n_args, n_kw, args); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_viper, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + call, fun_viper_call + ); + +#endif // MICROPY_EMIT_NATIVE + +/******************************************************************************/ +/* inline assembler functions */ + +#if MICROPY_EMIT_INLINE_ASM + +typedef mp_uint_t (*inline_asm_fun_0_t)(void); +typedef mp_uint_t (*inline_asm_fun_1_t)(mp_uint_t); +typedef mp_uint_t (*inline_asm_fun_2_t)(mp_uint_t, mp_uint_t); +typedef mp_uint_t (*inline_asm_fun_3_t)(mp_uint_t, mp_uint_t, mp_uint_t); +typedef mp_uint_t (*inline_asm_fun_4_t)(mp_uint_t, mp_uint_t, mp_uint_t, mp_uint_t); + +// convert a MicroPython object to a sensible value for inline asm +static mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) { + // TODO for byte_array, pass pointer to the array + if (mp_obj_is_small_int(obj)) { + return MP_OBJ_SMALL_INT_VALUE(obj); + } else if (obj == mp_const_none) { + return 0; + } else if (obj == mp_const_false) { + return 0; + } else if (obj == mp_const_true) { + return 1; + } else if (mp_obj_is_exact_type(obj, &mp_type_int)) { + return mp_obj_int_get_truncated(obj); + } else if (mp_obj_is_str(obj)) { + // pointer to the string (it's probably constant though!) + size_t l; + return (mp_uint_t)mp_obj_str_get_data(obj, &l); + } else { + const mp_obj_type_t *type = mp_obj_get_type(obj); + #if MICROPY_PY_BUILTINS_FLOAT + if (type == &mp_type_float) { + // convert float to int (could also pass in float registers) + return (mp_int_t)mp_obj_float_get(obj); + } + #endif + if (type == &mp_type_tuple || type == &mp_type_list) { + // pointer to start of tuple (could pass length, but then could use len(x) for that) + size_t len; + mp_obj_t *items; + mp_obj_get_array(obj, &len, &items); + return (mp_uint_t)items; + } else { + mp_buffer_info_t bufinfo; + if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_READ)) { + // supports the buffer protocol, return a pointer to the data + return (mp_uint_t)bufinfo.buf; + } else { + // just pass along a pointer to the object + return (mp_uint_t)obj; + } + } + } +} + +static mp_obj_t fun_asm_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_obj_fun_asm_t *self = MP_OBJ_TO_PTR(self_in); + + mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false); + + const void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data); + + mp_uint_t ret; + if (n_args == 0) { + ret = ((inline_asm_fun_0_t)fun)(); + } else if (n_args == 1) { + ret = ((inline_asm_fun_1_t)fun)(convert_obj_for_inline_asm(args[0])); + } else if (n_args == 2) { + ret = ((inline_asm_fun_2_t)fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1])); + } else if (n_args == 3) { + ret = ((inline_asm_fun_3_t)fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[2])); + } else { + // compiler allows at most 4 arguments + assert(n_args == 4); + ret = ((inline_asm_fun_4_t)fun)( + convert_obj_for_inline_asm(args[0]), + convert_obj_for_inline_asm(args[1]), + convert_obj_for_inline_asm(args[2]), + convert_obj_for_inline_asm(args[3]) + ); + } + + return mp_native_to_obj(ret, self->type_sig); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_asm, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + call, fun_asm_call + ); + +#endif // MICROPY_EMIT_INLINE_ASM diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objfun.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objfun.h new file mode 100644 index 00000000..af7c3348 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objfun.h @@ -0,0 +1,112 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJFUN_H +#define MICROPY_INCLUDED_PY_OBJFUN_H + +#include "py/bc.h" +#include "py/obj.h" + +typedef struct _mp_obj_fun_bc_t { + mp_obj_base_t base; + const mp_module_context_t *context; // context within which this function was defined + struct _mp_raw_code_t *const *child_table; // table of children + const byte *bytecode; // bytecode for the function + #if MICROPY_PY_SYS_SETTRACE + const struct _mp_raw_code_t *rc; + #endif + // the following extra_args array is allocated space to take (in order): + // - values of positional default args (if any) + // - a single slot for default kw args dict (if it has them) + mp_obj_t extra_args[]; +} mp_obj_fun_bc_t; + +typedef struct _mp_obj_fun_asm_t { + mp_obj_base_t base; + size_t n_args; + const void *fun_data; // GC must be able to trace this pointer + mp_uint_t type_sig; +} mp_obj_fun_asm_t; + +mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_module_context_t *cm, struct _mp_raw_code_t *const *raw_code_table); +void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); + +#if MICROPY_EMIT_NATIVE + +static inline mp_obj_t mp_obj_new_fun_native(const mp_obj_t *def_args, const void *fun_data, const mp_module_context_t *mc, struct _mp_raw_code_t *const *child_table) { + mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(mp_obj_new_fun_bc(def_args, (const byte *)fun_data, mc, child_table)); + o->base.type = &mp_type_fun_native; + return MP_OBJ_FROM_PTR(o); +} + +static inline mp_obj_t mp_obj_new_fun_viper(const void *fun_data, const mp_module_context_t *mc, struct _mp_raw_code_t *const *child_table) { + mp_obj_fun_bc_t *o = mp_obj_malloc(mp_obj_fun_bc_t, &mp_type_fun_viper); + o->bytecode = fun_data; + o->context = mc; + o->child_table = child_table; + return MP_OBJ_FROM_PTR(o); +} + +static inline const uint8_t *mp_obj_fun_native_get_prelude_ptr(const mp_obj_fun_bc_t *fun_native) { + // Obtain a pointer to the start of the function prelude, based on prelude_ptr_index. + uintptr_t prelude_ptr_index = ((uintptr_t *)fun_native->bytecode)[0]; + const uint8_t *prelude_ptr; + if (prelude_ptr_index == 0) { + prelude_ptr = (const uint8_t *)fun_native->child_table; + } else { + prelude_ptr = (const uint8_t *)fun_native->child_table[prelude_ptr_index]; + } + return prelude_ptr; +} + +static inline void *mp_obj_fun_native_get_function_start(const mp_obj_fun_bc_t *fun_native) { + // Obtain a pointer to the start of the function executable machine code. + return MICROPY_MAKE_POINTER_CALLABLE((void *)(fun_native->bytecode + sizeof(uintptr_t))); +} + +static inline void *mp_obj_fun_native_get_generator_start(const mp_obj_fun_bc_t *fun_native) { + // Obtain a pointer to the start of the generator executable machine code. + uintptr_t start_offset = ((uintptr_t *)fun_native->bytecode)[1]; + return MICROPY_MAKE_POINTER_CALLABLE((void *)(fun_native->bytecode + start_offset)); +} + +static inline void *mp_obj_fun_native_get_generator_resume(const mp_obj_fun_bc_t *fun_native) { + // Obtain a pointer to the resume location of the generator executable machine code. + return MICROPY_MAKE_POINTER_CALLABLE((void *)&((uintptr_t *)fun_native->bytecode)[2]); +} + +#endif + +#if MICROPY_EMIT_INLINE_ASM +static inline mp_obj_t mp_obj_new_fun_asm(size_t n_args, const void *fun_data, mp_uint_t type_sig) { + mp_obj_fun_asm_t *o = mp_obj_malloc(mp_obj_fun_asm_t, &mp_type_fun_asm); + o->n_args = n_args; + o->fun_data = fun_data; + o->type_sig = type_sig; + return MP_OBJ_FROM_PTR(o); +} +#endif + +#endif // MICROPY_INCLUDED_PY_OBJFUN_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objgenerator.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objgenerator.c new file mode 100644 index 00000000..431cbad5 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objgenerator.c @@ -0,0 +1,364 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * Copyright (c) 2014-2017 Paul Sokolovsky + * + * 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 "py/runtime.h" +#include "py/bc.h" +#include "py/objstr.h" +#include "py/objgenerator.h" +#include "py/objfun.h" +#include "py/stackctrl.h" + +// Instance of GeneratorExit exception - needed by generator.close() +const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t *)&mp_const_empty_tuple_obj}; + +/******************************************************************************/ +/* generator wrapper */ + +typedef struct _mp_obj_gen_instance_t { + mp_obj_base_t base; + // mp_const_none: Not-running, no exception. + // MP_OBJ_NULL: Running, no exception. + // other: Not running, pending exception. + mp_obj_t pend_exc; + mp_code_state_t code_state; +} mp_obj_gen_instance_t; + +static mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // A generating function is just a bytecode function with type mp_type_gen_wrap + mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); + + // bytecode prelude: get state size and exception stack size + const uint8_t *ip = self_fun->bytecode; + MP_BC_PRELUDE_SIG_DECODE(ip); + + // allocate the generator object, with room for local stack and exception stack + mp_obj_gen_instance_t *o = mp_obj_malloc_var(mp_obj_gen_instance_t, code_state.state, byte, + n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t), + &mp_type_gen_instance); + + o->pend_exc = mp_const_none; + o->code_state.fun_bc = self_fun; + o->code_state.n_state = n_state; + mp_setup_code_state(&o->code_state, n_args, n_kw, args); + return MP_OBJ_FROM_PTR(o); +} + +#if MICROPY_PY_FUNCTION_ATTRS +#define GEN_WRAP_TYPE_ATTR attr, mp_obj_fun_bc_attr, +#else +#define GEN_WRAP_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_gen_wrap, + MP_QSTR_generator, + MP_TYPE_FLAG_BINDS_SELF, + GEN_WRAP_TYPE_ATTR + call, gen_wrap_call + ); + +/******************************************************************************/ +// native generator wrapper + +#if MICROPY_EMIT_NATIVE + +// Based on mp_obj_gen_instance_t. +typedef struct _mp_obj_gen_instance_native_t { + mp_obj_base_t base; + mp_obj_t pend_exc; + mp_code_state_native_t code_state; +} mp_obj_gen_instance_native_t; + +static mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // The state for a native generating function is held in the same struct as a bytecode function + mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); + + // Determine start of prelude. + const uint8_t *prelude_ptr = mp_obj_fun_native_get_prelude_ptr(self_fun); + + // Extract n_state from the prelude. + const uint8_t *ip = prelude_ptr; + MP_BC_PRELUDE_SIG_DECODE(ip); + + // Allocate the generator object, with room for local stack (exception stack not needed). + mp_obj_gen_instance_native_t *o = mp_obj_malloc_var(mp_obj_gen_instance_native_t, code_state.state, byte, n_state * sizeof(mp_obj_t), &mp_type_gen_instance); + + // Parse the input arguments and set up the code state + o->pend_exc = mp_const_none; + o->code_state.fun_bc = self_fun; + o->code_state.n_state = n_state; + mp_setup_code_state_native(&o->code_state, n_args, n_kw, args); + + // Indicate we are a native function, which doesn't use this variable + o->code_state.exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_SENTINEL; + + // Prepare the generator instance for execution + o->code_state.ip = mp_obj_fun_native_get_generator_start(self_fun); + + return MP_OBJ_FROM_PTR(o); +} + +#if MICROPY_PY_FUNCTION_ATTRS +#define NATIVE_GEN_WRAP_TYPE_ATTR , attr, mp_obj_fun_bc_attr +#else +#define NATIVE_GEN_WRAP_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_native_gen_wrap, + MP_QSTR_generator, + MP_TYPE_FLAG_BINDS_SELF, + call, native_gen_wrap_call + NATIVE_GEN_WRAP_TYPE_ATTR + ); + +#endif // MICROPY_EMIT_NATIVE + +/******************************************************************************/ +/* generator instance */ + +static void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self); +} + +mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) { + MP_STACK_CHECK(); + mp_check_self(mp_obj_is_type(self_in, &mp_type_gen_instance)); + mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); + if (self->code_state.ip == 0) { + // Trying to resume an already stopped generator. + // This is an optimised "raise StopIteration(None)". + *ret_val = mp_const_none; + return MP_VM_RETURN_NORMAL; + } + + // Ensure the generator cannot be reentered during execution + if (self->pend_exc == MP_OBJ_NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("generator already executing")); + } + + #if MICROPY_PY_GENERATOR_PEND_THROW + // If exception is pending (set using .pend_throw()), process it now. + if (self->pend_exc != mp_const_none) { + throw_value = self->pend_exc; + } + #endif + + // If the generator is started, allow sending a value. + void *state_start = self->code_state.state - 1; + #if MICROPY_EMIT_NATIVE + if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { + state_start = ((mp_obj_gen_instance_native_t *)self)->code_state.state - 1; + } + #endif + if (self->code_state.sp == state_start) { + if (send_value != mp_const_none) { + mp_raise_TypeError(MP_ERROR_TEXT("can't send non-None value to a just-started generator")); + } + } else { + *self->code_state.sp = send_value; + } + + // Mark as running + self->pend_exc = MP_OBJ_NULL; + + // Set up the correct globals context for the generator and execute it + self->code_state.old_globals = mp_globals_get(); + mp_globals_set(self->code_state.fun_bc->context->module.globals); + + mp_vm_return_kind_t ret_kind; + + #if MICROPY_EMIT_NATIVE + if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { + // A native generator. + typedef uintptr_t (*mp_fun_native_gen_t)(void *, mp_obj_t); + mp_fun_native_gen_t fun = mp_obj_fun_native_get_generator_resume(self->code_state.fun_bc); + ret_kind = fun((void *)&self->code_state, throw_value); + } else + #endif + { + // A bytecode generator + ret_kind = mp_execute_bytecode(&self->code_state, throw_value); + } + + mp_globals_set(self->code_state.old_globals); + + // Mark as not running + self->pend_exc = mp_const_none; + + switch (ret_kind) { + case MP_VM_RETURN_NORMAL: + default: + // Explicitly mark generator as completed. If we don't do this, + // subsequent next() may re-execute statements after last yield + // again and again, leading to side effects. + self->code_state.ip = 0; + // This is an optimised "raise StopIteration(*ret_val)". + *ret_val = *self->code_state.sp; + break; + + case MP_VM_RETURN_YIELD: + *ret_val = *self->code_state.sp; + #if MICROPY_PY_GENERATOR_PEND_THROW + *self->code_state.sp = mp_const_none; + #endif + break; + + case MP_VM_RETURN_EXCEPTION: { + self->code_state.ip = 0; + #if MICROPY_EMIT_NATIVE + if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { + *ret_val = ((mp_obj_gen_instance_native_t *)self)->code_state.state[0]; + } else + #endif + { + *ret_val = self->code_state.state[0]; + } + // PEP479: if StopIteration is raised inside a generator it is replaced with RuntimeError + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(*ret_val)), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { + *ret_val = mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("generator raised StopIteration")); + } + break; + } + } + + return ret_kind; +} + +static mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, bool raise_stop_iteration) { + mp_obj_t ret; + switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) { + case MP_VM_RETURN_NORMAL: + default: + // A normal return is a StopIteration, either raise it or return + // MP_OBJ_STOP_ITERATION as an optimisation. + if (ret == mp_const_none) { + ret = MP_OBJ_NULL; + } + if (raise_stop_iteration) { + mp_raise_StopIteration(ret); + } else { + return mp_make_stop_iteration(ret); + } + + case MP_VM_RETURN_YIELD: + return ret; + + case MP_VM_RETURN_EXCEPTION: + nlr_raise(ret); + } +} + +static mp_obj_t gen_instance_iternext(mp_obj_t self_in) { + return gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL, false); +} + +static mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { + return gen_resume_and_raise(self_in, send_value, MP_OBJ_NULL, true); +} +static MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); + +static mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) { + // The signature of this function is: throw(type[, value[, traceback]]) + // CPython will pass all given arguments through the call chain and process them + // at the point they are used (native generators will handle them differently to + // user-defined generators with a throw() method). To save passing multiple + // values, MicroPython instead does partial processing here to reduce it down to + // one argument and passes that through: + // - if only args[1] is given, or args[2] is given but is None, args[1] is + // passed through (in the standard case it is an exception class or instance) + // - if args[2] is given and not None it is passed through (in the standard + // case it would be an exception instance and args[1] its corresponding class) + // - args[3] is always ignored + + mp_obj_t exc = args[1]; + if (n_args > 2 && args[2] != mp_const_none) { + exc = args[2]; + } + + return gen_resume_and_raise(args[0], mp_const_none, exc, true); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gen_instance_throw_obj, 2, 4, gen_instance_throw); + +static mp_obj_t gen_instance_close(mp_obj_t self_in) { + mp_obj_t ret; + switch (mp_obj_gen_resume(self_in, mp_const_none, MP_OBJ_FROM_PTR(&mp_const_GeneratorExit_obj), &ret)) { + case MP_VM_RETURN_YIELD: + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("generator ignored GeneratorExit")); + + // Swallow GeneratorExit (== successful close), and re-raise any other + case MP_VM_RETURN_EXCEPTION: + // ret should always be an instance of an exception class + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(ret)), MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { + return mp_const_none; + } + nlr_raise(ret); + + default: + // The only choice left is MP_VM_RETURN_NORMAL which is successful close + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_close_obj, gen_instance_close); + +#if MICROPY_PY_GENERATOR_PEND_THROW +static mp_obj_t gen_instance_pend_throw(mp_obj_t self_in, mp_obj_t exc_in) { + mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); + if (self->pend_exc == MP_OBJ_NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("generator already executing")); + } + mp_obj_t prev = self->pend_exc; + self->pend_exc = exc_in; + return prev; +} +static MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_pend_throw_obj, gen_instance_pend_throw); +#endif + +static const mp_rom_map_elem_t gen_instance_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&gen_instance_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&gen_instance_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_throw), MP_ROM_PTR(&gen_instance_throw_obj) }, + #if MICROPY_PY_GENERATOR_PEND_THROW + { MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(gen_instance_locals_dict, gen_instance_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_gen_instance, + MP_QSTR_generator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + print, gen_instance_print, + iter, gen_instance_iternext, + locals_dict, &gen_instance_locals_dict + ); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objgenerator.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objgenerator.h new file mode 100644 index 00000000..80bf9cd8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objgenerator.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJGENERATOR_H +#define MICROPY_INCLUDED_PY_OBJGENERATOR_H + +#include "py/obj.h" +#include "py/runtime.h" + +mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_val, mp_obj_t throw_val, mp_obj_t *ret_val); + +#endif // MICROPY_INCLUDED_PY_OBJGENERATOR_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objgetitemiter.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objgetitemiter.c new file mode 100644 index 00000000..c735c65b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objgetitemiter.c @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +// this is a wrapper object that turns something that has a __getitem__ method into an iterator + +typedef struct _mp_obj_getitem_iter_t { + mp_obj_base_t base; + mp_obj_t args[3]; +} mp_obj_getitem_iter_t; + +static mp_obj_t it_iternext(mp_obj_t self_in) { + mp_obj_getitem_iter_t *self = MP_OBJ_TO_PTR(self_in); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + // try to get next item + mp_obj_t value = mp_call_method_n_kw(1, 0, self->args); + self->args[2] = MP_OBJ_NEW_SMALL_INT(MP_OBJ_SMALL_INT_VALUE(self->args[2]) + 1); + nlr_pop(); + return value; + } else { + // an exception was raised + mp_obj_type_t *t = (mp_obj_type_t *)((mp_obj_base_t *)nlr.ret_val)->type; + if (t == &mp_type_StopIteration || t == &mp_type_IndexError) { + return MP_OBJ_STOP_ITERATION; + } else { + // re-raise exception + nlr_jump(nlr.ret_val); + } + } +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, it_iternext + ); + +// args are those returned from mp_load_method_maybe (ie either an attribute or a method) +mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_getitem_iter_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_getitem_iter_t *o = (mp_obj_getitem_iter_t *)iter_buf; + o->base.type = &mp_type_it; + o->args[0] = args[0]; + o->args[1] = args[1]; + o->args[2] = MP_OBJ_NEW_SMALL_INT(0); + return MP_OBJ_FROM_PTR(o); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objint.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objint.c new file mode 100644 index 00000000..6caa608f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objint.c @@ -0,0 +1,469 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/parsenum.h" +#include "py/smallint.h" +#include "py/objint.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/binary.h" + +#if MICROPY_PY_BUILTINS_FLOAT +#include +#endif + +// This dispatcher function is expected to be independent of the implementation of long int +static mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 0, 2, false); + + switch (n_args) { + case 0: + return MP_OBJ_NEW_SMALL_INT(0); + + case 1: { + mp_buffer_info_t bufinfo; + mp_obj_t o = mp_unary_op(MP_UNARY_OP_INT_MAYBE, args[0]); + if (o != MP_OBJ_NULL) { + return o; + } else if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { + // a textual representation, parse it + return mp_parse_num_integer(bufinfo.buf, bufinfo.len, 0, NULL); + #if MICROPY_PY_BUILTINS_FLOAT + } else if (mp_obj_is_float(args[0])) { + return mp_obj_new_int_from_float(mp_obj_float_get(args[0])); + #endif + } else { + mp_raise_TypeError_int_conversion(args[0]); + } + } + + case 2: + default: { + // should be a string, parse it + size_t l; + const char *s = mp_obj_str_get_data(args[0], &l); + return mp_parse_num_integer(s, l, mp_obj_get_int(args[1]), NULL); + } + } +} + +#if MICROPY_PY_BUILTINS_FLOAT + +typedef enum { + MP_FP_CLASS_FIT_SMALLINT, + MP_FP_CLASS_FIT_LONGINT, + MP_FP_CLASS_OVERFLOW +} mp_fp_as_int_class_t; + +static mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) { + union { + mp_float_t f; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t i; + #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + uint32_t i[2]; + #endif + } u = {val}; + + uint32_t e; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + e = u.i; + #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + e = u.i[MP_ENDIANNESS_LITTLE]; + #endif +#define MP_FLOAT_SIGN_SHIFT_I32 ((MP_FLOAT_FRAC_BITS + MP_FLOAT_EXP_BITS) % 32) +#define MP_FLOAT_EXP_SHIFT_I32 (MP_FLOAT_FRAC_BITS % 32) + + if (e & (1U << MP_FLOAT_SIGN_SHIFT_I32)) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + e |= u.i[MP_ENDIANNESS_BIG] != 0; + #endif + if ((e & ~(1U << MP_FLOAT_SIGN_SHIFT_I32)) == 0) { + // handle case of -0 (when sign is set but rest of bits are zero) + e = 0; + } else { + e += ((1U << MP_FLOAT_EXP_BITS) - 1) << MP_FLOAT_EXP_SHIFT_I32; + } + } else { + e &= ~((1U << MP_FLOAT_EXP_SHIFT_I32) - 1); + } + // 8 * sizeof(uintptr_t) counts the number of bits for a small int + // TODO provide a way to configure this properly + if (e <= ((8 * sizeof(uintptr_t) + MP_FLOAT_EXP_BIAS - 3) << MP_FLOAT_EXP_SHIFT_I32)) { + return MP_FP_CLASS_FIT_SMALLINT; + } + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG + if (e <= (((sizeof(long long) * MP_BITS_PER_BYTE) + MP_FLOAT_EXP_BIAS - 2) << MP_FLOAT_EXP_SHIFT_I32)) { + return MP_FP_CLASS_FIT_LONGINT; + } + #endif + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ + return MP_FP_CLASS_FIT_LONGINT; + #else + return MP_FP_CLASS_OVERFLOW; + #endif +} +#undef MP_FLOAT_SIGN_SHIFT_I32 +#undef MP_FLOAT_EXP_SHIFT_I32 + +mp_obj_t mp_obj_new_int_from_float(mp_float_t val) { + mp_float_union_t u = {val}; + // IEEE-754: if biased exponent is all 1 bits... + if (u.p.exp == ((1 << MP_FLOAT_EXP_BITS) - 1)) { + // ...then number is Inf (positive or negative) if fraction is 0, else NaN. + if (u.p.frc == 0) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("can't convert inf to int")); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("can't convert NaN to int")); + } + } else { + mp_fp_as_int_class_t icl = mp_classify_fp_as_int(val); + if (icl == MP_FP_CLASS_FIT_SMALLINT) { + return MP_OBJ_NEW_SMALL_INT((mp_int_t)val); + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ + } else { + mp_obj_int_t *o = mp_obj_int_new_mpz(); + mpz_set_from_float(&o->mpz, val); + return MP_OBJ_FROM_PTR(o); + } + #else + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG + } else if (icl == MP_FP_CLASS_FIT_LONGINT) { + return mp_obj_new_int_from_ll((long long)val); + #endif + } else { + mp_raise_ValueError(MP_ERROR_TEXT("float too big")); + } + #endif + } +} + +#endif + +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG +typedef mp_longint_impl_t fmt_int_t; +typedef unsigned long long fmt_uint_t; +#else +typedef mp_int_t fmt_int_t; +typedef mp_uint_t fmt_uint_t; +#endif + +void mp_obj_int_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + // The size of this buffer is rather arbitrary. If it's not large + // enough, a dynamic one will be allocated. + char stack_buf[sizeof(fmt_int_t) * 4]; + char *buf = stack_buf; + size_t buf_size = sizeof(stack_buf); + size_t fmt_size; + + char *str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size, self_in, 10, NULL, '\0', '\0'); + mp_print_str(print, str); + + if (buf != stack_buf) { + m_del(char, buf, buf_size); + } +} + +static const uint8_t log_base2_floor[] = { + 0, 1, 1, 2, + 2, 2, 2, 3, + 3, 3, 3, 3, + 3, 3, 3, 4, + /* if needed, these are the values for higher bases + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 5 + */ +}; + +size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char comma) { + assert(2 <= base && base <= 16); + size_t num_digits = num_bits / log_base2_floor[base - 1] + 1; + size_t num_commas = comma ? num_digits / 3 : 0; + size_t prefix_len = prefix ? strlen(prefix) : 0; + return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte +} + +// This routine expects you to pass in a buffer and size (in *buf and *buf_size). +// If, for some reason, this buffer is too small, then it will allocate a +// buffer and return the allocated buffer and size in *buf and *buf_size. It +// is the callers responsibility to free this allocated buffer. +// +// The resulting formatted string will be returned from this function and the +// formatted size will be in *fmt_size. +char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in, + int base, const char *prefix, char base_char, char comma) { + fmt_int_t num; + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE + // Only have small ints; get the integer value to format. + num = MP_OBJ_SMALL_INT_VALUE(self_in); + #else + if (mp_obj_is_small_int(self_in)) { + // A small int; get the integer value to format. + num = MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); + // Not a small int. + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG + const mp_obj_int_t *self = self_in; + // Get the value to format; mp_obj_get_int truncates to mp_int_t. + num = self->val; + #else + // Delegate to the implementation for the long int. + return mp_obj_int_formatted_impl(buf, buf_size, fmt_size, self_in, base, prefix, base_char, comma); + #endif + } + #endif + + char sign = '\0'; + if (num < 0) { + num = -num; + sign = '-'; + } + + size_t needed_size = mp_int_format_size(sizeof(fmt_int_t) * 8, base, prefix, comma); + if (needed_size > *buf_size) { + *buf = m_new(char, needed_size); + *buf_size = needed_size; + } + char *str = *buf; + + char *b = str + needed_size; + *(--b) = '\0'; + char *last_comma = b; + + if (num == 0) { + *(--b) = '0'; + } else { + do { + // The cast to fmt_uint_t is because num is positive and we want unsigned arithmetic + int c = (fmt_uint_t)num % base; + num = (fmt_uint_t)num / base; + if (c >= 10) { + c += base_char - 10; + } else { + c += '0'; + } + *(--b) = c; + if (comma && num != 0 && b > str && (last_comma - b) == 3) { + *(--b) = comma; + last_comma = b; + } + } + while (b > str && num != 0); + } + if (prefix) { + size_t prefix_len = strlen(prefix); + char *p = b - prefix_len; + if (p > str) { + b = p; + while (*prefix) { + *p++ = *prefix++; + } + } + } + if (sign && b > str) { + *(--b) = sign; + } + *fmt_size = *buf + needed_size - b - 1; + + return b; +} + +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE + +int mp_obj_int_sign(mp_obj_t self_in) { + mp_int_t val = mp_obj_get_int(self_in); + if (val < 0) { + return -1; + } else if (val > 0) { + return 1; + } else { + return 0; + } +} + +// This is called for operations on SMALL_INT that are not handled by mp_unary_op +mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + return MP_OBJ_NULL; // op not supported +} + +// This is called for operations on SMALL_INT that are not handled by mp_binary_op +mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + return mp_obj_int_binary_op_extra_cases(op, lhs_in, rhs_in); +} + +// This is called only with strings whose value doesn't fit in SMALL_INT +mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("long int not supported in this build")); + return mp_const_none; +} + +// This is called when an integer larger than a SMALL_INT is needed (although val might still fit in a SMALL_INT) +mp_obj_t mp_obj_new_int_from_ll(long long val) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); + return mp_const_none; +} + +// This is called when an integer larger than a SMALL_INT is needed (although val might still fit in a SMALL_INT) +mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); + return mp_const_none; +} + +mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) { + // SMALL_INT accepts only signed numbers, so make sure the input + // value fits completely in the small-int positive range. + if ((value & ~MP_SMALL_INT_POSITIVE_MASK) == 0) { + return MP_OBJ_NEW_SMALL_INT(value); + } + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); + return mp_const_none; +} + +mp_obj_t mp_obj_new_int(mp_int_t value) { + if (MP_SMALL_INT_FITS(value)) { + return MP_OBJ_NEW_SMALL_INT(value); + } + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); + return mp_const_none; +} + +mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { + return MP_OBJ_SMALL_INT_VALUE(self_in); +} + +mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { + return MP_OBJ_SMALL_INT_VALUE(self_in); +} + +#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE + +// This dispatcher function is expected to be independent of the implementation of long int +// It handles the extra cases for integer-like arithmetic +mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + if (rhs_in == mp_const_false) { + // false acts as 0 + return mp_binary_op(op, lhs_in, MP_OBJ_NEW_SMALL_INT(0)); + } else if (rhs_in == mp_const_true) { + // true acts as 0 + return mp_binary_op(op, lhs_in, MP_OBJ_NEW_SMALL_INT(1)); + } else if (op == MP_BINARY_OP_MULTIPLY) { + if (mp_obj_is_str_or_bytes(rhs_in) || mp_obj_is_type(rhs_in, &mp_type_tuple) || mp_obj_is_type(rhs_in, &mp_type_list)) { + // multiply is commutative for these types, so delegate to them + return mp_binary_op(op, rhs_in, lhs_in); + } + } + return MP_OBJ_NULL; // op not supported +} + +// this is a classmethod +static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { + // TODO: Support signed param (assumes signed=False at the moment) + (void)n_args; + + // get the buffer info + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + + const byte *buf = (const byte *)bufinfo.buf; + int delta = 1; + if (args[2] == MP_OBJ_NEW_QSTR(MP_QSTR_little)) { + buf += bufinfo.len - 1; + delta = -1; + } + + mp_uint_t value = 0; + size_t len = bufinfo.len; + for (; len--; buf += delta) { + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + if (value > (MP_SMALL_INT_MAX >> 8)) { + // Result will overflow a small-int so construct a big-int + return mp_obj_int_from_bytes_impl(args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little), bufinfo.len, bufinfo.buf); + } + #endif + value = (value << 8) | *buf; + } + return mp_obj_new_int_from_uint(value); +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 3, 4, int_from_bytes); +static MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj)); + +static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { + // TODO: Support signed param (assumes signed=False) + (void)n_args; + + mp_int_t len = mp_obj_get_int(args[1]); + if (len < 0) { + mp_raise_ValueError(NULL); + } + bool big_endian = args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little); + + vstr_t vstr; + vstr_init_len(&vstr, len); + byte *data = (byte *)vstr.buf; + memset(data, 0, len); + + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + if (!mp_obj_is_small_int(args[0])) { + mp_obj_int_to_bytes_impl(args[0], big_endian, len, data); + } else + #endif + { + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(args[0]); + size_t l = MIN((size_t)len, sizeof(val)); + mp_binary_set_int(l, big_endian, data + (big_endian ? (len - l) : 0), val); + } + + return mp_obj_new_bytes_from_vstr(&vstr); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes); + +static const mp_rom_map_elem_t int_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_to_bytes), MP_ROM_PTR(&int_to_bytes_obj) }, +}; + +static MP_DEFINE_CONST_DICT(int_locals_dict, int_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_int, + MP_QSTR_int, + MP_TYPE_FLAG_NONE, + make_new, mp_obj_int_make_new, + print, mp_obj_int_print, + unary_op, mp_obj_int_unary_op, + binary_op, mp_obj_int_binary_op, + locals_dict, &int_locals_dict + ); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objint.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objint.h new file mode 100644 index 00000000..5eed8770 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objint.h @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJINT_H +#define MICROPY_INCLUDED_PY_OBJINT_H + +#include "py/mpz.h" +#include "py/obj.h" + +typedef struct _mp_obj_int_t { + mp_obj_base_t base; + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG + mp_longint_impl_t val; + #elif MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ + mpz_t mpz; + #endif +} mp_obj_int_t; + +extern const mp_obj_int_t mp_sys_maxsize_obj; + +#if MICROPY_PY_BUILTINS_FLOAT +mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in); +#endif + +size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char comma); + +mp_obj_int_t *mp_obj_int_new_mpz(void); + +void mp_obj_int_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in, + int base, const char *prefix, char base_char, char comma); +char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in, + int base, const char *prefix, char base_char, char comma); +mp_int_t mp_obj_int_hash(mp_obj_t self_in); +mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf); +void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf); +int mp_obj_int_sign(mp_obj_t self_in); +mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in); +mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); +mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); +mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus); + +#endif // MICROPY_INCLUDED_PY_OBJINT_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objint_longlong.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objint_longlong.c new file mode 100644 index 00000000..ee499e02 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objint_longlong.c @@ -0,0 +1,295 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/smallint.h" +#include "py/objint.h" +#include "py/runtime.h" + +#if MICROPY_PY_BUILTINS_FLOAT +#include +#endif + +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG + +#if MICROPY_PY_SYS_MAXSIZE +// Export value for sys.maxsize +const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; +#endif + +mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) { + int delta = 1; + if (!big_endian) { + buf += len - 1; + delta = -1; + } + + mp_longint_impl_t value = 0; + for (; len--; buf += delta) { + value = (value << 8) | *buf; + } + return mp_obj_new_int_from_ll(value); +} + +void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); + mp_obj_int_t *self = self_in; + long long val = self->val; + if (big_endian) { + byte *b = buf + len; + while (b > buf) { + *--b = val; + val >>= 8; + } + } else { + for (; len > 0; --len) { + *buf++ = val; + val >>= 8; + } + } +} + +int mp_obj_int_sign(mp_obj_t self_in) { + mp_longint_impl_t val; + if (mp_obj_is_small_int(self_in)) { + val = MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + mp_obj_int_t *self = self_in; + val = self->val; + } + if (val < 0) { + return -1; + } else if (val > 0) { + return 1; + } else { + return 0; + } +} + +mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + mp_obj_int_t *o = o_in; + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(o->val != 0); + + // truncate value to fit in mp_int_t, which gives the same hash as + // small int if the value fits without truncation + case MP_UNARY_OP_HASH: + return MP_OBJ_NEW_SMALL_INT((mp_int_t)o->val); + + case MP_UNARY_OP_POSITIVE: + return o_in; + case MP_UNARY_OP_NEGATIVE: + return mp_obj_new_int_from_ll(-o->val); + case MP_UNARY_OP_INVERT: + return mp_obj_new_int_from_ll(~o->val); + case MP_UNARY_OP_ABS: { + mp_obj_int_t *self = MP_OBJ_TO_PTR(o_in); + if (self->val >= 0) { + return o_in; + } + self = mp_obj_new_int_from_ll(self->val); + // TODO could overflow long long + self->val = -self->val; + return MP_OBJ_FROM_PTR(self); + } + case MP_UNARY_OP_INT_MAYBE: + return o_in; + default: + return MP_OBJ_NULL; // op not supported + } +} + +mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + long long lhs_val; + long long rhs_val; + + if (mp_obj_is_small_int(lhs_in)) { + lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs_in); + } else { + assert(mp_obj_is_exact_type(lhs_in, &mp_type_int)); + lhs_val = ((mp_obj_int_t *)lhs_in)->val; + } + + if (mp_obj_is_small_int(rhs_in)) { + rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs_in); + } else if (mp_obj_is_exact_type(rhs_in, &mp_type_int)) { + rhs_val = ((mp_obj_int_t *)rhs_in)->val; + } else { + // delegate to generic function to check for extra cases + return mp_obj_int_binary_op_extra_cases(op, lhs_in, rhs_in); + } + + switch (op) { + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_INPLACE_ADD: + return mp_obj_new_int_from_ll(lhs_val + rhs_val); + case MP_BINARY_OP_SUBTRACT: + case MP_BINARY_OP_INPLACE_SUBTRACT: + return mp_obj_new_int_from_ll(lhs_val - rhs_val); + case MP_BINARY_OP_MULTIPLY: + case MP_BINARY_OP_INPLACE_MULTIPLY: + return mp_obj_new_int_from_ll(lhs_val * rhs_val); + case MP_BINARY_OP_FLOOR_DIVIDE: + case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: + if (rhs_val == 0) { + goto zero_division; + } + return mp_obj_new_int_from_ll(lhs_val / rhs_val); + case MP_BINARY_OP_MODULO: + case MP_BINARY_OP_INPLACE_MODULO: + if (rhs_val == 0) { + goto zero_division; + } + return mp_obj_new_int_from_ll(lhs_val % rhs_val); + + case MP_BINARY_OP_AND: + case MP_BINARY_OP_INPLACE_AND: + return mp_obj_new_int_from_ll(lhs_val & rhs_val); + case MP_BINARY_OP_OR: + case MP_BINARY_OP_INPLACE_OR: + return mp_obj_new_int_from_ll(lhs_val | rhs_val); + case MP_BINARY_OP_XOR: + case MP_BINARY_OP_INPLACE_XOR: + return mp_obj_new_int_from_ll(lhs_val ^ rhs_val); + + case MP_BINARY_OP_LSHIFT: + case MP_BINARY_OP_INPLACE_LSHIFT: + return mp_obj_new_int_from_ll(lhs_val << (int)rhs_val); + case MP_BINARY_OP_RSHIFT: + case MP_BINARY_OP_INPLACE_RSHIFT: + return mp_obj_new_int_from_ll(lhs_val >> (int)rhs_val); + + case MP_BINARY_OP_POWER: + case MP_BINARY_OP_INPLACE_POWER: { + if (rhs_val < 0) { + #if MICROPY_PY_BUILTINS_FLOAT + return mp_obj_float_binary_op(op, lhs_val, rhs_in); + #else + mp_raise_ValueError(MP_ERROR_TEXT("negative power with no float support")); + #endif + } + long long ans = 1; + while (rhs_val > 0) { + if (rhs_val & 1) { + ans *= lhs_val; + } + if (rhs_val == 1) { + break; + } + rhs_val /= 2; + lhs_val *= lhs_val; + } + return mp_obj_new_int_from_ll(ans); + } + + case MP_BINARY_OP_LESS: + return mp_obj_new_bool(lhs_val < rhs_val); + case MP_BINARY_OP_MORE: + return mp_obj_new_bool(lhs_val > rhs_val); + case MP_BINARY_OP_LESS_EQUAL: + return mp_obj_new_bool(lhs_val <= rhs_val); + case MP_BINARY_OP_MORE_EQUAL: + return mp_obj_new_bool(lhs_val >= rhs_val); + case MP_BINARY_OP_EQUAL: + return mp_obj_new_bool(lhs_val == rhs_val); + + default: + return MP_OBJ_NULL; // op not supported + } + +zero_division: + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); +} + +mp_obj_t mp_obj_new_int(mp_int_t value) { + if (MP_SMALL_INT_FITS(value)) { + return MP_OBJ_NEW_SMALL_INT(value); + } + return mp_obj_new_int_from_ll(value); +} + +mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) { + // SMALL_INT accepts only signed numbers, so make sure the input + // value fits completely in the small-int positive range. + if ((value & ~MP_SMALL_INT_POSITIVE_MASK) == 0) { + return MP_OBJ_NEW_SMALL_INT(value); + } + return mp_obj_new_int_from_ll(value); +} + +mp_obj_t mp_obj_new_int_from_ll(long long val) { + mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); + o->val = val; + return o; +} + +mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { + // TODO raise an exception if the unsigned long long won't fit + if (val >> (sizeof(unsigned long long) * 8 - 1) != 0) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ulonglong too large")); + } + mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); + o->val = val; + return o; +} + +mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { + // TODO this does not honor the given length of the string, but it all cases it should anyway be null terminated + // TODO check overflow + mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); + char *endptr; + o->val = strtoll(*str, &endptr, base); + *str = endptr; + return o; +} + +mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + const mp_obj_int_t *self = self_in; + return self->val; + } +} + +mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { + // TODO: Check overflow + return mp_obj_int_get_truncated(self_in); +} + +#if MICROPY_PY_BUILTINS_FLOAT +mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); + mp_obj_int_t *self = self_in; + return self->val; +} +#endif + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objint_mpz.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objint_mpz.c new file mode 100644 index 00000000..600316a4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objint_mpz.c @@ -0,0 +1,460 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/parsenumbase.h" +#include "py/smallint.h" +#include "py/objint.h" +#include "py/runtime.h" + +#if MICROPY_PY_BUILTINS_FLOAT +#include +#endif + +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ + +#if MICROPY_PY_SYS_MAXSIZE +// Export value for sys.maxsize +// *FORMAT-OFF* +#define DIG_MASK ((MPZ_LONG_1 << MPZ_DIG_SIZE) - 1) +static const mpz_dig_t maxsize_dig[] = { + #define NUM_DIG 1 + (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 0) & DIG_MASK, + #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 0) > DIG_MASK + #undef NUM_DIG + #define NUM_DIG 2 + (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 1) & DIG_MASK, + #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 1) > DIG_MASK + #undef NUM_DIG + #define NUM_DIG 3 + (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 2) & DIG_MASK, + #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 2) > DIG_MASK + #undef NUM_DIG + #define NUM_DIG 4 + (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 3) & DIG_MASK, + #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 3) > DIG_MASK + #error cannot encode MP_SSIZE_MAX as mpz + #endif + #endif + #endif + #endif +}; +// *FORMAT-ON* +const mp_obj_int_t mp_sys_maxsize_obj = { + {&mp_type_int}, + {.fixed_dig = 1, .len = NUM_DIG, .alloc = NUM_DIG, .dig = (mpz_dig_t *)maxsize_dig} +}; +#undef DIG_MASK +#undef NUM_DIG +#endif + +mp_obj_int_t *mp_obj_int_new_mpz(void) { + mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); + mpz_init_zero(&o->mpz); + return o; +} + +// This routine expects you to pass in a buffer and size (in *buf and buf_size). +// If, for some reason, this buffer is too small, then it will allocate a +// buffer and return the allocated buffer and size in *buf and *buf_size. It +// is the callers responsibility to free this allocated buffer. +// +// The resulting formatted string will be returned from this function and the +// formatted size will be in *fmt_size. +// +// This particular routine should only be called for the mpz representation of the int. +char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in, + int base, const char *prefix, char base_char, char comma) { + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); + const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + + size_t needed_size = mp_int_format_size(mpz_max_num_bits(&self->mpz), base, prefix, comma); + if (needed_size > *buf_size) { + *buf = m_new(char, needed_size); + *buf_size = needed_size; + } + char *str = *buf; + + *fmt_size = mpz_as_str_inpl(&self->mpz, base, prefix, base_char, comma, str); + + return str; +} + +mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) { + mp_obj_int_t *o = mp_obj_int_new_mpz(); + mpz_set_from_bytes(&o->mpz, big_endian, len, buf); + return MP_OBJ_FROM_PTR(o); +} + +void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); + mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + mpz_as_bytes(&self->mpz, big_endian, len, buf); +} + +int mp_obj_int_sign(mp_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(self_in); + if (val < 0) { + return -1; + } else if (val > 0) { + return 1; + } else { + return 0; + } + } + mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + if (self->mpz.len == 0) { + return 0; + } else if (self->mpz.neg == 0) { + return 1; + } else { + return -1; + } +} + +mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + mp_obj_int_t *o = MP_OBJ_TO_PTR(o_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(!mpz_is_zero(&o->mpz)); + case MP_UNARY_OP_HASH: + return MP_OBJ_NEW_SMALL_INT(mpz_hash(&o->mpz)); + case MP_UNARY_OP_POSITIVE: + return o_in; + case MP_UNARY_OP_NEGATIVE: { mp_obj_int_t *o2 = mp_obj_int_new_mpz(); + mpz_neg_inpl(&o2->mpz, &o->mpz); + return MP_OBJ_FROM_PTR(o2); + } + case MP_UNARY_OP_INVERT: { mp_obj_int_t *o2 = mp_obj_int_new_mpz(); + mpz_not_inpl(&o2->mpz, &o->mpz); + return MP_OBJ_FROM_PTR(o2); + } + case MP_UNARY_OP_ABS: { + mp_obj_int_t *self = MP_OBJ_TO_PTR(o_in); + if (self->mpz.neg == 0) { + return o_in; + } + mp_obj_int_t *self2 = mp_obj_int_new_mpz(); + mpz_abs_inpl(&self2->mpz, &self->mpz); + return MP_OBJ_FROM_PTR(self2); + } + case MP_UNARY_OP_INT_MAYBE: + return o_in; + default: + return MP_OBJ_NULL; // op not supported + } +} + +mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + const mpz_t *zlhs; + const mpz_t *zrhs; + mpz_t z_int; + mpz_dig_t z_int_dig[MPZ_NUM_DIG_FOR_INT]; + + // lhs could be a small int (eg small-int + mpz) + if (mp_obj_is_small_int(lhs_in)) { + mpz_init_fixed_from_int(&z_int, z_int_dig, MPZ_NUM_DIG_FOR_INT, MP_OBJ_SMALL_INT_VALUE(lhs_in)); + zlhs = &z_int; + } else { + assert(mp_obj_is_exact_type(lhs_in, &mp_type_int)); + zlhs = &((mp_obj_int_t *)MP_OBJ_TO_PTR(lhs_in))->mpz; + } + + // if rhs is small int, then lhs was not (otherwise mp_binary_op handles it) + if (mp_obj_is_small_int(rhs_in)) { + mpz_init_fixed_from_int(&z_int, z_int_dig, MPZ_NUM_DIG_FOR_INT, MP_OBJ_SMALL_INT_VALUE(rhs_in)); + zrhs = &z_int; + } else if (mp_obj_is_exact_type(rhs_in, &mp_type_int)) { + zrhs = &((mp_obj_int_t *)MP_OBJ_TO_PTR(rhs_in))->mpz; + #if MICROPY_PY_BUILTINS_FLOAT + } else if (mp_obj_is_float(rhs_in)) { + return mp_obj_float_binary_op(op, mpz_as_float(zlhs), rhs_in); + #endif + #if MICROPY_PY_BUILTINS_COMPLEX + } else if (mp_obj_is_type(rhs_in, &mp_type_complex)) { + return mp_obj_complex_binary_op(op, mpz_as_float(zlhs), 0, rhs_in); + #endif + } else { + // delegate to generic function to check for extra cases + return mp_obj_int_binary_op_extra_cases(op, lhs_in, rhs_in); + } + + #if MICROPY_PY_BUILTINS_FLOAT + if (op == MP_BINARY_OP_TRUE_DIVIDE || op == MP_BINARY_OP_INPLACE_TRUE_DIVIDE) { + if (mpz_is_zero(zrhs)) { + goto zero_division_error; + } + mp_float_t flhs = mpz_as_float(zlhs); + mp_float_t frhs = mpz_as_float(zrhs); + return mp_obj_new_float(flhs / frhs); + } + #endif + + if (op >= MP_BINARY_OP_INPLACE_OR && op < MP_BINARY_OP_CONTAINS) { + mp_obj_int_t *res = mp_obj_int_new_mpz(); + + switch (op) { + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_INPLACE_ADD: + mpz_add_inpl(&res->mpz, zlhs, zrhs); + break; + case MP_BINARY_OP_SUBTRACT: + case MP_BINARY_OP_INPLACE_SUBTRACT: + mpz_sub_inpl(&res->mpz, zlhs, zrhs); + break; + case MP_BINARY_OP_MULTIPLY: + case MP_BINARY_OP_INPLACE_MULTIPLY: + mpz_mul_inpl(&res->mpz, zlhs, zrhs); + break; + case MP_BINARY_OP_FLOOR_DIVIDE: + case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: { + if (mpz_is_zero(zrhs)) { + zero_division_error: + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); + } + mpz_t rem; + mpz_init_zero(&rem); + mpz_divmod_inpl(&res->mpz, &rem, zlhs, zrhs); + mpz_deinit(&rem); + break; + } + case MP_BINARY_OP_MODULO: + case MP_BINARY_OP_INPLACE_MODULO: { + if (mpz_is_zero(zrhs)) { + goto zero_division_error; + } + mpz_t quo; + mpz_init_zero(&quo); + mpz_divmod_inpl(&quo, &res->mpz, zlhs, zrhs); + mpz_deinit(&quo); + break; + } + + case MP_BINARY_OP_AND: + case MP_BINARY_OP_INPLACE_AND: + mpz_and_inpl(&res->mpz, zlhs, zrhs); + break; + case MP_BINARY_OP_OR: + case MP_BINARY_OP_INPLACE_OR: + mpz_or_inpl(&res->mpz, zlhs, zrhs); + break; + case MP_BINARY_OP_XOR: + case MP_BINARY_OP_INPLACE_XOR: + mpz_xor_inpl(&res->mpz, zlhs, zrhs); + break; + + case MP_BINARY_OP_LSHIFT: + case MP_BINARY_OP_INPLACE_LSHIFT: + case MP_BINARY_OP_RSHIFT: + case MP_BINARY_OP_INPLACE_RSHIFT: { + mp_int_t irhs = mp_obj_int_get_checked(rhs_in); + if (irhs < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); + } + if (op == MP_BINARY_OP_LSHIFT || op == MP_BINARY_OP_INPLACE_LSHIFT) { + mpz_shl_inpl(&res->mpz, zlhs, irhs); + } else { + mpz_shr_inpl(&res->mpz, zlhs, irhs); + } + break; + } + + case MP_BINARY_OP_POWER: + case MP_BINARY_OP_INPLACE_POWER: + if (mpz_is_neg(zrhs)) { + #if MICROPY_PY_BUILTINS_FLOAT + return mp_obj_float_binary_op(op, mpz_as_float(zlhs), rhs_in); + #else + mp_raise_ValueError(MP_ERROR_TEXT("negative power with no float support")); + #endif + } + mpz_pow_inpl(&res->mpz, zlhs, zrhs); + break; + + case MP_BINARY_OP_DIVMOD: { + if (mpz_is_zero(zrhs)) { + goto zero_division_error; + } + mp_obj_int_t *quo = mp_obj_int_new_mpz(); + mpz_divmod_inpl(&quo->mpz, &res->mpz, zlhs, zrhs); + mp_obj_t tuple[2] = {MP_OBJ_FROM_PTR(quo), MP_OBJ_FROM_PTR(res)}; + return mp_obj_new_tuple(2, tuple); + } + + default: + return MP_OBJ_NULL; // op not supported + } + + return MP_OBJ_FROM_PTR(res); + + } else { + int cmp = mpz_cmp(zlhs, zrhs); + switch (op) { + case MP_BINARY_OP_LESS: + return mp_obj_new_bool(cmp < 0); + case MP_BINARY_OP_MORE: + return mp_obj_new_bool(cmp > 0); + case MP_BINARY_OP_LESS_EQUAL: + return mp_obj_new_bool(cmp <= 0); + case MP_BINARY_OP_MORE_EQUAL: + return mp_obj_new_bool(cmp >= 0); + case MP_BINARY_OP_EQUAL: + return mp_obj_new_bool(cmp == 0); + + default: + return MP_OBJ_NULL; // op not supported + } + } +} + +#if MICROPY_PY_BUILTINS_POW3 +static mpz_t *mp_mpz_for_int(mp_obj_t arg, mpz_t *temp) { + if (mp_obj_is_small_int(arg)) { + mpz_init_from_int(temp, MP_OBJ_SMALL_INT_VALUE(arg)); + return temp; + } else { + mp_obj_int_t *arp_p = MP_OBJ_TO_PTR(arg); + return &(arp_p->mpz); + } +} + +mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { + if (!mp_obj_is_int(base) || !mp_obj_is_int(exponent) || !mp_obj_is_int(modulus)) { + mp_raise_TypeError(MP_ERROR_TEXT("pow() with 3 arguments requires integers")); + } else { + mp_obj_t result = mp_obj_new_int_from_ull(0); // Use the _from_ull version as this forces an mpz int + mp_obj_int_t *res_p = (mp_obj_int_t *)MP_OBJ_TO_PTR(result); + + mpz_t l_temp, r_temp, m_temp; + mpz_t *lhs = mp_mpz_for_int(base, &l_temp); + mpz_t *rhs = mp_mpz_for_int(exponent, &r_temp); + mpz_t *mod = mp_mpz_for_int(modulus, &m_temp); + + mpz_pow3_inpl(&(res_p->mpz), lhs, rhs, mod); + + if (lhs == &l_temp) { + mpz_deinit(lhs); + } + if (rhs == &r_temp) { + mpz_deinit(rhs); + } + if (mod == &m_temp) { + mpz_deinit(mod); + } + return result; + } +} +#endif + +mp_obj_t mp_obj_new_int(mp_int_t value) { + if (MP_SMALL_INT_FITS(value)) { + return MP_OBJ_NEW_SMALL_INT(value); + } + return mp_obj_new_int_from_ll(value); +} + +mp_obj_t mp_obj_new_int_from_ll(long long val) { + mp_obj_int_t *o = mp_obj_int_new_mpz(); + mpz_set_from_ll(&o->mpz, val, true); + return MP_OBJ_FROM_PTR(o); +} + +mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { + mp_obj_int_t *o = mp_obj_int_new_mpz(); + mpz_set_from_ll(&o->mpz, val, false); + return MP_OBJ_FROM_PTR(o); +} + +mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) { + // SMALL_INT accepts only signed numbers, so make sure the input + // value fits completely in the small-int positive range. + if ((value & ~MP_SMALL_INT_POSITIVE_MASK) == 0) { + return MP_OBJ_NEW_SMALL_INT(value); + } + return mp_obj_new_int_from_ull(value); +} + +mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { + mp_obj_int_t *o = mp_obj_int_new_mpz(); + size_t n = mpz_set_from_str(&o->mpz, *str, len, neg, base); + *str += n; + return MP_OBJ_FROM_PTR(o); +} + +mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + // hash returns actual int value if it fits in mp_int_t + return mpz_hash(&self->mpz); + } +} + +mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t value; + if (mpz_as_int_checked(&self->mpz, &value)) { + return value; + } else { + // overflow + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("overflow converting long int to machine word")); + } + } +} + +mp_uint_t mp_obj_int_get_uint_checked(mp_const_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + if (MP_OBJ_SMALL_INT_VALUE(self_in) >= 0) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } + } else { + const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t value; + if (mpz_as_uint_checked(&self->mpz, &value)) { + return value; + } + } + + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("overflow converting long int to machine word")); +} + +#if MICROPY_PY_BUILTINS_FLOAT +mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); + mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + return mpz_as_float(&self->mpz); +} +#endif + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objlist.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objlist.c new file mode 100644 index 00000000..2198beb8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objlist.c @@ -0,0 +1,542 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/objlist.h" +#include "py/runtime.h" +#include "py/stackctrl.h" + +static mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur, mp_obj_iter_buf_t *iter_buf); +static mp_obj_list_t *list_new(size_t n); +static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in); +static mp_obj_t list_pop(size_t n_args, const mp_obj_t *args); + +// TODO: Move to mpconfig.h +#define LIST_MIN_ALLOC 4 + +/******************************************************************************/ +/* list */ + +static void list_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + mp_obj_list_t *o = MP_OBJ_TO_PTR(o_in); + const char *item_separator = ", "; + if (!(MICROPY_PY_JSON && kind == PRINT_JSON)) { + kind = PRINT_REPR; + } else { + #if MICROPY_PY_JSON_SEPARATORS + item_separator = MP_PRINT_GET_EXT(print)->item_separator; + #endif + } + mp_print_str(print, "["); + for (size_t i = 0; i < o->len; i++) { + if (i > 0) { + mp_print_str(print, item_separator); + } + mp_obj_print_helper(print, o->items[i], kind); + } + mp_print_str(print, "]"); +} + +static mp_obj_t list_extend_from_iter(mp_obj_t list, mp_obj_t iterable) { + mp_obj_t iter = mp_getiter(iterable, NULL); + mp_obj_t item; + while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + mp_obj_list_append(list, item); + } + return list; +} + +mp_obj_t mp_obj_list_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + switch (n_args) { + case 0: + // return a new, empty list + return mp_obj_new_list(0, NULL); + + case 1: + default: { + // make list from iterable + // TODO: optimize list/tuple + mp_obj_t list = mp_obj_new_list(0, NULL); + return list_extend_from_iter(list, args[0]); + } + } +} + +static mp_obj_t list_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(self->len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(self->len); + #if MICROPY_PY_SYS_GETSIZEOF + case MP_UNARY_OP_SIZEOF: { + size_t sz = sizeof(*self) + sizeof(mp_obj_t) * self->alloc; + return MP_OBJ_NEW_SMALL_INT(sz); + } + #endif + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { + mp_obj_list_t *o = MP_OBJ_TO_PTR(lhs); + switch (op) { + case MP_BINARY_OP_ADD: { + if (!mp_obj_is_type(rhs, &mp_type_list)) { + return MP_OBJ_NULL; // op not supported + } + mp_obj_list_t *p = MP_OBJ_TO_PTR(rhs); + mp_obj_list_t *s = list_new(o->len + p->len); + mp_seq_cat(s->items, o->items, o->len, p->items, p->len, mp_obj_t); + return MP_OBJ_FROM_PTR(s); + } + case MP_BINARY_OP_INPLACE_ADD: { + list_extend(lhs, rhs); + return lhs; + } + case MP_BINARY_OP_MULTIPLY: { + mp_int_t n; + if (!mp_obj_get_int_maybe(rhs, &n)) { + return MP_OBJ_NULL; // op not supported + } + if (n < 0) { + n = 0; + } + mp_obj_list_t *s = list_new(o->len * n); + mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); + return MP_OBJ_FROM_PTR(s); + } + case MP_BINARY_OP_EQUAL: + case MP_BINARY_OP_LESS: + case MP_BINARY_OP_LESS_EQUAL: + case MP_BINARY_OP_MORE: + case MP_BINARY_OP_MORE_EQUAL: { + if (!mp_obj_is_type(rhs, &mp_type_list)) { + if (op == MP_BINARY_OP_EQUAL) { + return mp_const_false; + } + return MP_OBJ_NULL; // op not supported + } + + mp_obj_list_t *another = MP_OBJ_TO_PTR(rhs); + bool res = mp_seq_cmp_objs(op, o->items, o->len, another->items, another->len); + return mp_obj_new_bool(res); + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { + mp_raise_NotImplementedError(NULL); + } + + mp_int_t len_adj = slice.start - slice.stop; + assert(len_adj <= 0); + mp_seq_replace_slice_no_grow(self->items, self->len, slice.start, slice.stop, self->items /*NULL*/, 0, sizeof(*self->items)); + // Clear "freed" elements at the end of list + mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); + self->len += len_adj; + return mp_const_none; + } + #endif + mp_obj_t args[2] = {self_in, index}; + list_pop(2, args); + return mp_const_none; + } else if (value == MP_OBJ_SENTINEL) { + // load + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { + return mp_seq_extract_slice(self->len, self->items, &slice); + } + mp_obj_list_t *res = list_new(slice.stop - slice.start); + mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t); + return MP_OBJ_FROM_PTR(res); + } + #endif + size_t index_val = mp_get_index(self->base.type, self->len, index, false); + return self->items[index_val]; + } else { + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + size_t value_len; + mp_obj_t *value_items; + mp_obj_get_array(value, &value_len, &value_items); + mp_bound_slice_t slice_out; + if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice_out)) { + mp_raise_NotImplementedError(NULL); + } + mp_int_t len_adj = value_len - (slice_out.stop - slice_out.start); + if (len_adj > 0) { + if (self->len + len_adj > self->alloc) { + // TODO: Might optimize memory copies here by checking if block can + // be grown inplace or not + self->items = m_renew(mp_obj_t, self->items, self->alloc, self->len + len_adj); + self->alloc = self->len + len_adj; + } + mp_seq_replace_slice_grow_inplace(self->items, self->len, + slice_out.start, slice_out.stop, value_items, value_len, len_adj, sizeof(*self->items)); + } else { + mp_seq_replace_slice_no_grow(self->items, self->len, + slice_out.start, slice_out.stop, value_items, value_len, sizeof(*self->items)); + // Clear "freed" elements at the end of list + mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); + // TODO: apply allocation policy re: alloc_size + } + self->len += len_adj; + return mp_const_none; + } + #endif + mp_obj_list_store(self_in, index, value); + return mp_const_none; + } +} + +static mp_obj_t list_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { + return mp_obj_new_list_iterator(o_in, 0, iter_buf); +} + +mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + if (self->len >= self->alloc) { + self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2); + self->alloc *= 2; + mp_seq_clear(self->items, self->len + 1, self->alloc, sizeof(*self->items)); + } + self->items[self->len++] = arg; + return mp_const_none; // return None, as per CPython +} + +static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + if (mp_obj_is_type(arg_in, &mp_type_list)) { + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *arg = MP_OBJ_TO_PTR(arg_in); + + if (self->len + arg->len > self->alloc) { + // TODO: use alloc policy for "4" + self->items = m_renew(mp_obj_t, self->items, self->alloc, self->len + arg->len + 4); + self->alloc = self->len + arg->len + 4; + mp_seq_clear(self->items, self->len + arg->len, self->alloc, sizeof(*self->items)); + } + + memcpy(self->items + self->len, arg->items, sizeof(mp_obj_t) * arg->len); + self->len += arg->len; + } else { + list_extend_from_iter(self_in, arg_in); + } + return mp_const_none; // return None, as per CPython +} + +static mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { + mp_check_self(mp_obj_is_type(args[0], &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(args[0]); + if (self->len == 0) { + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("pop from empty list")); + } + size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); + mp_obj_t ret = self->items[index]; + self->len -= 1; + memmove(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t)); + // Clear stale pointer from slot which just got freed to prevent GC issues + self->items[self->len] = MP_OBJ_NULL; + if (self->alloc > LIST_MIN_ALLOC && self->alloc > 2 * self->len) { + self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc / 2); + self->alloc /= 2; + } + return ret; +} + +static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, mp_obj_t binop_less_result) { + MP_STACK_CHECK(); + while (head < tail) { + mp_obj_t *h = head - 1; + mp_obj_t *t = tail; + mp_obj_t v = key_fn == MP_OBJ_NULL ? tail[0] : mp_call_function_1(key_fn, tail[0]); // get pivot using key_fn + for (;;) { + do {++h; + } while (h < t && mp_binary_op(MP_BINARY_OP_LESS, key_fn == MP_OBJ_NULL ? h[0] : mp_call_function_1(key_fn, h[0]), v) == binop_less_result); + do {--t; + } while (h < t && mp_binary_op(MP_BINARY_OP_LESS, v, key_fn == MP_OBJ_NULL ? t[0] : mp_call_function_1(key_fn, t[0])) == binop_less_result); + if (h >= t) { + break; + } + mp_obj_t x = h[0]; + h[0] = t[0]; + t[0] = x; + } + mp_obj_t x = h[0]; + h[0] = tail[0]; + tail[0] = x; + // do the smaller recursive call first, to keep stack within O(log(N)) + if (t - head < tail - h - 1) { + mp_quicksort(head, t, key_fn, binop_less_result); + head = h + 1; + } else { + mp_quicksort(h + 1, tail, key_fn, binop_less_result); + tail = t; + } + } +} + +// TODO Python defines sort to be stable but ours is not +mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_reverse, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + // parse args + struct { + mp_arg_val_t key, reverse; + } args; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); + + mp_check_self(mp_obj_is_type(pos_args[0], &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + if (self->len > 1) { + mp_quicksort(self->items, self->items + self->len - 1, + args.key.u_obj == mp_const_none ? MP_OBJ_NULL : args.key.u_obj, + args.reverse.u_bool ? mp_const_false : mp_const_true); + } + + return mp_const_none; +} + +static mp_obj_t list_clear(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + self->len = 0; + self->items = m_renew(mp_obj_t, self->items, self->alloc, LIST_MIN_ALLOC); + self->alloc = LIST_MIN_ALLOC; + mp_seq_clear(self->items, 0, self->alloc, sizeof(*self->items)); + return mp_const_none; +} + +static mp_obj_t list_copy(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_list(self->len, self->items); +} + +static mp_obj_t list_count(mp_obj_t self_in, mp_obj_t value) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + return mp_seq_count_obj(self->items, self->len, value); +} + +static mp_obj_t list_index(size_t n_args, const mp_obj_t *args) { + mp_check_self(mp_obj_is_type(args[0], &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(args[0]); + return mp_seq_index_obj(self->items, self->len, n_args, args); +} + +static mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + // insert has its own strange index logic + mp_int_t index = MP_OBJ_SMALL_INT_VALUE(idx); + if (index < 0) { + index += self->len; + } + if (index < 0) { + index = 0; + } + if ((size_t)index > self->len) { + index = self->len; + } + + mp_obj_list_append(self_in, mp_const_none); + + for (mp_int_t i = self->len - 1; i > index; i--) { + self->items[i] = self->items[i - 1]; + } + self->items[index] = obj; + + return mp_const_none; +} + +mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + mp_obj_t args[] = {self_in, value}; + args[1] = list_index(2, args); + list_pop(2, args); + + return mp_const_none; +} + +static mp_obj_t list_reverse(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t len = self->len; + for (mp_int_t i = 0; i < len / 2; i++) { + mp_obj_t a = self->items[i]; + self->items[i] = self->items[len - i - 1]; + self->items[len - i - 1] = a; + } + + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_2(list_append_obj, mp_obj_list_append); +static MP_DEFINE_CONST_FUN_OBJ_2(list_extend_obj, list_extend); +static MP_DEFINE_CONST_FUN_OBJ_1(list_clear_obj, list_clear); +static MP_DEFINE_CONST_FUN_OBJ_1(list_copy_obj, list_copy); +static MP_DEFINE_CONST_FUN_OBJ_2(list_count_obj, list_count); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(list_index_obj, 2, 4, list_index); +static MP_DEFINE_CONST_FUN_OBJ_3(list_insert_obj, list_insert); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(list_pop_obj, 1, 2, list_pop); +static MP_DEFINE_CONST_FUN_OBJ_2(list_remove_obj, mp_obj_list_remove); +static MP_DEFINE_CONST_FUN_OBJ_1(list_reverse_obj, list_reverse); +static MP_DEFINE_CONST_FUN_OBJ_KW(list_sort_obj, 1, mp_obj_list_sort); + +static const mp_rom_map_elem_t list_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&list_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&list_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&list_copy_obj) }, + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&list_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&list_extend_obj) }, + { MP_ROM_QSTR(MP_QSTR_index), MP_ROM_PTR(&list_index_obj) }, + { MP_ROM_QSTR(MP_QSTR_insert), MP_ROM_PTR(&list_insert_obj) }, + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&list_pop_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&list_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_reverse), MP_ROM_PTR(&list_reverse_obj) }, + { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&list_sort_obj) }, +}; + +static MP_DEFINE_CONST_DICT(list_locals_dict, list_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_list, + MP_QSTR_list, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_list_make_new, + print, list_print, + unary_op, list_unary_op, + binary_op, list_binary_op, + subscr, list_subscr, + iter, list_getiter, + locals_dict, &list_locals_dict + ); + + +void mp_obj_list_init(mp_obj_list_t *o, size_t n) { + o->base.type = &mp_type_list; + o->alloc = n < LIST_MIN_ALLOC ? LIST_MIN_ALLOC : n; + o->len = n; + o->items = m_new(mp_obj_t, o->alloc); + mp_seq_clear(o->items, n, o->alloc, sizeof(*o->items)); +} + +static mp_obj_list_t *list_new(size_t n) { + mp_obj_list_t *o = m_new_obj(mp_obj_list_t); + mp_obj_list_init(o, n); + return o; +} + +mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items) { + mp_obj_list_t *o = list_new(n); + if (items != NULL) { + for (size_t i = 0; i < n; i++) { + o->items[i] = items[i]; + } + } + return MP_OBJ_FROM_PTR(o); +} + +void mp_obj_list_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + *len = self->len; + *items = self->items; +} + +void mp_obj_list_set_len(mp_obj_t self_in, size_t len) { + // trust that the caller knows what it's doing + // TODO realloc if len got much smaller than alloc + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + self->len = len; +} + +void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + size_t i = mp_get_index(self->base.type, self->len, index, false); + self->items[i] = value; +} + +/******************************************************************************/ +/* list iterator */ + +typedef struct _mp_obj_list_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_t list; + size_t cur; +} mp_obj_list_it_t; + +static mp_obj_t list_it_iternext(mp_obj_t self_in) { + mp_obj_list_it_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *list = MP_OBJ_TO_PTR(self->list); + if (self->cur < list->len) { + mp_obj_t o_out = list->items[self->cur]; + self->cur += 1; + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_list_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_list_it_t *o = (mp_obj_list_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = list_it_iternext; + o->list = list; + o->cur = cur; + return MP_OBJ_FROM_PTR(o); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objlist.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objlist.h new file mode 100644 index 00000000..a3bba680 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objlist.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJLIST_H +#define MICROPY_INCLUDED_PY_OBJLIST_H + +#include "py/obj.h" + +typedef struct _mp_obj_list_t { + mp_obj_base_t base; + size_t alloc; + size_t len; + mp_obj_t *items; +} mp_obj_list_t; + +void mp_obj_list_init(mp_obj_list_t *o, size_t n); +mp_obj_t mp_obj_list_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); + +#endif // MICROPY_INCLUDED_PY_OBJLIST_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objmap.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objmap.c new file mode 100644 index 00000000..1911a751 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objmap.c @@ -0,0 +1,72 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +typedef struct _mp_obj_map_t { + mp_obj_base_t base; + size_t n_iters; + mp_obj_t fun; + mp_obj_t iters[]; +} mp_obj_map_t; + +static mp_obj_t map_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 2, MP_OBJ_FUN_ARGS_MAX, false); + mp_obj_map_t *o = mp_obj_malloc_var(mp_obj_map_t, iters, mp_obj_t, n_args - 1, type); + o->n_iters = n_args - 1; + o->fun = args[0]; + for (size_t i = 0; i < n_args - 1; i++) { + o->iters[i] = mp_getiter(args[i + 1], NULL); + } + return MP_OBJ_FROM_PTR(o); +} + +static mp_obj_t map_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_map)); + mp_obj_map_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t *nextses = m_new(mp_obj_t, self->n_iters); + + for (size_t i = 0; i < self->n_iters; i++) { + mp_obj_t next = mp_iternext(self->iters[i]); + if (next == MP_OBJ_STOP_ITERATION) { + m_del(mp_obj_t, nextses, self->n_iters); + return MP_OBJ_STOP_ITERATION; + } + nextses[i] = next; + } + return mp_call_function_n_kw(self->fun, self->n_iters, 0, nextses); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_map, + MP_QSTR_map, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, map_make_new, + iter, map_iternext + ); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objmodule.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objmodule.c new file mode 100644 index 00000000..5ce373b8 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objmodule.c @@ -0,0 +1,257 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * Copyright (c) 2014-2015 Paul Sokolovsky + * + * 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 "py/bc.h" +#include "py/objmodule.h" +#include "py/runtime.h" +#include "py/builtin.h" + +static void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); + + const char *module_name = ""; + mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_MAP_LOOKUP); + if (elem != NULL) { + module_name = mp_obj_str_get_str(elem->value); + } + + #if MICROPY_PY___FILE__ + // If we store __file__ to imported modules then try to lookup this + // symbol to give more information about the module. + elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___file__), MP_MAP_LOOKUP); + if (elem != NULL) { + mp_printf(print, "", module_name, mp_obj_str_get_str(elem->value)); + return; + } + #endif + + mp_printf(print, "", module_name); +} + +static void module_attr_try_delegation(mp_obj_t self_in, qstr attr, mp_obj_t *dest); + +static void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); + if (dest[0] == MP_OBJ_NULL) { + // load attribute + mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + if (elem != NULL) { + dest[0] = elem->value; + #if MICROPY_CPYTHON_COMPAT + } else if (attr == MP_QSTR___dict__) { + dest[0] = MP_OBJ_FROM_PTR(self->globals); + #endif + #if MICROPY_MODULE_GETATTR + } else if (attr != MP_QSTR___getattr__) { + elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___getattr__), MP_MAP_LOOKUP); + if (elem != NULL) { + dest[0] = mp_call_function_1(elem->value, MP_OBJ_NEW_QSTR(attr)); + } else { + module_attr_try_delegation(self_in, attr, dest); + } + #endif + } else { + module_attr_try_delegation(self_in, attr, dest); + } + } else { + // delete/store attribute + mp_obj_dict_t *dict = self->globals; + if (dict->map.is_fixed) { + #if MICROPY_CAN_OVERRIDE_BUILTINS + if (dict == &mp_module_builtins_globals) { + if (MP_STATE_VM(mp_module_builtins_override_dict) == NULL) { + MP_STATE_VM(mp_module_builtins_override_dict) = MP_OBJ_TO_PTR(mp_obj_new_dict(1)); + } + dict = MP_STATE_VM(mp_module_builtins_override_dict); + } else + #endif + { + // can't delete or store to fixed map + module_attr_try_delegation(self_in, attr, dest); + return; + } + } + if (dest[1] == MP_OBJ_NULL) { + // delete attribute + mp_obj_dict_delete(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(attr)); + } else { + // store attribute + mp_obj_dict_store(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(attr), dest[1]); + } + dest[0] = MP_OBJ_NULL; // indicate success + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_module, + MP_QSTR_module, + MP_TYPE_FLAG_NONE, + print, module_print, + attr, module_attr + ); + +mp_obj_t mp_obj_new_module(qstr module_name) { + mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; + mp_map_elem_t *el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + // We could error out if module already exists, but let C extensions + // add new members to existing modules. + if (el->value != MP_OBJ_NULL) { + return el->value; + } + + // create new module object + mp_module_context_t *o = m_new_obj(mp_module_context_t); + o->module.base.type = &mp_type_module; + o->module.globals = MP_OBJ_TO_PTR(mp_obj_new_dict(MICROPY_MODULE_DICT_SIZE)); + + // store __name__ entry in the module + mp_obj_dict_store(MP_OBJ_FROM_PTR(o->module.globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(module_name)); + + // store the new module into the slot in the global dict holding all modules + el->value = MP_OBJ_FROM_PTR(o); + + // return the new module + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +// Global module table and related functions + +static const mp_rom_map_elem_t mp_builtin_module_table[] = { + // built-in modules declared with MP_REGISTER_MODULE() + MICROPY_REGISTERED_MODULES +}; +MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table); + +static const mp_rom_map_elem_t mp_builtin_extensible_module_table[] = { + // built-in modules declared with MP_REGISTER_EXTENSIBLE_MODULE() + MICROPY_REGISTERED_EXTENSIBLE_MODULES +}; +MP_DEFINE_CONST_MAP(mp_builtin_extensible_module_map, mp_builtin_extensible_module_table); + +#if MICROPY_MODULE_ATTR_DELEGATION && defined(MICROPY_MODULE_DELEGATIONS) +typedef struct _mp_module_delegation_entry_t { + mp_rom_obj_t mod; + mp_attr_fun_t fun; +} mp_module_delegation_entry_t; + +static const mp_module_delegation_entry_t mp_builtin_module_delegation_table[] = { + // delegation entries declared with MP_REGISTER_MODULE_DELEGATION() + MICROPY_MODULE_DELEGATIONS +}; +#endif + +// Attempts to find (and initialise) a built-in, otherwise returns +// MP_OBJ_NULL. +mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { + mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)(extensible ? &mp_builtin_extensible_module_map : &mp_builtin_module_map), MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); + if (!elem) { + #if MICROPY_PY_SYS + // Special case for sys, which isn't extensible but can always be + // imported with the alias `usys`. + if (module_name == MP_QSTR_usys) { + return MP_OBJ_FROM_PTR(&mp_module_sys); + } + #endif + + if (extensible) { + // At this point we've already tried non-extensible built-ins, the + // filesystem, and now extensible built-ins. No match, so fail + // the import. + return MP_OBJ_NULL; + } + + // We're trying to match a non-extensible built-in (i.e. before trying + // the filesystem), but if the user is importing `ufoo`, _and_ `foo` + // is an extensible module, then allow it as a way of forcing the + // built-in. Essentially, this makes it as if all the extensible + // built-ins also had non-extensible aliases named `ufoo`. Newer code + // should be using sys.path to force the built-in, but this retains + // the old behaviour of the u-prefix being used to force a built-in + // import. + size_t module_name_len; + const char *module_name_str = (const char *)qstr_data(module_name, &module_name_len); + if (module_name_str[0] != 'u') { + return MP_OBJ_NULL; + } + elem = mp_map_lookup((mp_map_t *)&mp_builtin_extensible_module_map, MP_OBJ_NEW_QSTR(qstr_from_strn(module_name_str + 1, module_name_len - 1)), MP_MAP_LOOKUP); + if (!elem) { + return MP_OBJ_NULL; + } + } + + #if MICROPY_MODULE_BUILTIN_INIT + // If found, it's a newly loaded built-in, so init it. This can run + // multiple times, so the module must ensure that it handles being + // initialised multiple times. + mp_obj_t dest[2]; + mp_load_method_maybe(elem->value, MP_QSTR___init__, dest); + if (dest[0] != MP_OBJ_NULL) { + mp_call_method_n_kw(0, 0, dest); + } + #endif + + return elem->value; +} + +static void module_attr_try_delegation(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + #if MICROPY_MODULE_ATTR_DELEGATION && defined(MICROPY_MODULE_DELEGATIONS) + // Delegate lookup to a module's custom attr method. + size_t n = MP_ARRAY_SIZE(mp_builtin_module_delegation_table); + for (size_t i = 0; i < n; ++i) { + if (*(mp_obj_t *)(&mp_builtin_module_delegation_table[i].mod) == self_in) { + mp_builtin_module_delegation_table[i].fun(self_in, attr, dest); + break; + } + } + #else + (void)self_in; + (void)attr; + (void)dest; + #endif +} + +void mp_module_generic_attr(qstr attr, mp_obj_t *dest, const uint16_t *keys, mp_obj_t *values) { + for (size_t i = 0; keys[i] != MP_QSTRnull; ++i) { + if (attr == keys[i]) { + if (dest[0] == MP_OBJ_NULL) { + // load attribute (MP_OBJ_NULL returned for deleted items) + dest[0] = values[i]; + } else { + // delete or store (delete stores MP_OBJ_NULL) + values[i] = dest[1]; + dest[0] = MP_OBJ_NULL; // indicate success + } + return; + } + } +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objmodule.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objmodule.h new file mode 100644 index 00000000..8b14cd9f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objmodule.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJMODULE_H +#define MICROPY_INCLUDED_PY_OBJMODULE_H + +#include "py/obj.h" + +#ifndef NO_QSTR +// Only include module definitions when not doing qstr extraction, because the +// qstr extraction stage also generates this module definition header file. +#include "genhdr/moduledefs.h" +#endif + +extern const mp_map_t mp_builtin_module_map; +extern const mp_map_t mp_builtin_extensible_module_map; + +mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible); + +void mp_module_generic_attr(qstr attr, mp_obj_t *dest, const uint16_t *keys, mp_obj_t *values); + +#endif // MICROPY_INCLUDED_PY_OBJMODULE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objnamedtuple.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objnamedtuple.c new file mode 100644 index 00000000..f019604d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objnamedtuple.c @@ -0,0 +1,185 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/objtuple.h" +#include "py/runtime.h" +#include "py/objstr.h" +#include "py/objnamedtuple.h" + +#if MICROPY_PY_COLLECTIONS + +size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name) { + for (size_t i = 0; i < type->n_fields; i++) { + if (type->fields[i] == name) { + return i; + } + } + return (size_t)-1; +} + +#if MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT +static mp_obj_t namedtuple_asdict(mp_obj_t self_in) { + mp_obj_namedtuple_t *self = MP_OBJ_TO_PTR(self_in); + const qstr *fields = ((mp_obj_namedtuple_type_t *)self->tuple.base.type)->fields; + mp_obj_t dict = mp_obj_new_dict(self->tuple.len); + // make it an OrderedDict + mp_obj_dict_t *dictObj = MP_OBJ_TO_PTR(dict); + dictObj->base.type = &mp_type_ordereddict; + dictObj->map.is_ordered = 1; + for (size_t i = 0; i < self->tuple.len; ++i) { + mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(fields[i]), self->tuple.items[i]); + } + return dict; +} +MP_DEFINE_CONST_FUN_OBJ_1(namedtuple_asdict_obj, namedtuple_asdict); +#endif + +static void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_namedtuple_t *o = MP_OBJ_TO_PTR(o_in); + mp_printf(print, "%q", o->tuple.base.type->name); + const qstr *fields = ((mp_obj_namedtuple_type_t *)o->tuple.base.type)->fields; + mp_obj_attrtuple_print_helper(print, fields, &o->tuple); +} + +static void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] == MP_OBJ_NULL) { + // load attribute + mp_obj_namedtuple_t *self = MP_OBJ_TO_PTR(self_in); + #if MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT + if (attr == MP_QSTR__asdict) { + dest[0] = MP_OBJ_FROM_PTR(&namedtuple_asdict_obj); + dest[1] = self_in; + return; + } + #endif + size_t id = mp_obj_namedtuple_find_field((mp_obj_namedtuple_type_t *)self->tuple.base.type, attr); + if (id == (size_t)-1) { + return; + } + dest[0] = self->tuple.items[id]; + } else { + // delete/store attribute + // provide more detailed error message than we'd get by just returning + mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("can't set attribute")); + } +} + +static mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + const mp_obj_namedtuple_type_t *type = (const mp_obj_namedtuple_type_t *)type_in; + size_t num_fields = type->n_fields; + if (n_args + n_kw != num_fields) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), + num_fields, n_args + n_kw); + #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("%q() takes %d positional arguments but %d were given"), + ((mp_obj_type_t *)&type->base)->name, num_fields, n_args + n_kw); + #endif + } + + // Create a namedtuple with explicit malloc. Calling mp_obj_new_tuple + // with num_fields=0 returns a read-only object. + mp_obj_tuple_t *tuple = mp_obj_malloc_var(mp_obj_tuple_t, items, mp_obj_t, num_fields, type_in); + tuple->len = num_fields; + + // Copy the positional args into the first slots of the namedtuple + memcpy(&tuple->items[0], args, sizeof(mp_obj_t) * n_args); + + // Fill in the remaining slots with the keyword args + memset(&tuple->items[n_args], 0, sizeof(mp_obj_t) * n_kw); + for (size_t i = n_args; i < n_args + 2 * n_kw; i += 2) { + qstr kw = mp_obj_str_get_qstr(args[i]); + size_t id = mp_obj_namedtuple_find_field(type, kw); + if (id == (size_t)-1) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("unexpected keyword argument '%q'"), kw); + #endif + } + if (tuple->items[id] != MP_OBJ_NULL) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_arg_error_terse_mismatch(); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function got multiple values for argument '%q'"), kw); + #endif + } + tuple->items[id] = args[i + 1]; + } + + return MP_OBJ_FROM_PTR(tuple); +} + +mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields) { + mp_obj_namedtuple_type_t *o = m_new_obj_var0(mp_obj_namedtuple_type_t, fields, qstr, n_fields); + o->n_fields = n_fields; + for (size_t i = 0; i < n_fields; i++) { + o->fields[i] = mp_obj_str_get_qstr(fields[i]); + } + return o; +} + +static mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) { + mp_obj_namedtuple_type_t *o = mp_obj_new_namedtuple_base(n_fields, fields); + mp_obj_type_t *type = (mp_obj_type_t *)&o->base; + type->base.type = &mp_type_type; + type->flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE; // can match tuple + type->name = name; + MP_OBJ_TYPE_SET_SLOT(type, make_new, namedtuple_make_new, 0); + MP_OBJ_TYPE_SET_SLOT(type, print, namedtuple_print, 1); + MP_OBJ_TYPE_SET_SLOT(type, unary_op, mp_obj_tuple_unary_op, 2); + MP_OBJ_TYPE_SET_SLOT(type, binary_op, mp_obj_tuple_binary_op, 3); + MP_OBJ_TYPE_SET_SLOT(type, attr, namedtuple_attr, 4); + MP_OBJ_TYPE_SET_SLOT(type, subscr, mp_obj_tuple_subscr, 5); + MP_OBJ_TYPE_SET_SLOT(type, iter, mp_obj_tuple_getiter, 6); + MP_OBJ_TYPE_SET_SLOT(type, parent, &mp_type_tuple, 7); + return MP_OBJ_FROM_PTR(o); +} + +static mp_obj_t new_namedtuple_type(mp_obj_t name_in, mp_obj_t fields_in) { + qstr name = mp_obj_str_get_qstr(name_in); + size_t n_fields; + mp_obj_t *fields; + #if MICROPY_CPYTHON_COMPAT + if (mp_obj_is_str(fields_in)) { + fields_in = mp_obj_str_split(1, &fields_in); + } + #endif + mp_obj_get_array(fields_in, &n_fields, &fields); + return mp_obj_new_namedtuple_type(name, n_fields, fields); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_namedtuple_obj, new_namedtuple_type); + +#endif // MICROPY_PY_COLLECTIONS diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objnamedtuple.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objnamedtuple.h new file mode 100644 index 00000000..c4f4149f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objnamedtuple.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Paul Sokolovsky + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJNAMEDTUPLE_H +#define MICROPY_INCLUDED_PY_OBJNAMEDTUPLE_H + +#include "py/objtuple.h" + +typedef struct _mp_obj_namedtuple_type_t { + // This is a mp_obj_type_t with eight slots. + mp_obj_empty_type_t base; + void *slots[8]; + size_t n_fields; + qstr fields[]; +} mp_obj_namedtuple_type_t; + +typedef struct _mp_obj_namedtuple_t { + mp_obj_tuple_t tuple; +} mp_obj_namedtuple_t; + +size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name); +mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields); + +#endif // MICROPY_INCLUDED_PY_OBJNAMEDTUPLE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objnone.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objnone.c new file mode 100644 index 00000000..a8ce8ebf --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objnone.c @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" + +#if !MICROPY_OBJ_IMMEDIATE_OBJS +typedef struct _mp_obj_none_t { + mp_obj_base_t base; +} mp_obj_none_t; +#endif + +static void none_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)self_in; + if (MICROPY_PY_JSON && kind == PRINT_JSON) { + mp_print_str(print, "null"); + } else { + mp_print_str(print, "None"); + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_NoneType, + MP_QSTR_NoneType, + MP_TYPE_FLAG_NONE, + print, none_print + ); + +#if !MICROPY_OBJ_IMMEDIATE_OBJS +const mp_obj_none_t mp_const_none_obj = {{&mp_type_NoneType}}; +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objobject.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objobject.c new file mode 100644 index 00000000..ff93fd08 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objobject.c @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/objtype.h" +#include "py/runtime.h" + +typedef struct _mp_obj_object_t { + mp_obj_base_t base; +} mp_obj_object_t; + +static mp_obj_t object_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)args; + mp_arg_check_num(n_args, n_kw, 0, 0, false); + mp_obj_object_t *o = mp_obj_malloc(mp_obj_object_t, type); + return MP_OBJ_FROM_PTR(o); +} + +#if MICROPY_CPYTHON_COMPAT +static mp_obj_t object___init__(mp_obj_t self) { + (void)self; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(object___init___obj, object___init__); + +static mp_obj_t object___new__(mp_obj_t cls) { + if (!mp_obj_is_type(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t *)MP_OBJ_TO_PTR(cls))) { + mp_raise_TypeError(MP_ERROR_TEXT("arg must be user-type")); + } + // This executes only "__new__" part of instance creation. + // TODO: This won't work well for classes with native bases. + // TODO: This is a hack, should be resolved along the lines of + // https://github.com/micropython/micropython/issues/606#issuecomment-43685883 + const mp_obj_type_t *native_base; + return MP_OBJ_FROM_PTR(mp_obj_new_instance(MP_OBJ_TO_PTR(cls), &native_base)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__); +static MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj)); + +#if MICROPY_PY_DELATTR_SETATTR +static mp_obj_t object___setattr__(mp_obj_t self_in, mp_obj_t attr, mp_obj_t value) { + if (!mp_obj_is_instance_type(mp_obj_get_type(self_in))) { + mp_raise_TypeError(MP_ERROR_TEXT("arg must be user-type")); + } + + if (!mp_obj_is_str(attr)) { + mp_raise_TypeError(NULL); + } + + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + mp_map_lookup(&self->members, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(object___setattr___obj, object___setattr__); + +static mp_obj_t object___delattr__(mp_obj_t self_in, mp_obj_t attr) { + if (!mp_obj_is_instance_type(mp_obj_get_type(self_in))) { + mp_raise_TypeError(MP_ERROR_TEXT("arg must be user-type")); + } + + if (!mp_obj_is_str(attr)) { + mp_raise_TypeError(NULL); + } + + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + if (mp_map_lookup(&self->members, attr, MP_MAP_LOOKUP_REMOVE_IF_FOUND) == NULL) { + mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("no such attribute")); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(object___delattr___obj, object___delattr__); +#endif + +static const mp_rom_map_elem_t object_locals_dict_table[] = { + #if MICROPY_CPYTHON_COMPAT + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&object___init___obj) }, + #endif + #if MICROPY_CPYTHON_COMPAT + { MP_ROM_QSTR(MP_QSTR___new__), MP_ROM_PTR(&object___new___obj) }, + #endif + #if MICROPY_PY_DELATTR_SETATTR + { MP_ROM_QSTR(MP_QSTR___setattr__), MP_ROM_PTR(&object___setattr___obj) }, + { MP_ROM_QSTR(MP_QSTR___delattr__), MP_ROM_PTR(&object___delattr___obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(object_locals_dict, object_locals_dict_table); +#endif + +#if MICROPY_CPYTHON_COMPAT +#define OBJECT_TYPE_LOCALS_DICT , locals_dict, &object_locals_dict +#else +#define OBJECT_TYPE_LOCALS_DICT +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_object, + MP_QSTR_object, + MP_TYPE_FLAG_NONE, + make_new, object_make_new + OBJECT_TYPE_LOCALS_DICT + ); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objpolyiter.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objpolyiter.c new file mode 100644 index 00000000..65c1f182 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objpolyiter.c @@ -0,0 +1,86 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paul Sokolovsky + * + * 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 "py/runtime.h" + +// This is universal iterator type which calls "iternext" method stored in +// particular object instance. (So, each instance of this time can have its +// own iteration behavior.) Having this type saves to define type objects +// for various internal iterator objects. + +// Any instance should have these 2 fields at the beginning +typedef struct _mp_obj_polymorph_iter_t { + mp_obj_base_t base; + mp_fun_1_t iternext; +} mp_obj_polymorph_iter_t; + +static mp_obj_t polymorph_it_iternext(mp_obj_t self_in) { + mp_obj_polymorph_iter_t *self = MP_OBJ_TO_PTR(self_in); + // Redirect call to object instance's iternext method + return self->iternext(self_in); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_polymorph_iter, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, polymorph_it_iternext + ); + +#if MICROPY_ENABLE_FINALISER +// mp_type_polymorph_iter_with_finaliser is a variant of the universal iterator +// above which has a finaliser function attached. This function will be run when +// the GC collects the iter object and can be used to close file handles etc. + +// Any instance should have these 3 fields at the beginning +typedef struct _mp_obj_polymorph_iter_with_finaliser_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_fun_1_t finaliser; +} mp_obj_polymorph_with_finaliser_iter_t; + +static mp_obj_t mp_obj_polymorph_iter_del(mp_obj_t self_in) { + mp_obj_polymorph_with_finaliser_iter_t *self = MP_OBJ_TO_PTR(self_in); + // Redirect call to object instance's finaliser method + return self->finaliser(self_in); +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_obj_polymorph_iter_del_obj, mp_obj_polymorph_iter_del); + +static const mp_rom_map_elem_t mp_obj_polymorph_iter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_obj_polymorph_iter_del_obj) }, +}; +static MP_DEFINE_CONST_DICT(mp_obj_polymorph_iter_locals_dict, mp_obj_polymorph_iter_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_polymorph_iter_with_finaliser, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, polymorph_it_iternext, + locals_dict, &mp_obj_polymorph_iter_locals_dict + ); +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objproperty.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objproperty.c new file mode 100644 index 00000000..155ffb16 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objproperty.c @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +#if MICROPY_PY_BUILTINS_PROPERTY + +typedef struct _mp_obj_property_t { + mp_obj_base_t base; + mp_obj_t proxy[3]; // getter, setter, deleter +} mp_obj_property_t; + +static mp_obj_t property_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + enum { ARG_fget, ARG_fset, ARG_fdel, ARG_doc }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_doc, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals); + + mp_obj_property_t *o = mp_obj_malloc(mp_obj_property_t, type); + o->proxy[0] = vals[ARG_fget].u_obj; + o->proxy[1] = vals[ARG_fset].u_obj; + o->proxy[2] = vals[ARG_fdel].u_obj; + // vals[ARG_doc] is silently discarded + return MP_OBJ_FROM_PTR(o); +} + +static mp_obj_t property_getter(mp_obj_t self_in, mp_obj_t getter) { + mp_obj_property_t *p2 = m_new_obj(mp_obj_property_t); + *p2 = *(mp_obj_property_t *)MP_OBJ_TO_PTR(self_in); + p2->proxy[0] = getter; + return MP_OBJ_FROM_PTR(p2); +} + +static MP_DEFINE_CONST_FUN_OBJ_2(property_getter_obj, property_getter); + +static mp_obj_t property_setter(mp_obj_t self_in, mp_obj_t setter) { + mp_obj_property_t *p2 = m_new_obj(mp_obj_property_t); + *p2 = *(mp_obj_property_t *)MP_OBJ_TO_PTR(self_in); + p2->proxy[1] = setter; + return MP_OBJ_FROM_PTR(p2); +} + +static MP_DEFINE_CONST_FUN_OBJ_2(property_setter_obj, property_setter); + +static mp_obj_t property_deleter(mp_obj_t self_in, mp_obj_t deleter) { + mp_obj_property_t *p2 = m_new_obj(mp_obj_property_t); + *p2 = *(mp_obj_property_t *)MP_OBJ_TO_PTR(self_in); + p2->proxy[2] = deleter; + return MP_OBJ_FROM_PTR(p2); +} + +static MP_DEFINE_CONST_FUN_OBJ_2(property_deleter_obj, property_deleter); + +static const mp_rom_map_elem_t property_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_getter), MP_ROM_PTR(&property_getter_obj) }, + { MP_ROM_QSTR(MP_QSTR_setter), MP_ROM_PTR(&property_setter_obj) }, + { MP_ROM_QSTR(MP_QSTR_deleter), MP_ROM_PTR(&property_deleter_obj) }, +}; + +static MP_DEFINE_CONST_DICT(property_locals_dict, property_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_property, + MP_QSTR_property, + MP_TYPE_FLAG_NONE, + make_new, property_make_new, + locals_dict, &property_locals_dict + ); + +const mp_obj_t *mp_obj_property_get(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_property)); + mp_obj_property_t *self = MP_OBJ_TO_PTR(self_in); + return self->proxy; +} + +#endif // MICROPY_PY_BUILTINS_PROPERTY diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objrange.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objrange.c new file mode 100644 index 00000000..9a4ecd3f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objrange.c @@ -0,0 +1,234 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +/******************************************************************************/ +/* range iterator */ + +typedef struct _mp_obj_range_it_t { + mp_obj_base_t base; + // TODO make these values generic objects or something + mp_int_t cur; + mp_int_t stop; + mp_int_t step; +} mp_obj_range_it_t; + +static mp_obj_t range_it_iternext(mp_obj_t o_in) { + mp_obj_range_it_t *o = MP_OBJ_TO_PTR(o_in); + if ((o->step > 0 && o->cur < o->stop) || (o->step < 0 && o->cur > o->stop)) { + mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT(o->cur); + o->cur += o->step; + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_range_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, range_it_iternext + ); + +static mp_obj_t mp_obj_new_range_iterator(mp_int_t cur, mp_int_t stop, mp_int_t step, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_range_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_range_it_t *o = (mp_obj_range_it_t *)iter_buf; + o->base.type = &mp_type_range_it; + o->cur = cur; + o->stop = stop; + o->step = step; + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +/* range */ + +typedef struct _mp_obj_range_t { + mp_obj_base_t base; + // TODO make these values generic objects or something + mp_int_t start; + mp_int_t stop; + mp_int_t step; +} mp_obj_range_t; + +static void range_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_range_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "range(" INT_FMT ", " INT_FMT "", self->start, self->stop); + if (self->step == 1) { + mp_print_str(print, ")"); + } else { + mp_printf(print, ", " INT_FMT ")", self->step); + } +} + +static mp_obj_t range_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 3, false); + + mp_obj_range_t *o = mp_obj_malloc(mp_obj_range_t, type); + o->start = 0; + o->step = 1; + + if (n_args == 1) { + o->stop = mp_obj_get_int(args[0]); + } else { + o->start = mp_obj_get_int(args[0]); + o->stop = mp_obj_get_int(args[1]); + if (n_args == 3) { + o->step = mp_obj_get_int(args[2]); + if (o->step == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("zero step")); + } + } + } + + return MP_OBJ_FROM_PTR(o); +} + +static mp_int_t range_len(mp_obj_range_t *self) { + // When computing length, need to take into account step!=1 and step<0. + mp_int_t len = self->stop - self->start + self->step; + if (self->step > 0) { + len -= 1; + } else { + len += 1; + } + len = len / self->step; + if (len < 0) { + len = 0; + } + return len; +} + +static mp_obj_t range_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_range_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t len = range_len(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len > 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +#if MICROPY_PY_BUILTINS_RANGE_BINOP +static mp_obj_t range_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + if (!mp_obj_is_type(rhs_in, &mp_type_range) || op != MP_BINARY_OP_EQUAL) { + return MP_OBJ_NULL; // op not supported + } + mp_obj_range_t *lhs = MP_OBJ_TO_PTR(lhs_in); + mp_obj_range_t *rhs = MP_OBJ_TO_PTR(rhs_in); + mp_int_t lhs_len = range_len(lhs); + mp_int_t rhs_len = range_len(rhs); + return mp_obj_new_bool( + lhs_len == rhs_len + && (lhs_len == 0 + || (lhs->start == rhs->start + && (lhs_len == 1 || lhs->step == rhs->step))) + ); +} +#endif + +static mp_obj_t range_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + if (value == MP_OBJ_SENTINEL) { + // load + mp_obj_range_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t len = range_len(self); + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_bound_slice_t slice; + mp_seq_get_fast_slice_indexes(len, index, &slice); + mp_obj_range_t *o = mp_obj_malloc(mp_obj_range_t, &mp_type_range); + o->start = self->start + slice.start * self->step; + o->stop = self->start + slice.stop * self->step; + o->step = slice.step * self->step; + if (slice.step < 0) { + // Negative slice steps have inclusive stop, so adjust for exclusive + o->stop -= self->step; + } + return MP_OBJ_FROM_PTR(o); + } + #endif + size_t index_val = mp_get_index(self->base.type, len, index, false); + return MP_OBJ_NEW_SMALL_INT(self->start + index_val * self->step); + } else { + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t range_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { + mp_obj_range_t *o = MP_OBJ_TO_PTR(o_in); + return mp_obj_new_range_iterator(o->start, o->stop, o->step, iter_buf); +} + + +#if MICROPY_PY_BUILTINS_RANGE_ATTRS +static void range_attr(mp_obj_t o_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + mp_obj_range_t *o = MP_OBJ_TO_PTR(o_in); + if (attr == MP_QSTR_start) { + dest[0] = mp_obj_new_int(o->start); + } else if (attr == MP_QSTR_stop) { + dest[0] = mp_obj_new_int(o->stop); + } else if (attr == MP_QSTR_step) { + dest[0] = mp_obj_new_int(o->step); + } +} +#endif + +#if MICROPY_PY_BUILTINS_RANGE_BINOP +#define RANGE_TYPE_BINOP binary_op, range_binary_op, +#else +#define RANGE_TYPE_BINOP +#endif + +#if MICROPY_PY_BUILTINS_RANGE_ATTRS +#define RANGE_TYPE_ATTR attr, range_attr, +#else +#define RANGE_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_range, + MP_QSTR_range, + MP_TYPE_FLAG_NONE, + make_new, range_make_new, + RANGE_TYPE_BINOP + RANGE_TYPE_ATTR + print, range_print, + unary_op, range_unary_op, + subscr, range_subscr, + iter, range_getiter + ); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objreversed.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objreversed.c new file mode 100644 index 00000000..c580ee28 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objreversed.c @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 "py/runtime.h" + +#if MICROPY_PY_BUILTINS_REVERSED + +typedef struct _mp_obj_reversed_t { + mp_obj_base_t base; + mp_obj_t seq; // sequence object that we are reversing + mp_uint_t cur_index; // current index, plus 1; 0=no more, 1=last one (index 0) +} mp_obj_reversed_t; + +static mp_obj_t reversed_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + // check if __reversed__ exists, and if so delegate to it + mp_obj_t dest[2]; + mp_load_method_maybe(args[0], MP_QSTR___reversed__, dest); + if (dest[0] != MP_OBJ_NULL) { + return mp_call_method_n_kw(0, 0, dest); + } + + mp_obj_reversed_t *o = mp_obj_malloc(mp_obj_reversed_t, type); + o->seq = args[0]; + o->cur_index = mp_obj_get_int(mp_obj_len(args[0])); // start at the end of the sequence + + return MP_OBJ_FROM_PTR(o); +} + +static mp_obj_t reversed_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_reversed)); + mp_obj_reversed_t *self = MP_OBJ_TO_PTR(self_in); + + // "raise" stop iteration if we are at the end (the start) of the sequence + if (self->cur_index == 0) { + return MP_OBJ_STOP_ITERATION; + } + + // pre-decrement and index sequence + self->cur_index -= 1; + return mp_obj_subscr(self->seq, MP_OBJ_NEW_SMALL_INT(self->cur_index), MP_OBJ_SENTINEL); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_reversed, + MP_QSTR_reversed, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, reversed_make_new, + iter, reversed_iternext + ); + +#endif // MICROPY_PY_BUILTINS_REVERSED diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objset.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objset.c new file mode 100644 index 00000000..c8fa12a7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objset.c @@ -0,0 +1,596 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 Damien P. George + * + * 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 "py/runtime.h" +#include "py/builtin.h" + +#if MICROPY_PY_BUILTINS_SET + +typedef struct _mp_obj_set_t { + mp_obj_base_t base; + mp_set_t set; +} mp_obj_set_t; + +typedef struct _mp_obj_set_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_set_t *set; + size_t cur; +} mp_obj_set_it_t; + +static bool is_set_or_frozenset(mp_obj_t o) { + return mp_obj_is_type(o, &mp_type_set) + #if MICROPY_PY_BUILTINS_FROZENSET + || mp_obj_is_type(o, &mp_type_frozenset) + #endif + ; +} + +// This macro is shorthand for mp_check_self to verify the argument is a set. +#define check_set(o) mp_check_self(mp_obj_is_type(o, &mp_type_set)) + +// This macro is shorthand for mp_check_self to verify the argument is a +// set or frozenset for methods that operate on both of these types. +#define check_set_or_frozenset(o) mp_check_self(is_set_or_frozenset(o)) + +static void set_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + #if MICROPY_PY_BUILTINS_FROZENSET + bool is_frozen = mp_obj_is_type(self_in, &mp_type_frozenset); + #endif + if (self->set.used == 0) { + #if MICROPY_PY_BUILTINS_FROZENSET + if (is_frozen) { + mp_print_str(print, "frozen"); + } + #endif + mp_print_str(print, "set()"); + return; + } + bool first = true; + #if MICROPY_PY_BUILTINS_FROZENSET + if (is_frozen) { + mp_print_str(print, "frozenset("); + } + #endif + mp_print_str(print, "{"); + for (size_t i = 0; i < self->set.alloc; i++) { + if (mp_set_slot_is_filled(&self->set, i)) { + if (!first) { + mp_print_str(print, ", "); + } + first = false; + mp_obj_print_helper(print, self->set.table[i], PRINT_REPR); + } + } + mp_print_str(print, "}"); + #if MICROPY_PY_BUILTINS_FROZENSET + if (is_frozen) { + mp_print_str(print, ")"); + } + #endif +} + +static mp_obj_t set_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + switch (n_args) { + case 0: { + // create a new, empty set + mp_obj_set_t *set = MP_OBJ_TO_PTR(mp_obj_new_set(0, NULL)); + // set actual set/frozenset type + set->base.type = type; + return MP_OBJ_FROM_PTR(set); + } + + case 1: + default: { // can only be 0 or 1 arg + // 1 argument, an iterable from which we make a new set + mp_obj_t set = mp_obj_new_set(0, NULL); + mp_obj_t iterable = mp_getiter(args[0], NULL); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + mp_obj_set_store(set, item); + } + // Set actual set/frozenset type + ((mp_obj_set_t *)MP_OBJ_TO_PTR(set))->base.type = type; + return set; + } + } +} + +static mp_obj_t set_it_iternext(mp_obj_t self_in) { + mp_obj_set_it_t *self = MP_OBJ_TO_PTR(self_in); + size_t max = self->set->set.alloc; + mp_set_t *set = &self->set->set; + + for (size_t i = self->cur; i < max; i++) { + if (mp_set_slot_is_filled(set, i)) { + self->cur = i + 1; + return set->table[i]; + } + } + + return MP_OBJ_STOP_ITERATION; +} + +static mp_obj_t set_getiter(mp_obj_t set_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_set_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_set_it_t *o = (mp_obj_set_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = set_it_iternext; + o->set = (mp_obj_set_t *)MP_OBJ_TO_PTR(set_in); + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +/* set methods */ + +static mp_obj_t set_add(mp_obj_t self_in, mp_obj_t item) { + check_set(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_add_obj, set_add); + +static mp_obj_t set_clear(mp_obj_t self_in) { + check_set(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_set_clear(&self->set); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(set_clear_obj, set_clear); + +static mp_obj_t set_copy(mp_obj_t self_in) { + check_set_or_frozenset(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_set_t *other = mp_obj_malloc(mp_obj_set_t, self->base.type); + mp_set_init(&other->set, self->set.alloc); + other->set.used = self->set.used; + memcpy(other->set.table, self->set.table, self->set.alloc * sizeof(mp_obj_t)); + return MP_OBJ_FROM_PTR(other); +} +static MP_DEFINE_CONST_FUN_OBJ_1(set_copy_obj, set_copy); + +static mp_obj_t set_discard(mp_obj_t self_in, mp_obj_t item) { + check_set(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_REMOVE_IF_FOUND); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_discard_obj, set_discard); + +static mp_obj_t set_diff_int(size_t n_args, const mp_obj_t *args, bool update) { + mp_obj_t self; + if (update) { + check_set(args[0]); + self = args[0]; + } else { + self = set_copy(args[0]); + } + + for (size_t i = 1; i < n_args; i++) { + mp_obj_t other = args[i]; + if (self == other) { + set_clear(self); + } else { + mp_set_t *self_set = &((mp_obj_set_t *)MP_OBJ_TO_PTR(self))->set; + mp_obj_t iter = mp_getiter(other, NULL); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + mp_set_lookup(self_set, next, MP_MAP_LOOKUP_REMOVE_IF_FOUND); + } + } + } + + return self; +} + +static mp_obj_t set_diff(size_t n_args, const mp_obj_t *args) { + return set_diff_int(n_args, args, false); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR(set_diff_obj, 1, set_diff); + +static mp_obj_t set_diff_update(size_t n_args, const mp_obj_t *args) { + set_diff_int(n_args, args, true); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR(set_diff_update_obj, 1, set_diff_update); + +static mp_obj_t set_intersect_int(mp_obj_t self_in, mp_obj_t other, bool update) { + if (update) { + check_set(self_in); + } else { + check_set_or_frozenset(self_in); + } + + if (self_in == other) { + return update ? mp_const_none : set_copy(self_in); + } + + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_set_t *out = MP_OBJ_TO_PTR(mp_obj_new_set(0, NULL)); + + mp_obj_t iter = mp_getiter(other, NULL); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) { + set_add(MP_OBJ_FROM_PTR(out), next); + } + } + + if (update) { + m_del(mp_obj_t, self->set.table, self->set.alloc); + self->set.alloc = out->set.alloc; + self->set.used = out->set.used; + self->set.table = out->set.table; + } + + return update ? mp_const_none : MP_OBJ_FROM_PTR(out); +} + +static mp_obj_t set_intersect(mp_obj_t self_in, mp_obj_t other) { + return set_intersect_int(self_in, other, false); +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_intersect_obj, set_intersect); + +static mp_obj_t set_intersect_update(mp_obj_t self_in, mp_obj_t other) { + return set_intersect_int(self_in, other, true); +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_intersect_update_obj, set_intersect_update); + +static mp_obj_t set_isdisjoint(mp_obj_t self_in, mp_obj_t other) { + check_set_or_frozenset(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_iter_buf_t iter_buf; + mp_obj_t iter = mp_getiter(other, &iter_buf); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) { + return mp_const_false; + } + } + return mp_const_true; +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_isdisjoint_obj, set_isdisjoint); + +static mp_obj_t set_issubset_internal(mp_obj_t self_in, mp_obj_t other_in, bool proper) { + mp_obj_set_t *self; + bool cleanup_self = false; + if (is_set_or_frozenset(self_in)) { + self = MP_OBJ_TO_PTR(self_in); + } else { + self = MP_OBJ_TO_PTR(set_make_new(&mp_type_set, 1, 0, &self_in)); + cleanup_self = true; + } + + mp_obj_set_t *other; + bool cleanup_other = false; + if (is_set_or_frozenset(other_in)) { + other = MP_OBJ_TO_PTR(other_in); + } else { + other = MP_OBJ_TO_PTR(set_make_new(&mp_type_set, 1, 0, &other_in)); + cleanup_other = true; + } + mp_obj_t out = mp_const_true; + if (proper && self->set.used == other->set.used) { + out = mp_const_false; + } else { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iter = set_getiter(MP_OBJ_FROM_PTR(self), &iter_buf); + mp_obj_t next; + while ((next = set_it_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + if (!mp_set_lookup(&other->set, next, MP_MAP_LOOKUP)) { + out = mp_const_false; + break; + } + } + } + // TODO: Should free objects altogether + if (cleanup_self) { + set_clear(MP_OBJ_FROM_PTR(self)); + } + if (cleanup_other) { + set_clear(MP_OBJ_FROM_PTR(other)); + } + return out; +} + +static mp_obj_t set_issubset(mp_obj_t self_in, mp_obj_t other_in) { + return set_issubset_internal(self_in, other_in, false); +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_issubset_obj, set_issubset); + +static mp_obj_t set_issubset_proper(mp_obj_t self_in, mp_obj_t other_in) { + return set_issubset_internal(self_in, other_in, true); +} + +static mp_obj_t set_issuperset(mp_obj_t self_in, mp_obj_t other_in) { + return set_issubset_internal(other_in, self_in, false); +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_issuperset_obj, set_issuperset); + +static mp_obj_t set_issuperset_proper(mp_obj_t self_in, mp_obj_t other_in) { + return set_issubset_internal(other_in, self_in, true); +} + +static mp_obj_t set_equal(mp_obj_t self_in, mp_obj_t other_in) { + assert(is_set_or_frozenset(other_in)); + check_set_or_frozenset(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_set_t *other = MP_OBJ_TO_PTR(other_in); + if (self->set.used != other->set.used) { + return mp_const_false; + } + return set_issubset(self_in, other_in); +} + +static mp_obj_t set_pop(mp_obj_t self_in) { + check_set(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t obj = mp_set_remove_first(&self->set); + if (obj == MP_OBJ_NULL) { + mp_raise_msg(&mp_type_KeyError, MP_ERROR_TEXT("pop from an empty set")); + } + return obj; +} +static MP_DEFINE_CONST_FUN_OBJ_1(set_pop_obj, set_pop); + +static mp_obj_t set_remove(mp_obj_t self_in, mp_obj_t item) { + check_set(self_in); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + if (mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_REMOVE_IF_FOUND) == MP_OBJ_NULL) { + mp_raise_type_arg(&mp_type_KeyError, item); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_remove_obj, set_remove); + +static mp_obj_t set_symmetric_difference_update(mp_obj_t self_in, mp_obj_t other_in) { + check_set_or_frozenset(self_in); // can be frozenset due to call from set_symmetric_difference + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t iter = mp_getiter(other_in, NULL); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_symmetric_difference_update_obj, set_symmetric_difference_update); + +static mp_obj_t set_symmetric_difference(mp_obj_t self_in, mp_obj_t other_in) { + mp_obj_t self_out = set_copy(self_in); + set_symmetric_difference_update(self_out, other_in); + return self_out; +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_symmetric_difference_obj, set_symmetric_difference); + +static void set_update_int(mp_obj_set_t *self, mp_obj_t other_in) { + mp_obj_t iter = mp_getiter(other_in, NULL); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + } +} + +static mp_obj_t set_update(size_t n_args, const mp_obj_t *args) { + check_set(args[0]); + for (size_t i = 1; i < n_args; i++) { + set_update_int(MP_OBJ_TO_PTR(args[0]), args[i]); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR(set_update_obj, 1, set_update); + +static mp_obj_t set_union(mp_obj_t self_in, mp_obj_t other_in) { + check_set_or_frozenset(self_in); + mp_obj_t self = set_copy(self_in); + set_update_int(MP_OBJ_TO_PTR(self), other_in); + return self; +} +static MP_DEFINE_CONST_FUN_OBJ_2(set_union_obj, set_union); + +static mp_obj_t set_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(self->set.used != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(self->set.used); + #if MICROPY_PY_BUILTINS_FROZENSET + case MP_UNARY_OP_HASH: + if (mp_obj_is_type(self_in, &mp_type_frozenset)) { + // start hash with unique value + mp_int_t hash = (mp_int_t)(uintptr_t)&mp_type_frozenset; + size_t max = self->set.alloc; + mp_set_t *set = &self->set; + + for (size_t i = 0; i < max; i++) { + if (mp_set_slot_is_filled(set, i)) { + hash += MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, set->table[i])); + } + } + return MP_OBJ_NEW_SMALL_INT(hash); + } + MP_FALLTHROUGH + #endif + default: + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t set_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { + mp_obj_t args[] = {lhs, rhs}; + #if MICROPY_PY_BUILTINS_FROZENSET + bool update = mp_obj_is_type(lhs, &mp_type_set); + #else + bool update = true; + #endif + if (op != MP_BINARY_OP_CONTAINS && !is_set_or_frozenset(rhs)) { + // For all ops except containment the RHS must be a set/frozenset + return MP_OBJ_NULL; + } + switch (op) { + case MP_BINARY_OP_OR: + return set_union(lhs, rhs); + case MP_BINARY_OP_XOR: + return set_symmetric_difference(lhs, rhs); + case MP_BINARY_OP_AND: + return set_intersect(lhs, rhs); + case MP_BINARY_OP_SUBTRACT: + return set_diff(2, args); + case MP_BINARY_OP_INPLACE_OR: + if (update) { + set_update(2, args); + return lhs; + } else { + return set_union(lhs, rhs); + } + case MP_BINARY_OP_INPLACE_XOR: + if (update) { + set_symmetric_difference_update(lhs, rhs); + return lhs; + } else { + return set_symmetric_difference(lhs, rhs); + } + case MP_BINARY_OP_INPLACE_AND: + rhs = set_intersect_int(lhs, rhs, update); + if (update) { + return lhs; + } else { + return rhs; + } + case MP_BINARY_OP_INPLACE_SUBTRACT: + return set_diff_int(2, args, update); + case MP_BINARY_OP_LESS: + return set_issubset_proper(lhs, rhs); + case MP_BINARY_OP_MORE: + return set_issuperset_proper(lhs, rhs); + case MP_BINARY_OP_EQUAL: + return set_equal(lhs, rhs); + case MP_BINARY_OP_LESS_EQUAL: + return set_issubset(lhs, rhs); + case MP_BINARY_OP_MORE_EQUAL: + return set_issuperset(lhs, rhs); + case MP_BINARY_OP_CONTAINS: { + mp_obj_set_t *o = MP_OBJ_TO_PTR(lhs); + mp_obj_t elem = mp_set_lookup(&o->set, rhs, MP_MAP_LOOKUP); + return mp_obj_new_bool(elem != MP_OBJ_NULL); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +/******************************************************************************/ +/* set constructors & public C API */ + +static const mp_rom_map_elem_t set_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&set_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&set_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&set_copy_obj) }, + { MP_ROM_QSTR(MP_QSTR_discard), MP_ROM_PTR(&set_discard_obj) }, + { MP_ROM_QSTR(MP_QSTR_difference), MP_ROM_PTR(&set_diff_obj) }, + { MP_ROM_QSTR(MP_QSTR_difference_update), MP_ROM_PTR(&set_diff_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_intersection), MP_ROM_PTR(&set_intersect_obj) }, + { MP_ROM_QSTR(MP_QSTR_intersection_update), MP_ROM_PTR(&set_intersect_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_isdisjoint), MP_ROM_PTR(&set_isdisjoint_obj) }, + { MP_ROM_QSTR(MP_QSTR_issubset), MP_ROM_PTR(&set_issubset_obj) }, + { MP_ROM_QSTR(MP_QSTR_issuperset), MP_ROM_PTR(&set_issuperset_obj) }, + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&set_pop_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&set_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_symmetric_difference), MP_ROM_PTR(&set_symmetric_difference_obj) }, + { MP_ROM_QSTR(MP_QSTR_symmetric_difference_update), MP_ROM_PTR(&set_symmetric_difference_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_union), MP_ROM_PTR(&set_union_obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&set_update_obj) }, + { MP_ROM_QSTR(MP_QSTR___contains__), MP_ROM_PTR(&mp_op_contains_obj) }, +}; +static MP_DEFINE_CONST_DICT(set_locals_dict, set_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_set, + MP_QSTR_set, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, set_make_new, + print, set_print, + unary_op, set_unary_op, + binary_op, set_binary_op, + iter, set_getiter, + locals_dict, &set_locals_dict + ); + +#if MICROPY_PY_BUILTINS_FROZENSET +static const mp_rom_map_elem_t frozenset_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&set_copy_obj) }, + { MP_ROM_QSTR(MP_QSTR_difference), MP_ROM_PTR(&set_diff_obj) }, + { MP_ROM_QSTR(MP_QSTR_intersection), MP_ROM_PTR(&set_intersect_obj) }, + { MP_ROM_QSTR(MP_QSTR_isdisjoint), MP_ROM_PTR(&set_isdisjoint_obj) }, + { MP_ROM_QSTR(MP_QSTR_issubset), MP_ROM_PTR(&set_issubset_obj) }, + { MP_ROM_QSTR(MP_QSTR_issuperset), MP_ROM_PTR(&set_issuperset_obj) }, + { MP_ROM_QSTR(MP_QSTR_symmetric_difference), MP_ROM_PTR(&set_symmetric_difference_obj) }, + { MP_ROM_QSTR(MP_QSTR_union), MP_ROM_PTR(&set_union_obj) }, + { MP_ROM_QSTR(MP_QSTR___contains__), MP_ROM_PTR(&mp_op_contains_obj) }, +}; +static MP_DEFINE_CONST_DICT(frozenset_locals_dict, frozenset_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_frozenset, + MP_QSTR_frozenset, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, set_make_new, + print, set_print, + unary_op, set_unary_op, + binary_op, set_binary_op, + iter, set_getiter, + locals_dict, &frozenset_locals_dict + ); +#endif + +mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) { + mp_obj_set_t *o = mp_obj_malloc(mp_obj_set_t, &mp_type_set); + mp_set_init(&o->set, n_args); + for (size_t i = 0; i < n_args; i++) { + mp_set_lookup(&o->set, items[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + } + return MP_OBJ_FROM_PTR(o); +} + +void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_set)); + mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); + mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); +} + +#endif // MICROPY_PY_BUILTINS_SET diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objsingleton.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objsingleton.c new file mode 100644 index 00000000..c1f102c9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objsingleton.c @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" + +/******************************************************************************/ +/* singleton objects defined by Python */ + +typedef struct _mp_obj_singleton_t { + mp_obj_base_t base; + qstr name; +} mp_obj_singleton_t; + +static void singleton_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_singleton_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "%q", self->name); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_singleton, MP_QSTR_, MP_TYPE_FLAG_NONE, + print, singleton_print + ); + +const mp_obj_singleton_t mp_const_ellipsis_obj = {{&mp_type_singleton}, MP_QSTR_Ellipsis}; +#if MICROPY_PY_BUILTINS_NOTIMPLEMENTED +const mp_obj_singleton_t mp_const_notimplemented_obj = {{&mp_type_singleton}, MP_QSTR_NotImplemented}; +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objslice.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objslice.c new file mode 100644 index 00000000..3e757552 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objslice.c @@ -0,0 +1,191 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" + +/******************************************************************************/ +/* slice object */ + +#if MICROPY_PY_BUILTINS_SLICE + +static void slice_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_slice_t *o = MP_OBJ_TO_PTR(o_in); + mp_print_str(print, "slice("); + mp_obj_print_helper(print, o->start, PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, o->stop, PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, o->step, PRINT_REPR); + mp_print_str(print, ")"); +} + +static mp_obj_t slice_unary_op(mp_unary_op_t op, mp_obj_t o_in) { + // Needed to explicitly opt out of default __hash__. + // REVISIT: CPython implements comparison operators for slice. + return MP_OBJ_NULL; +} + +#if MICROPY_PY_BUILTINS_SLICE_INDICES +static mp_obj_t slice_indices(mp_obj_t self_in, mp_obj_t length_obj) { + mp_int_t length = mp_obj_get_int(length_obj); + mp_bound_slice_t bound_indices; + mp_obj_slice_indices(self_in, length, &bound_indices); + + mp_obj_t results[3] = { + MP_OBJ_NEW_SMALL_INT(bound_indices.start), + MP_OBJ_NEW_SMALL_INT(bound_indices.stop), + MP_OBJ_NEW_SMALL_INT(bound_indices.step), + }; + return mp_obj_new_tuple(3, results); +} +static MP_DEFINE_CONST_FUN_OBJ_2(slice_indices_obj, slice_indices); +#endif + +#if MICROPY_PY_BUILTINS_SLICE_ATTRS +static void slice_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + mp_obj_slice_t *self = MP_OBJ_TO_PTR(self_in); + + if (attr == MP_QSTR_start) { + dest[0] = self->start; + } else if (attr == MP_QSTR_stop) { + dest[0] = self->stop; + } else if (attr == MP_QSTR_step) { + dest[0] = self->step; + #if MICROPY_PY_BUILTINS_SLICE_INDICES + } else if (attr == MP_QSTR_indices) { + dest[0] = MP_OBJ_FROM_PTR(&slice_indices_obj); + dest[1] = self_in; + #endif + } +} +#endif + +#if MICROPY_PY_BUILTINS_SLICE_INDICES && !MICROPY_PY_BUILTINS_SLICE_ATTRS +static const mp_rom_map_elem_t slice_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_indices), MP_ROM_PTR(&slice_indices_obj) }, +}; +static MP_DEFINE_CONST_DICT(slice_locals_dict, slice_locals_dict_table); +#endif + +#if MICROPY_PY_BUILTINS_SLICE_ATTRS +#define SLICE_TYPE_ATTR_OR_LOCALS_DICT attr, slice_attr, +#elif MICROPY_PY_BUILTINS_SLICE_INDICES +#define SLICE_TYPE_ATTR_OR_LOCALS_DICT locals_dict, &slice_locals_dict, +#else +#define SLICE_TYPE_ATTR_OR_LOCALS_DICT +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_slice, + MP_QSTR_slice, + MP_TYPE_FLAG_NONE, + unary_op, slice_unary_op, + SLICE_TYPE_ATTR_OR_LOCALS_DICT + print, slice_print + ); + +mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) { + mp_obj_slice_t *o = mp_obj_malloc(mp_obj_slice_t, &mp_type_slice); + o->start = ostart; + o->stop = ostop; + o->step = ostep; + return MP_OBJ_FROM_PTR(o); +} + +// Return the real index and step values for a slice when applied to a sequence of +// the given length, resolving missing components, negative values and values off +// the end of the sequence. +void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *result) { + mp_obj_slice_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t start, stop, step; + + if (self->step == mp_const_none) { + step = 1; + } else { + step = mp_obj_get_int(self->step); + if (step == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("slice step can't be zero")); + } + } + + if (step > 0) { + // Positive step + if (self->start == mp_const_none) { + start = 0; + } else { + start = mp_obj_get_int(self->start); + if (start < 0) { + start += length; + } + start = MIN(length, MAX(start, 0)); + } + + if (self->stop == mp_const_none) { + stop = length; + } else { + stop = mp_obj_get_int(self->stop); + if (stop < 0) { + stop += length; + } + stop = MIN(length, MAX(stop, 0)); + } + } else { + // Negative step + if (self->start == mp_const_none) { + start = length - 1; + } else { + start = mp_obj_get_int(self->start); + if (start < 0) { + start += length; + } + start = MIN(length - 1, MAX(start, -1)); + } + + if (self->stop == mp_const_none) { + stop = -1; + } else { + stop = mp_obj_get_int(self->stop); + if (stop < 0) { + stop += length; + } + stop = MIN(length - 1, MAX(stop, -1)); + } + } + + result->start = start; + result->stop = stop; + result->step = step; +} + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objstr.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objstr.c new file mode 100644 index 00000000..c7e4ebf5 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objstr.c @@ -0,0 +1,2457 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2018 Paul Sokolovsky + * + * 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 "py/unicode.h" +#include "py/objstr.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include "py/stackctrl.h" + +#if MICROPY_PY_BUILTINS_STR_OP_MODULO +static mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict); +#endif + +static mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); +static NORETURN void bad_implicit_conversion(mp_obj_t self_in); + +static mp_obj_t mp_obj_new_str_type_from_vstr(const mp_obj_type_t *type, vstr_t *vstr); + +static void str_check_arg_type(const mp_obj_type_t *self_type, const mp_obj_t arg) { + // String operations generally need the args type to match the object they're called on, + // e.g. str.find(str), byte.startswith(byte) + // with the exception that bytes may be used for bytearray and vice versa. + const mp_obj_type_t *arg_type = mp_obj_get_type(arg); + + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (arg_type == &mp_type_bytearray) { + arg_type = &mp_type_bytes; + } + if (self_type == &mp_type_bytearray) { + self_type = &mp_type_bytes; + } + #endif + + if (arg_type != self_type) { + bad_implicit_conversion(arg); + } +} + +static void check_is_str_or_bytes(mp_obj_t self_in) { + mp_check_self(mp_obj_is_str_or_bytes(self_in)); +} + +/******************************************************************************/ +/* str */ + +void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t str_len, bool is_bytes) { + // this escapes characters, but it will be very slow to print (calling print many times) + bool has_single_quote = false; + bool has_double_quote = false; + for (const byte *s = str_data, *top = str_data + str_len; !has_double_quote && s < top; s++) { + if (*s == '\'') { + has_single_quote = true; + } else if (*s == '"') { + has_double_quote = true; + } + } + int quote_char = '\''; + if (has_single_quote && !has_double_quote) { + quote_char = '"'; + } + mp_printf(print, "%c", quote_char); + for (const byte *s = str_data, *top = str_data + str_len; s < top; s++) { + if (*s == quote_char) { + mp_printf(print, "\\%c", quote_char); + } else if (*s == '\\') { + mp_print_str(print, "\\\\"); + } else if (*s >= 0x20 && *s != 0x7f && (!is_bytes || *s < 0x80)) { + // In strings, anything which is not ascii control character + // is printed as is, this includes characters in range 0x80-0xff + // (which can be non-Latin letters, etc.) + mp_printf(print, "%c", *s); + } else if (*s == '\n') { + mp_print_str(print, "\\n"); + } else if (*s == '\r') { + mp_print_str(print, "\\r"); + } else if (*s == '\t') { + mp_print_str(print, "\\t"); + } else { + mp_printf(print, "\\x%02x", *s); + } + } + mp_printf(print, "%c", quote_char); +} + +#if MICROPY_PY_JSON +void mp_str_print_json(const mp_print_t *print, const byte *str_data, size_t str_len) { + // for JSON spec, see http://www.ietf.org/rfc/rfc4627.txt + // if we are given a valid utf8-encoded string, we will print it in a JSON-conforming way + mp_print_str(print, "\""); + for (const byte *s = str_data, *top = str_data + str_len; s < top; s++) { + if (*s == '"' || *s == '\\') { + mp_printf(print, "\\%c", *s); + } else if (*s >= 32) { + // this will handle normal and utf-8 encoded chars + mp_printf(print, "%c", *s); + } else if (*s == '\n') { + mp_print_str(print, "\\n"); + } else if (*s == '\r') { + mp_print_str(print, "\\r"); + } else if (*s == '\t') { + mp_print_str(print, "\\t"); + } else { + // this will handle control chars + mp_printf(print, "\\u%04x", *s); + } + } + mp_print_str(print, "\""); +} +#endif + +static void str_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + GET_STR_DATA_LEN(self_in, str_data, str_len); + #if MICROPY_PY_JSON + if (kind == PRINT_JSON) { + mp_str_print_json(print, str_data, str_len); + return; + } + #endif + #if !MICROPY_PY_BUILTINS_STR_UNICODE + bool is_bytes = mp_obj_is_type(self_in, &mp_type_bytes); + #else + bool is_bytes = true; + #endif + if (kind == PRINT_RAW || (!MICROPY_PY_BUILTINS_STR_UNICODE && kind == PRINT_STR && !is_bytes)) { + print->print_strn(print->data, (const char *)str_data, str_len); + } else { + if (is_bytes) { + print->print_strn(print->data, "b", 1); + } + mp_str_print_quoted(print, str_data, str_len, is_bytes); + } +} + +mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + #if MICROPY_CPYTHON_COMPAT + if (n_kw != 0) { + mp_arg_error_unimpl_kw(); + } + #endif + + mp_arg_check_num(n_args, n_kw, 0, 3, false); + + switch (n_args) { + case 0: + return MP_OBJ_NEW_QSTR(MP_QSTR_); + + case 1: { + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + mp_obj_print_helper(&print, args[0], PRINT_STR); + return mp_obj_new_str_type_from_vstr(type, &vstr); + } + + default: // 2 or 3 args + // TODO: validate 2nd/3rd args + if (mp_obj_is_type(args[0], &mp_type_bytes)) { + GET_STR_DATA_LEN(args[0], str_data, str_len); + GET_STR_HASH(args[0], str_hash); + if (str_hash == 0) { + str_hash = qstr_compute_hash(str_data, str_len); + } + #if MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + if (!utf8_check(str_data, str_len)) { + mp_raise_msg(&mp_type_UnicodeError, NULL); + } + #endif + + // Check if a qstr with this data already exists + qstr q = qstr_find_strn((const char *)str_data, str_len); + if (q != MP_QSTRnull) { + return MP_OBJ_NEW_QSTR(q); + } + + mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_copy(type, NULL, str_len)); + o->data = str_data; + o->hash = str_hash; + return MP_OBJ_FROM_PTR(o); + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + // This will utf-8 check the input. + return mp_obj_new_str(bufinfo.buf, bufinfo.len); + } + } +} + +static mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + + #if MICROPY_CPYTHON_COMPAT + if (n_kw != 0) { + mp_arg_error_unimpl_kw(); + } + #else + (void)n_kw; + #endif + + if (n_args == 0) { + return mp_const_empty_bytes; + } + + if (mp_obj_is_type(args[0], &mp_type_bytes)) { + return args[0]; + } + + if (mp_obj_is_str(args[0])) { + if (n_args < 2 || n_args > 3) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + goto wrong_args; + #else + mp_raise_TypeError(MP_ERROR_TEXT("string argument without an encoding")); + #endif + } + GET_STR_DATA_LEN(args[0], str_data, str_len); + GET_STR_HASH(args[0], str_hash); + if (str_hash == 0) { + str_hash = qstr_compute_hash(str_data, str_len); + } + mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_copy(&mp_type_bytes, NULL, str_len)); + o->data = str_data; + o->hash = str_hash; + return MP_OBJ_FROM_PTR(o); + } + + if (n_args > 1) { + goto wrong_args; + } + + if (mp_obj_is_small_int(args[0])) { + mp_int_t len = MP_OBJ_SMALL_INT_VALUE(args[0]); + if (len < 0) { + mp_raise_ValueError(NULL); + } + vstr_t vstr; + vstr_init_len(&vstr, len); + memset(vstr.buf, 0, len); + return mp_obj_new_bytes_from_vstr(&vstr); + } + + // check if argument has the buffer protocol + mp_buffer_info_t bufinfo; + if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { + return mp_obj_new_bytes(bufinfo.buf, bufinfo.len); + } + + vstr_t vstr; + // Try to create array of exact len if initializer len is known + mp_obj_t len_in = mp_obj_len_maybe(args[0]); + if (len_in == MP_OBJ_NULL) { + vstr_init(&vstr, 16); + } else { + mp_int_t len = MP_OBJ_SMALL_INT_VALUE(len_in); + vstr_init(&vstr, len); + } + + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(args[0], &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + mp_int_t val = mp_obj_get_int(item); + #if MICROPY_FULL_CHECKS + if (val < 0 || val > 255) { + mp_raise_ValueError(MP_ERROR_TEXT("bytes value out of range")); + } + #endif + vstr_add_byte(&vstr, val); + } + + return mp_obj_new_bytes_from_vstr(&vstr); + +wrong_args: + mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments")); +} + +// like strstr but with specified length and allows \0 bytes +// TODO replace with something more efficient/standard +const byte *find_subbytes(const byte *haystack, size_t hlen, const byte *needle, size_t nlen, int direction) { + if (hlen >= nlen) { + size_t str_index, str_index_end; + if (direction > 0) { + str_index = 0; + str_index_end = hlen - nlen; + } else { + str_index = hlen - nlen; + str_index_end = 0; + } + for (;;) { + if (memcmp(&haystack[str_index], needle, nlen) == 0) { + // found + return haystack + str_index; + } + if (str_index == str_index_end) { + // not found + break; + } + str_index += direction; + } + } + return NULL; +} + +// Note: this function is used to check if an object is a str or bytes, which +// works because both those types use it as their binary_op method. Revisit +// mp_obj_is_str_or_bytes if this fact changes. +mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + // check for modulo + if (op == MP_BINARY_OP_MODULO) { + #if MICROPY_PY_BUILTINS_STR_OP_MODULO + mp_obj_t *args = &rhs_in; + size_t n_args = 1; + mp_obj_t dict = MP_OBJ_NULL; + if (mp_obj_is_type(rhs_in, &mp_type_tuple)) { + // TODO: Support tuple subclasses? + mp_obj_tuple_get(rhs_in, &n_args, &args); + } else if (mp_obj_is_type(rhs_in, &mp_type_dict)) { + dict = rhs_in; + } + return str_modulo_format(lhs_in, n_args, args, dict); + #else + return MP_OBJ_NULL; + #endif + } + + // from now on we need lhs type and data, so extract them + const mp_obj_type_t *lhs_type = mp_obj_get_type(lhs_in); + GET_STR_DATA_LEN(lhs_in, lhs_data, lhs_len); + + // check for multiply + if (op == MP_BINARY_OP_MULTIPLY) { + mp_int_t n; + if (!mp_obj_get_int_maybe(rhs_in, &n)) { + return MP_OBJ_NULL; // op not supported + } + if (n <= 0) { + if (lhs_type == &mp_type_str) { + return MP_OBJ_NEW_QSTR(MP_QSTR_); // empty str + } else { + return mp_const_empty_bytes; + } + } + vstr_t vstr; + vstr_init_len(&vstr, lhs_len * n); + mp_seq_multiply(lhs_data, sizeof(*lhs_data), lhs_len, n, vstr.buf); + return mp_obj_new_str_type_from_vstr(lhs_type, &vstr); + } + + // From now on all operations allow: + // - str with str + // - bytes with bytes + // - bytes with bytearray + // - bytes with array.array + // To do this efficiently we use the buffer protocol to extract the raw + // data for the rhs, but only if the lhs is a bytes object. + // + // NOTE: CPython does not allow comparison between bytes ard array.array + // (even if the array is of type 'b'), even though it allows addition of + // such types. We are not compatible with this (we do allow comparison + // of bytes with anything that has the buffer protocol). It would be + // easy to "fix" this with a bit of extra logic below, but it costs code + // size and execution time so we don't. + + const byte *rhs_data; + size_t rhs_len; + if (lhs_type == mp_obj_get_type(rhs_in)) { + GET_STR_DATA_LEN(rhs_in, rhs_data_, rhs_len_); + rhs_data = rhs_data_; + rhs_len = rhs_len_; + } else if (lhs_type == &mp_type_bytes) { + mp_buffer_info_t bufinfo; + if (!mp_get_buffer(rhs_in, &bufinfo, MP_BUFFER_READ)) { + return MP_OBJ_NULL; // op not supported + } + rhs_data = bufinfo.buf; + rhs_len = bufinfo.len; + } else { + // LHS is str and RHS has an incompatible type + // (except if operation is EQUAL, but that's handled by mp_obj_equal) + + // CONTAINS must fail with a bad-implicit-conversion exception, because + // otherwise mp_binary_op() will fallback to `list(lhs).__contains__(rhs)`. + if (op == MP_BINARY_OP_CONTAINS) { + bad_implicit_conversion(rhs_in); + } + + // All other operations are not supported, and may be handled by another + // type, eg for reverse operations. + return MP_OBJ_NULL; + } + + switch (op) { + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_INPLACE_ADD: { + if (lhs_len == 0 && mp_obj_get_type(rhs_in) == lhs_type) { + return rhs_in; + } + if (rhs_len == 0) { + return lhs_in; + } + + vstr_t vstr; + vstr_init_len(&vstr, lhs_len + rhs_len); + memcpy(vstr.buf, lhs_data, lhs_len); + memcpy(vstr.buf + lhs_len, rhs_data, rhs_len); + return mp_obj_new_str_type_from_vstr(lhs_type, &vstr); + } + + case MP_BINARY_OP_CONTAINS: + return mp_obj_new_bool(find_subbytes(lhs_data, lhs_len, rhs_data, rhs_len, 1) != NULL); + + // case MP_BINARY_OP_NOT_EQUAL: // This is never passed here + case MP_BINARY_OP_EQUAL: // This will be passed only for bytes, str is dealt with in mp_obj_equal() + case MP_BINARY_OP_LESS: + case MP_BINARY_OP_LESS_EQUAL: + case MP_BINARY_OP_MORE: + case MP_BINARY_OP_MORE_EQUAL: + return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_data, lhs_len, rhs_data, rhs_len)); + + default: + return MP_OBJ_NULL; // op not supported + } +} + +#if !MICROPY_PY_BUILTINS_STR_UNICODE +// objstrunicode defines own version +const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, size_t self_len, + mp_obj_t index, bool is_slice) { + size_t index_val = mp_get_index(type, self_len, index, is_slice); + return self_data + index_val; +} +#endif + +// This is used for both bytes and 8-bit strings. This is not used for unicode strings. +static mp_obj_t bytes_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + const mp_obj_type_t *type = mp_obj_get_type(self_in); + GET_STR_DATA_LEN(self_in, self_data, self_len); + if (value == MP_OBJ_SENTINEL) { + // load + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(self_len, index, &slice)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported")); + } + return mp_obj_new_str_of_type(type, self_data + slice.start, slice.stop - slice.start); + } + #endif + size_t index_val = mp_get_index(type, self_len, index, false); + // If we have unicode enabled the type will always be bytes, so take the short cut. + if (MICROPY_PY_BUILTINS_STR_UNICODE || type == &mp_type_bytes) { + return MP_OBJ_NEW_SMALL_INT(self_data[index_val]); + } else { + return mp_obj_new_str_via_qstr((char *)&self_data[index_val], 1); + } + } else { + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) { + check_is_str_or_bytes(self_in); + const mp_obj_type_t *self_type = mp_obj_get_type(self_in); + const mp_obj_type_t *ret_type = self_type; + + // get separation string + GET_STR_DATA_LEN(self_in, sep_str, sep_len); + + // process args + size_t seq_len; + mp_obj_t *seq_items; + + if (!mp_obj_is_type(arg, &mp_type_list) && !mp_obj_is_type(arg, &mp_type_tuple)) { + // arg is not a list nor a tuple, try to convert it to a list + // TODO: Try to optimize? + arg = mp_obj_list_make_new(&mp_type_list, 1, 0, &arg); + } + mp_obj_get_array(arg, &seq_len, &seq_items); + + // count required length + size_t required_len = 0; + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (self_type == &mp_type_bytearray) { + self_type = &mp_type_bytes; + } + #endif + for (size_t i = 0; i < seq_len; i++) { + const mp_obj_type_t *seq_type = mp_obj_get_type(seq_items[i]); + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (seq_type == &mp_type_bytearray) { + seq_type = &mp_type_bytes; + } + #endif + if (seq_type != self_type) { + mp_raise_TypeError( + MP_ERROR_TEXT("join expects a list of str/bytes objects consistent with self object")); + } + if (i > 0) { + required_len += sep_len; + } + GET_STR_LEN(seq_items[i], l); + required_len += l; + } + + // make joined string + vstr_t vstr; + vstr_init_len(&vstr, required_len); + byte *data = (byte *)vstr.buf; + for (size_t i = 0; i < seq_len; i++) { + if (i > 0) { + memcpy(data, sep_str, sep_len); + data += sep_len; + } + GET_STR_DATA_LEN(seq_items[i], s, l); + memcpy(data, s, l); + data += l; + } + + // return joined string + return mp_obj_new_str_type_from_vstr(ret_type, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join); + +mp_obj_t mp_obj_str_split(size_t n_args, const mp_obj_t *args) { + const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); + mp_int_t splits = -1; + mp_obj_t sep = mp_const_none; + if (n_args > 1) { + sep = args[1]; + if (n_args > 2) { + splits = mp_obj_get_int(args[2]); + } + } + + mp_obj_t res = mp_obj_new_list(0, NULL); + GET_STR_DATA_LEN(args[0], s, len); + const byte *top = s + len; + + if (sep == mp_const_none) { + // sep not given, so separate on whitespace + + // Initial whitespace is not counted as split, so we pre-do it + while (s < top && unichar_isspace(*s)) { + s++; + } + while (s < top && splits != 0) { + const byte *start = s; + while (s < top && !unichar_isspace(*s)) { + s++; + } + mp_obj_list_append(res, mp_obj_new_str_of_type(self_type, start, s - start)); + if (s >= top) { + break; + } + while (s < top && unichar_isspace(*s)) { + s++; + } + if (splits > 0) { + splits--; + } + } + + if (s < top) { + mp_obj_list_append(res, mp_obj_new_str_of_type(self_type, s, top - s)); + } + + } else { + // sep given + str_check_arg_type(self_type, sep); + + size_t sep_len; + const char *sep_str = mp_obj_str_get_data(sep, &sep_len); + + if (sep_len == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("empty separator")); + } + + for (;;) { + const byte *start = s; + for (;;) { + if (splits == 0 || s + sep_len > top) { + s = top; + break; + } else if (memcmp(s, sep_str, sep_len) == 0) { + break; + } + s++; + } + mp_obj_list_append(res, mp_obj_new_str_of_type(self_type, start, s - start)); + if (s >= top) { + break; + } + s += sep_len; + if (splits > 0) { + splits--; + } + } + } + + return res; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_split_obj, 1, 3, mp_obj_str_split); + +#if MICROPY_PY_BUILTINS_STR_SPLITLINES +static mp_obj_t str_splitlines(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_keepends }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_keepends, MP_ARG_BOOL, {.u_bool = false} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_type_t *self_type = mp_obj_get_type(pos_args[0]); + mp_obj_t res = mp_obj_new_list(0, NULL); + + GET_STR_DATA_LEN(pos_args[0], s, len); + const byte *top = s + len; + + while (s < top) { + const byte *start = s; + size_t match = 0; + while (s < top) { + if (*s == '\n') { + match = 1; + break; + } else if (*s == '\r') { + if (s[1] == '\n') { + match = 2; + } else { + match = 1; + } + break; + } + s++; + } + size_t sub_len = s - start; + if (args[ARG_keepends].u_bool) { + sub_len += match; + } + mp_obj_list_append(res, mp_obj_new_str_of_type(self_type, start, sub_len)); + s += match; + } + + return res; +} +MP_DEFINE_CONST_FUN_OBJ_KW(str_splitlines_obj, 1, str_splitlines); +#endif + +static mp_obj_t str_rsplit(size_t n_args, const mp_obj_t *args) { + if (n_args < 3) { + // If we don't have split limit, it doesn't matter from which side + // we split. + return mp_obj_str_split(n_args, args); + } + const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); + mp_obj_t sep = args[1]; + GET_STR_DATA_LEN(args[0], s, len); + + mp_int_t splits = mp_obj_get_int(args[2]); + if (splits < 0) { + // Negative limit means no limit, so delegate to split(). + return mp_obj_str_split(n_args, args); + } + + mp_int_t org_splits = splits; + // Preallocate list to the max expected # of elements, as we + // will fill it from the end. + mp_obj_list_t *res = MP_OBJ_TO_PTR(mp_obj_new_list(splits + 1, NULL)); + mp_int_t idx = splits; + + if (sep == mp_const_none) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("rsplit(None,n)")); + } else { + size_t sep_len; + const char *sep_str = mp_obj_str_get_data(sep, &sep_len); + + if (sep_len == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("empty separator")); + } + + const byte *beg = s; + const byte *last = s + len; + for (;;) { + s = last - sep_len; + for (;;) { + if (splits == 0 || s < beg) { + break; + } else if (memcmp(s, sep_str, sep_len) == 0) { + break; + } + s--; + } + if (s < beg || splits == 0) { + res->items[idx] = mp_obj_new_str_of_type(self_type, beg, last - beg); + break; + } + res->items[idx--] = mp_obj_new_str_of_type(self_type, s + sep_len, last - s - sep_len); + last = s; + splits--; + } + if (idx != 0) { + // We split less parts than split limit, now go cleanup surplus + size_t used = org_splits + 1 - idx; + memmove(res->items, &res->items[idx], used * sizeof(mp_obj_t)); + mp_seq_clear(res->items, used, res->alloc, sizeof(*res->items)); + res->len = used; + } + } + + return MP_OBJ_FROM_PTR(res); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rsplit_obj, 1, 3, str_rsplit); + +static mp_obj_t str_finder(size_t n_args, const mp_obj_t *args, int direction, bool is_index) { + const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); + check_is_str_or_bytes(args[0]); + + // check argument type + str_check_arg_type(self_type, args[1]); + + GET_STR_DATA_LEN(args[0], haystack, haystack_len); + GET_STR_DATA_LEN(args[1], needle, needle_len); + + const byte *start = haystack; + const byte *end = haystack + haystack_len; + if (n_args >= 3 && args[2] != mp_const_none) { + start = str_index_to_ptr(self_type, haystack, haystack_len, args[2], true); + } + if (n_args >= 4 && args[3] != mp_const_none) { + end = str_index_to_ptr(self_type, haystack, haystack_len, args[3], true); + } + + if (end < start) { + goto out_error; + } + + const byte *p = find_subbytes(start, end - start, needle, needle_len, direction); + if (p == NULL) { + out_error: + // not found + if (is_index) { + mp_raise_ValueError(MP_ERROR_TEXT("substring not found")); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } + } else { + // found + #if MICROPY_PY_BUILTINS_STR_UNICODE + if (self_type == &mp_type_str) { + return MP_OBJ_NEW_SMALL_INT(utf8_ptr_to_index(haystack, p)); + } + #endif + return MP_OBJ_NEW_SMALL_INT(p - haystack); + } +} + +static mp_obj_t str_find(size_t n_args, const mp_obj_t *args) { + return str_finder(n_args, args, 1, false); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj, 2, 4, str_find); + +static mp_obj_t str_rfind(size_t n_args, const mp_obj_t *args) { + return str_finder(n_args, args, -1, false); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rfind_obj, 2, 4, str_rfind); + +static mp_obj_t str_index(size_t n_args, const mp_obj_t *args) { + return str_finder(n_args, args, 1, true); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_index_obj, 2, 4, str_index); + +static mp_obj_t str_rindex(size_t n_args, const mp_obj_t *args) { + return str_finder(n_args, args, -1, true); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rindex_obj, 2, 4, str_rindex); + +// TODO: (Much) more variety in args +static mp_obj_t str_startswith(size_t n_args, const mp_obj_t *args) { + const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); + GET_STR_DATA_LEN(args[0], str, str_len); + size_t prefix_len; + const char *prefix = mp_obj_str_get_data(args[1], &prefix_len); + const byte *start = str; + if (n_args > 2) { + start = str_index_to_ptr(self_type, str, str_len, args[2], true); + } + if (prefix_len + (start - str) > str_len) { + return mp_const_false; + } + return mp_obj_new_bool(memcmp(start, prefix, prefix_len) == 0); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_startswith_obj, 2, 3, str_startswith); + +static mp_obj_t str_endswith(size_t n_args, const mp_obj_t *args) { + GET_STR_DATA_LEN(args[0], str, str_len); + size_t suffix_len; + const char *suffix = mp_obj_str_get_data(args[1], &suffix_len); + if (n_args > 2) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("start/end indices")); + } + + if (suffix_len > str_len) { + return mp_const_false; + } + return mp_obj_new_bool(memcmp(str + (str_len - suffix_len), suffix, suffix_len) == 0); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj, 2, 3, str_endswith); + +enum { LSTRIP, RSTRIP, STRIP }; + +static mp_obj_t str_uni_strip(int type, size_t n_args, const mp_obj_t *args) { + check_is_str_or_bytes(args[0]); + const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); + + const byte *chars_to_del; + uint chars_to_del_len; + static const byte whitespace[] = " \t\n\r\v\f"; + + if (n_args == 1) { + chars_to_del = whitespace; + chars_to_del_len = sizeof(whitespace) - 1; + } else { + str_check_arg_type(self_type, args[1]); + GET_STR_DATA_LEN(args[1], s, l); + chars_to_del = s; + chars_to_del_len = l; + } + + GET_STR_DATA_LEN(args[0], orig_str, orig_str_len); + + size_t first_good_char_pos = 0; + bool first_good_char_pos_set = false; + size_t last_good_char_pos = 0; + size_t i = 0; + int delta = 1; + if (type == RSTRIP) { + i = orig_str_len - 1; + delta = -1; + } + for (size_t len = orig_str_len; len > 0; len--) { + if (find_subbytes(chars_to_del, chars_to_del_len, &orig_str[i], 1, 1) == NULL) { + if (!first_good_char_pos_set) { + first_good_char_pos_set = true; + first_good_char_pos = i; + if (type == LSTRIP) { + last_good_char_pos = orig_str_len - 1; + break; + } else if (type == RSTRIP) { + first_good_char_pos = 0; + last_good_char_pos = i; + break; + } + } + last_good_char_pos = i; + } + i += delta; + } + + if (!first_good_char_pos_set) { + // string is all whitespace, return '' + if (self_type == &mp_type_str) { + return MP_OBJ_NEW_QSTR(MP_QSTR_); + } else { + return mp_const_empty_bytes; + } + } + + assert(last_good_char_pos >= first_good_char_pos); + // +1 to accommodate the last character + size_t stripped_len = last_good_char_pos - first_good_char_pos + 1; + if (stripped_len == orig_str_len) { + // If nothing was stripped, don't bother to dup original string + // TODO: watch out for this case when we'll get to bytearray.strip() + assert(first_good_char_pos == 0); + return args[0]; + } + return mp_obj_new_str_of_type(self_type, orig_str + first_good_char_pos, stripped_len); +} + +static mp_obj_t str_strip(size_t n_args, const mp_obj_t *args) { + return str_uni_strip(STRIP, n_args, args); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_strip_obj, 1, 2, str_strip); + +static mp_obj_t str_lstrip(size_t n_args, const mp_obj_t *args) { + return str_uni_strip(LSTRIP, n_args, args); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_lstrip_obj, 1, 2, str_lstrip); + +static mp_obj_t str_rstrip(size_t n_args, const mp_obj_t *args) { + return str_uni_strip(RSTRIP, n_args, args); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rstrip_obj, 1, 2, str_rstrip); + +#if MICROPY_PY_BUILTINS_STR_CENTER +static mp_obj_t str_center(mp_obj_t str_in, mp_obj_t width_in) { + GET_STR_DATA_LEN(str_in, str, str_len); + mp_uint_t width = mp_obj_get_int(width_in); + if (str_len >= width) { + return str_in; + } + + vstr_t vstr; + vstr_init_len(&vstr, width); + memset(vstr.buf, ' ', width); + int left = (width - str_len) / 2; + memcpy(vstr.buf + left, str, str_len); + return mp_obj_new_str_type_from_vstr(mp_obj_get_type(str_in), &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_2(str_center_obj, str_center); +#endif + +// Takes an int arg, but only parses unsigned numbers, and only changes +// *num if at least one digit was parsed. +static const char *str_to_int(const char *str, const char *top, int *num) { + if (str < top && '0' <= *str && *str <= '9') { + *num = 0; + do { + *num = *num * 10 + (*str - '0'); + str++; + } + while (str < top && '0' <= *str && *str <= '9'); + } + return str; +} + +static bool isalignment(char ch) { + return ch && strchr("<>=^", ch) != NULL; +} + +static bool istype(char ch) { + return ch && strchr("bcdeEfFgGnosxX%", ch) != NULL; +} + +static bool arg_looks_integer(mp_obj_t arg) { + return mp_obj_is_bool(arg) || mp_obj_is_int(arg); +} + +static bool arg_looks_numeric(mp_obj_t arg) { + return arg_looks_integer(arg) + #if MICROPY_PY_BUILTINS_FLOAT + || mp_obj_is_float(arg) + #endif + ; +} + +#if MICROPY_PY_BUILTINS_STR_OP_MODULO +static mp_obj_t arg_as_int(mp_obj_t arg) { + #if MICROPY_PY_BUILTINS_FLOAT + if (mp_obj_is_float(arg)) { + return mp_obj_new_int_from_float(mp_obj_float_get(arg)); + } + #endif + return arg; +} +#endif + +#if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE +static NORETURN void terse_str_format_value_error(void) { + mp_raise_ValueError(MP_ERROR_TEXT("bad format string")); +} +#else +// define to nothing to improve coverage +#define terse_str_format_value_error() +#endif + +static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *arg_i, size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + + for (; str < top; str++) { + if (*str == '}') { + str++; + if (str < top && *str == '}') { + vstr_add_byte(&vstr, '}'); + continue; + } + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError(MP_ERROR_TEXT("single '}' encountered in format string")); + #endif + } + if (*str != '{') { + vstr_add_byte(&vstr, *str); + continue; + } + + str++; + if (str < top && *str == '{') { + vstr_add_byte(&vstr, '{'); + continue; + } + + // replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}" + + const char *field_name = NULL; + const char *field_name_top = NULL; + char conversion = '\0'; + const char *format_spec = NULL; + + if (str < top && *str != '}' && *str != '!' && *str != ':') { + field_name = (const char *)str; + while (str < top && *str != '}' && *str != '!' && *str != ':') { + ++str; + } + field_name_top = (const char *)str; + } + + // conversion ::= "r" | "s" + + if (str < top && *str == '!') { + str++; + if (str < top && (*str == 'r' || *str == 's')) { + conversion = *str++; + } else { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL + mp_raise_ValueError(MP_ERROR_TEXT("bad conversion specifier")); + #else + if (str >= top) { + mp_raise_ValueError( + MP_ERROR_TEXT("end of format while looking for conversion specifier")); + } else { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("unknown conversion specifier %c"), *str); + } + #endif + } + } + + if (str < top && *str == ':') { + str++; + // {:} is the same as {}, which is the same as {!s} + // This makes a difference when passing in a True or False + // '{}'.format(True) returns 'True' + // '{:d}'.format(True) returns '1' + // So we treat {:} as {} and this later gets treated to be {!s} + if (*str != '}') { + format_spec = str; + for (int nest = 1; str < top;) { + if (*str == '{') { + ++nest; + } else if (*str == '}') { + if (--nest == 0) { + break; + } + } + ++str; + } + } + } + if (str >= top) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError(MP_ERROR_TEXT("unmatched '{' in format")); + #endif + } + if (*str != '}') { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError(MP_ERROR_TEXT("expected ':' after format specifier")); + #endif + } + + mp_obj_t arg = mp_const_none; + + if (field_name) { + int index = 0; + if (MP_LIKELY(unichar_isdigit(*field_name))) { + if (*arg_i > 0) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError( + MP_ERROR_TEXT("can't switch from automatic field numbering to manual field specification")); + #endif + } + field_name = str_to_int(field_name, field_name_top, &index); + if ((uint)index >= n_args - 1) { + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("tuple index out of range")); + } + arg = args[index + 1]; + *arg_i = -1; + } else { + const char *lookup; + for (lookup = field_name; lookup < field_name_top && *lookup != '.' && *lookup != '['; lookup++) {; + } + mp_obj_t field_q = mp_obj_new_str_via_qstr(field_name, lookup - field_name); // should it be via qstr? + field_name = lookup; + mp_map_elem_t *key_elem = mp_map_lookup(kwargs, field_q, MP_MAP_LOOKUP); + if (key_elem == NULL) { + mp_raise_type_arg(&mp_type_KeyError, field_q); + } + arg = key_elem->value; + } + if (field_name < field_name_top) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("attributes not supported")); + } + } else { + if (*arg_i < 0) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError( + MP_ERROR_TEXT("can't switch from manual field specification to automatic field numbering")); + #endif + } + if ((uint)*arg_i >= n_args - 1) { + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("tuple index out of range")); + } + arg = args[(*arg_i) + 1]; + (*arg_i)++; + } + if (!format_spec && !conversion) { + conversion = 's'; + } + if (conversion) { + mp_print_kind_t print_kind; + if (conversion == 's') { + print_kind = PRINT_STR; + } else { + assert(conversion == 'r'); + print_kind = PRINT_REPR; + } + vstr_t arg_vstr; + mp_print_t arg_print; + vstr_init_print(&arg_vstr, 16, &arg_print); + mp_obj_print_helper(&arg_print, arg, print_kind); + arg = mp_obj_new_str_type_from_vstr(&mp_type_str, &arg_vstr); + } + + char fill = '\0'; + char align = '\0'; + int width = -1; + int precision = -1; + char type = '\0'; + int flags = 0; + + if (format_spec) { + // The format specifier (from http://docs.python.org/2/library/string.html#formatspec) + // + // [[fill]align][sign][#][0][width][,][.precision][type] + // fill ::= + // align ::= "<" | ">" | "=" | "^" + // sign ::= "+" | "-" | " " + // width ::= integer + // precision ::= integer + // type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%" + + // recursively call the formatter to format any nested specifiers + MP_STACK_CHECK(); + vstr_t format_spec_vstr = mp_obj_str_format_helper(format_spec, str, arg_i, n_args, args, kwargs); + const char *s = vstr_null_terminated_str(&format_spec_vstr); + const char *stop = s + format_spec_vstr.len; + if (isalignment(*s)) { + align = *s++; + } else if (*s && isalignment(s[1])) { + fill = *s++; + align = *s++; + } + if (*s == '+' || *s == '-' || *s == ' ') { + if (*s == '+') { + flags |= PF_FLAG_SHOW_SIGN; + } else if (*s == ' ') { + flags |= PF_FLAG_SPACE_SIGN; + } + s++; + } + if (*s == '#') { + flags |= PF_FLAG_SHOW_PREFIX; + s++; + } + if (*s == '0') { + if (!align && arg_looks_numeric(arg)) { + align = '='; + } + if (!fill) { + fill = '0'; + } + } + s = str_to_int(s, stop, &width); + if (*s == ',') { + flags |= PF_FLAG_SHOW_COMMA; + s++; + } + if (*s == '.') { + s++; + s = str_to_int(s, stop, &precision); + } + if (istype(*s)) { + type = *s++; + } + if (*s) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError(MP_ERROR_TEXT("invalid format specifier")); + #endif + } + vstr_clear(&format_spec_vstr); + } + if (!align) { + if (arg_looks_numeric(arg)) { + align = '>'; + } else { + align = '<'; + } + } + if (!fill) { + fill = ' '; + } + + if (flags & (PF_FLAG_SHOW_SIGN | PF_FLAG_SPACE_SIGN)) { + if (type == 's') { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError(MP_ERROR_TEXT("sign not allowed in string format specifier")); + #endif + } + if (type == 'c') { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError( + MP_ERROR_TEXT("sign not allowed with integer format specifier 'c'")); + #endif + } + } + + switch (align) { + case '<': + flags |= PF_FLAG_LEFT_ADJUST; + break; + case '=': + flags |= PF_FLAG_PAD_AFTER_SIGN; + break; + case '^': + flags |= PF_FLAG_CENTER_ADJUST; + break; + } + + if (arg_looks_integer(arg)) { + switch (type) { + case 'b': + mp_print_mp_int(&print, arg, 2, 'a', flags, fill, width, 0); + continue; + + case 'c': { + char ch = mp_obj_get_int(arg); + mp_print_strn(&print, &ch, 1, flags, fill, width); + continue; + } + + case '\0': // No explicit format type implies 'd' + case 'n': // I don't think we support locales in uPy so use 'd' + case 'd': + mp_print_mp_int(&print, arg, 10, 'a', flags, fill, width, 0); + continue; + + case 'o': + if (flags & PF_FLAG_SHOW_PREFIX) { + flags |= PF_FLAG_SHOW_OCTAL_LETTER; + } + + mp_print_mp_int(&print, arg, 8, 'a', flags, fill, width, 0); + continue; + + case 'X': + case 'x': + mp_print_mp_int(&print, arg, 16, type - ('X' - 'A'), flags, fill, width, 0); + continue; + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case '%': + // The floating point formatters all work with anything that + // looks like an integer + break; + + default: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("unknown format code '%c' for object of type '%s'"), + type, mp_obj_get_type_str(arg)); + #endif + } + } + + // NOTE: no else here. We need the e, f, g etc formats for integer + // arguments (from above if) to take this if. + if (arg_looks_numeric(arg)) { + if (!type) { + + // Even though the docs say that an unspecified type is the same + // as 'g', there is one subtle difference, when the exponent + // is one less than the precision. + // + // '{:10.1}'.format(0.0) ==> '0e+00' + // '{:10.1g}'.format(0.0) ==> '0' + // + // TODO: Figure out how to deal with this. + // + // A proper solution would involve adding a special flag + // or something to format_float, and create a format_double + // to deal with doubles. In order to fix this when using + // sprintf, we'd need to use the e format and tweak the + // returned result to strip trailing zeros like the g format + // does. + // + // {:10.3} and {:10.2e} with 1.23e2 both produce 1.23e+02 + // but with 1.e2 you get 1e+02 and 1.00e+02 + // + // Stripping the trailing 0's (like g) does would make the + // e format give us the right format. + // + // CPython sources say: + // Omitted type specifier. Behaves in the same way as repr(x) + // and str(x) if no precision is given, else like 'g', but with + // at least one digit after the decimal point. */ + + type = 'g'; + } + if (type == 'n') { + type = 'g'; + } + + switch (type) { + #if MICROPY_PY_BUILTINS_FLOAT + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + mp_print_float(&print, mp_obj_get_float(arg), type, flags, fill, width, precision); + break; + + case '%': + flags |= PF_FLAG_ADD_PERCENT; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + #define F100 100.0F + #else + #define F100 100.0 + #endif + mp_print_float(&print, mp_obj_get_float(arg) * F100, 'f', flags, fill, width, precision); +#undef F100 + break; + #endif + + default: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("unknown format code '%c' for object of type '%s'"), + type, mp_obj_get_type_str(arg)); + #endif + } + } else { + // arg doesn't look like a number + + if (align == '=') { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError( + MP_ERROR_TEXT("'=' alignment not allowed in string format specifier")); + #endif + } + + switch (type) { + case '\0': // no explicit format type implies 's' + case 's': { + size_t slen; + const char *s = mp_obj_str_get_data(arg, &slen); + if (precision < 0) { + precision = slen; + } + if (slen > (size_t)precision) { + slen = precision; + } + mp_print_strn(&print, s, slen, flags, fill, width); + break; + } + + default: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("unknown format code '%c' for object of type '%s'"), + type, mp_obj_get_type_str(arg)); + #endif + } + } + } + + return vstr; +} + +mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + check_is_str_or_bytes(args[0]); + + GET_STR_DATA_LEN(args[0], str, len); + int arg_i = 0; + vstr_t vstr = mp_obj_str_format_helper((const char *)str, (const char *)str + len, &arg_i, n_args, args, kwargs); + return mp_obj_new_str_type_from_vstr(mp_obj_get_type(args[0]), &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_KW(str_format_obj, 1, mp_obj_str_format); + +#if MICROPY_PY_BUILTINS_STR_OP_MODULO +static mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict) { + check_is_str_or_bytes(pattern); + + GET_STR_DATA_LEN(pattern, str, len); + #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_TERSE + const byte *start_str = str; + #endif + bool is_bytes = mp_obj_is_type(pattern, &mp_type_bytes); + size_t arg_i = 0; + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + + for (const byte *top = str + len; str < top; str++) { + mp_obj_t arg = MP_OBJ_NULL; + if (*str != '%') { + vstr_add_byte(&vstr, *str); + continue; + } + if (++str >= top) { + goto incomplete_format; + } + if (*str == '%') { + vstr_add_byte(&vstr, '%'); + continue; + } + + // Dictionary value lookup + if (*str == '(') { + if (dict == MP_OBJ_NULL) { + mp_raise_TypeError(MP_ERROR_TEXT("format needs a dict")); + } + arg_i = 1; // we used up the single dict argument + const byte *key = ++str; + while (*str != ')') { + if (str >= top) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError(MP_ERROR_TEXT("incomplete format key")); + #endif + } + ++str; + } + mp_obj_t k_obj = mp_obj_new_str_via_qstr((const char *)key, str - key); + arg = mp_obj_dict_get(dict, k_obj); + str++; + } + + int flags = 0; + char fill = ' '; + int alt = 0; + while (str < top) { + if (*str == '-') { + flags |= PF_FLAG_LEFT_ADJUST; + } else if (*str == '+') { + flags |= PF_FLAG_SHOW_SIGN; + } else if (*str == ' ') { + flags |= PF_FLAG_SPACE_SIGN; + } else if (*str == '#') { + alt = PF_FLAG_SHOW_PREFIX; + } else if (*str == '0') { + flags |= PF_FLAG_PAD_AFTER_SIGN; + fill = '0'; + } else { + break; + } + str++; + } + // parse width, if it exists + int width = 0; + if (str < top) { + if (*str == '*') { + if (arg_i >= n_args) { + goto not_enough_args; + } + width = mp_obj_get_int(args[arg_i++]); + str++; + } else { + str = (const byte *)str_to_int((const char *)str, (const char *)top, &width); + } + } + int prec = -1; + if (str < top && *str == '.') { + if (++str < top) { + if (*str == '*') { + if (arg_i >= n_args) { + goto not_enough_args; + } + prec = mp_obj_get_int(args[arg_i++]); + str++; + } else { + prec = 0; + str = (const byte *)str_to_int((const char *)str, (const char *)top, &prec); + } + } + } + + if (str >= top) { + incomplete_format: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_ValueError(MP_ERROR_TEXT("incomplete format")); + #endif + } + + // Tuple value lookup + if (arg == MP_OBJ_NULL) { + if (arg_i >= n_args) { + not_enough_args: + mp_raise_TypeError(MP_ERROR_TEXT("format string needs more arguments")); + } + arg = args[arg_i++]; + } + switch (*str) { + case 'c': + if (mp_obj_is_str(arg)) { + size_t slen; + const char *s = mp_obj_str_get_data(arg, &slen); + if (slen != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("%c needs int or char")); + } + mp_print_strn(&print, s, 1, flags, ' ', width); + } else if (arg_looks_integer(arg)) { + char ch = mp_obj_get_int(arg); + mp_print_strn(&print, &ch, 1, flags, ' ', width); + } else { + mp_raise_TypeError(MP_ERROR_TEXT("integer needed")); + } + break; + + case 'd': + case 'i': + case 'u': + mp_print_mp_int(&print, arg_as_int(arg), 10, 'a', flags, fill, width, prec); + break; + + #if MICROPY_PY_BUILTINS_FLOAT + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + mp_print_float(&print, mp_obj_get_float(arg), *str, flags, fill, width, prec); + break; + #endif + + case 'o': + if (alt) { + flags |= (PF_FLAG_SHOW_PREFIX | PF_FLAG_SHOW_OCTAL_LETTER); + } + mp_print_mp_int(&print, arg, 8, 'a', flags, fill, width, prec); + break; + + case 'r': + case 's': { + vstr_t arg_vstr; + mp_print_t arg_print; + vstr_init_print(&arg_vstr, 16, &arg_print); + mp_print_kind_t print_kind = (*str == 'r' ? PRINT_REPR : PRINT_STR); + if (print_kind == PRINT_STR && is_bytes && mp_obj_is_type(arg, &mp_type_bytes)) { + // If we have something like b"%s" % b"1", bytes arg should be + // printed undecorated. + print_kind = PRINT_RAW; + } + mp_obj_print_helper(&arg_print, arg, print_kind); + uint vlen = arg_vstr.len; + if (prec < 0) { + prec = vlen; + } + if (vlen > (uint)prec) { + vlen = prec; + } + mp_print_strn(&print, arg_vstr.buf, vlen, flags, ' ', width); + vstr_clear(&arg_vstr); + break; + } + + case 'X': + case 'x': + mp_print_mp_int(&print, arg, 16, *str - ('X' - 'A'), flags | alt, fill, width, prec); + break; + + default: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + terse_str_format_value_error(); + #else + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("unsupported format character '%c' (0x%x) at index %d"), + *str, *str, str - start_str); + #endif + } + } + + if (dict == MP_OBJ_NULL && arg_i != n_args) { + // NOTE: if `dict` exists, then `n_args` is 1 and the dict is always consumed; either + // positionally, or as a map of named args, even if none were actually referenced. + mp_raise_TypeError(MP_ERROR_TEXT("format string didn't convert all arguments")); + } + + return mp_obj_new_str_type_from_vstr(is_bytes ? &mp_type_bytes : &mp_type_str, &vstr); +} +#endif + +// The implementation is optimized, returning the original string if there's +// nothing to replace. +static mp_obj_t str_replace(size_t n_args, const mp_obj_t *args) { + check_is_str_or_bytes(args[0]); + + mp_int_t max_rep = -1; + if (n_args == 4) { + max_rep = mp_obj_get_int(args[3]); + if (max_rep == 0) { + return args[0]; + } else if (max_rep < 0) { + max_rep = -1; + } + } + + // if max_rep is still -1 by this point we will need to do all possible replacements + + // check argument types + + const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); + + str_check_arg_type(self_type, args[1]); + str_check_arg_type(self_type, args[2]); + + // extract string data + + GET_STR_DATA_LEN(args[0], str, str_len); + GET_STR_DATA_LEN(args[1], old, old_len); + GET_STR_DATA_LEN(args[2], new, new_len); + + // old won't exist in str if it's longer, so nothing to replace + if (old_len > str_len) { + return args[0]; + } + + // data for the replaced string + byte *data = NULL; + vstr_t vstr; + + // do 2 passes over the string: + // first pass computes the required length of the replaced string + // second pass does the replacements + for (;;) { + size_t replaced_str_index = 0; + size_t num_replacements_done = 0; + const byte *old_occurrence; + const byte *offset_ptr = str; + size_t str_len_remain = str_len; + if (old_len == 0) { + // if old_str is empty, copy new_str to start of replaced string + // copy the replacement string + if (data != NULL) { + memcpy(data, new, new_len); + } + replaced_str_index += new_len; + num_replacements_done++; + } + while (num_replacements_done != (size_t)max_rep && str_len_remain > 0 && (old_occurrence = find_subbytes(offset_ptr, str_len_remain, old, old_len, 1)) != NULL) { + if (old_len == 0) { + old_occurrence += 1; + } + // copy from just after end of last occurrence of to-be-replaced string to right before start of next occurrence + if (data != NULL) { + memcpy(data + replaced_str_index, offset_ptr, old_occurrence - offset_ptr); + } + replaced_str_index += old_occurrence - offset_ptr; + // copy the replacement string + if (data != NULL) { + memcpy(data + replaced_str_index, new, new_len); + } + replaced_str_index += new_len; + offset_ptr = old_occurrence + old_len; + str_len_remain = str + str_len - offset_ptr; + num_replacements_done++; + } + + // copy from just after end of last occurrence of to-be-replaced string to end of old string + if (data != NULL) { + memcpy(data + replaced_str_index, offset_ptr, str_len_remain); + } + replaced_str_index += str_len_remain; + + if (data == NULL) { + // first pass + if (num_replacements_done == 0) { + // no substr found, return original string + return args[0]; + } else { + // substr found, allocate new string + vstr_init_len(&vstr, replaced_str_index); + data = (byte *)vstr.buf; + assert(data != NULL); + } + } else { + // second pass, we are done + break; + } + } + + return mp_obj_new_str_type_from_vstr(self_type, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_replace_obj, 3, 4, str_replace); + +#if MICROPY_PY_BUILTINS_STR_COUNT +static mp_obj_t str_count(size_t n_args, const mp_obj_t *args) { + const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); + check_is_str_or_bytes(args[0]); + + // check argument type + str_check_arg_type(self_type, args[1]); + + GET_STR_DATA_LEN(args[0], haystack, haystack_len); + GET_STR_DATA_LEN(args[1], needle, needle_len); + + const byte *start = haystack; + const byte *end = haystack + haystack_len; + if (n_args >= 3 && args[2] != mp_const_none) { + start = str_index_to_ptr(self_type, haystack, haystack_len, args[2], true); + } + if (n_args >= 4 && args[3] != mp_const_none) { + end = str_index_to_ptr(self_type, haystack, haystack_len, args[3], true); + } + + // if needle_len is zero then we count each gap between characters as an occurrence + if (needle_len == 0) { + return MP_OBJ_NEW_SMALL_INT(utf8_charlen(start, end - start) + 1); + } + + bool is_str = self_type == &mp_type_str; + + // count the occurrences + mp_int_t num_occurrences = 0; + for (const byte *haystack_ptr = start; haystack_ptr + needle_len <= end;) { + if (memcmp(haystack_ptr, needle, needle_len) == 0) { + num_occurrences++; + haystack_ptr += needle_len; + } else { + haystack_ptr = is_str ? utf8_next_char(haystack_ptr) : haystack_ptr + 1; + } + } + + return MP_OBJ_NEW_SMALL_INT(num_occurrences); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_count_obj, 2, 4, str_count); +#endif + +#if MICROPY_PY_BUILTINS_STR_PARTITION +static mp_obj_t str_partitioner(mp_obj_t self_in, mp_obj_t arg, int direction) { + check_is_str_or_bytes(self_in); + const mp_obj_type_t *self_type = mp_obj_get_type(self_in); + str_check_arg_type(self_type, arg); + + GET_STR_DATA_LEN(self_in, str, str_len); + GET_STR_DATA_LEN(arg, sep, sep_len); + + if (sep_len == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("empty separator")); + } + + mp_obj_t result[3]; + if (self_type == &mp_type_str) { + result[0] = MP_OBJ_NEW_QSTR(MP_QSTR_); + result[1] = MP_OBJ_NEW_QSTR(MP_QSTR_); + result[2] = MP_OBJ_NEW_QSTR(MP_QSTR_); + } else { + result[0] = mp_const_empty_bytes; + result[1] = mp_const_empty_bytes; + result[2] = mp_const_empty_bytes; + } + + if (direction > 0) { + result[0] = self_in; + } else { + result[2] = self_in; + } + + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (mp_obj_get_type(arg) != self_type) { + arg = mp_obj_new_str_of_type(self_type, sep, sep_len); + } + #endif + + const byte *position_ptr = find_subbytes(str, str_len, sep, sep_len, direction); + if (position_ptr != NULL) { + size_t position = position_ptr - str; + result[0] = mp_obj_new_str_of_type(self_type, str, position); + result[1] = arg; + result[2] = mp_obj_new_str_of_type(self_type, str + position + sep_len, str_len - position - sep_len); + } + + return mp_obj_new_tuple(3, result); +} + +static mp_obj_t str_partition(mp_obj_t self_in, mp_obj_t arg) { + return str_partitioner(self_in, arg, 1); +} +MP_DEFINE_CONST_FUN_OBJ_2(str_partition_obj, str_partition); + +static mp_obj_t str_rpartition(mp_obj_t self_in, mp_obj_t arg) { + return str_partitioner(self_in, arg, -1); +} +MP_DEFINE_CONST_FUN_OBJ_2(str_rpartition_obj, str_rpartition); +#endif + +// Supposedly not too critical operations, so optimize for code size +static mp_obj_t str_caseconv(unichar (*op)(unichar), mp_obj_t self_in) { + GET_STR_DATA_LEN(self_in, self_data, self_len); + vstr_t vstr; + vstr_init_len(&vstr, self_len); + byte *data = (byte *)vstr.buf; + for (size_t i = 0; i < self_len; i++) { + *data++ = op(*self_data++); + } + return mp_obj_new_str_type_from_vstr(mp_obj_get_type(self_in), &vstr); +} + +static mp_obj_t str_lower(mp_obj_t self_in) { + return str_caseconv(unichar_tolower, self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(str_lower_obj, str_lower); + +static mp_obj_t str_upper(mp_obj_t self_in) { + return str_caseconv(unichar_toupper, self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(str_upper_obj, str_upper); + +static mp_obj_t str_uni_istype(bool (*f)(unichar), mp_obj_t self_in) { + GET_STR_DATA_LEN(self_in, self_data, self_len); + + if (self_len == 0) { + return mp_const_false; // default to False for empty str + } + + if (f != unichar_isupper && f != unichar_islower) { + for (size_t i = 0; i < self_len; i++) { + if (!f(*self_data++)) { + return mp_const_false; + } + } + } else { + bool contains_alpha = false; + + for (size_t i = 0; i < self_len; i++) { // only check alphanumeric characters + if (unichar_isalpha(*self_data++)) { + contains_alpha = true; + if (!f(*(self_data - 1))) { // -1 because we already incremented above + return mp_const_false; + } + } + } + + if (!contains_alpha) { + return mp_const_false; + } + } + + return mp_const_true; +} + +static mp_obj_t str_isspace(mp_obj_t self_in) { + return str_uni_istype(unichar_isspace, self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(str_isspace_obj, str_isspace); + +static mp_obj_t str_isalpha(mp_obj_t self_in) { + return str_uni_istype(unichar_isalpha, self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(str_isalpha_obj, str_isalpha); + +static mp_obj_t str_isdigit(mp_obj_t self_in) { + return str_uni_istype(unichar_isdigit, self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(str_isdigit_obj, str_isdigit); + +static mp_obj_t str_isupper(mp_obj_t self_in) { + return str_uni_istype(unichar_isupper, self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(str_isupper_obj, str_isupper); + +static mp_obj_t str_islower(mp_obj_t self_in) { + return str_uni_istype(unichar_islower, self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(str_islower_obj, str_islower); + +#if MICROPY_CPYTHON_COMPAT +// These methods are superfluous in the presence of str() and bytes() +// constructors. +// TODO: should accept kwargs too +static mp_obj_t bytes_decode(size_t n_args, const mp_obj_t *args) { + mp_obj_t new_args[2]; + if (n_args == 1) { + new_args[0] = args[0]; + new_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_utf_hyphen_8); + args = new_args; + n_args++; + } + return mp_obj_str_make_new(&mp_type_str, n_args, 0, args); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bytes_decode_obj, 1, 3, bytes_decode); + +// TODO: should accept kwargs too +static mp_obj_t str_encode(size_t n_args, const mp_obj_t *args) { + mp_obj_t new_args[2]; + if (n_args == 1) { + new_args[0] = args[0]; + new_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_utf_hyphen_8); + args = new_args; + n_args++; + } + return bytes_make_new(NULL, n_args, 0, args); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_encode_obj, 1, 3, str_encode); +#endif + +#if MICROPY_PY_BUILTINS_BYTES_HEX +mp_obj_t mp_obj_bytes_hex(size_t n_args, const mp_obj_t *args, const mp_obj_type_t *type) { + // First argument is the data to convert. + // Second argument is an optional separator to be used between values. + const char *sep = NULL; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + + // Code below assumes non-zero buffer length when computing size with + // separator, so handle the zero-length case here. + if (bufinfo.len == 0) { + return mp_const_empty_bytes; + } + + vstr_t vstr; + size_t out_len = bufinfo.len * 2; + if (n_args > 1) { + // 1-char separator between hex numbers + out_len += bufinfo.len - 1; + sep = mp_obj_str_get_str(args[1]); + } + vstr_init_len(&vstr, out_len); + byte *in = bufinfo.buf, *out = (byte *)vstr.buf; + for (mp_uint_t i = bufinfo.len; i--;) { + byte d = (*in >> 4); + if (d > 9) { + d += 'a' - '9' - 1; + } + *out++ = d + '0'; + d = (*in++ & 0xf); + if (d > 9) { + d += 'a' - '9' - 1; + } + *out++ = d + '0'; + if (sep != NULL && i != 0) { + *out++ = *sep; + } + } + return mp_obj_new_str_type_from_vstr(type, &vstr); +} + +mp_obj_t mp_obj_bytes_fromhex(mp_obj_t type_in, mp_obj_t data) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); + + if ((bufinfo.len & 1) != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("odd-length string")); + } + vstr_t vstr; + vstr_init_len(&vstr, bufinfo.len / 2); + byte *in = bufinfo.buf, *out = (byte *)vstr.buf; + byte hex_byte = 0; + for (mp_uint_t i = bufinfo.len; i--;) { + byte hex_ch = *in++; + if (unichar_isxdigit(hex_ch)) { + hex_byte += unichar_xdigit_value(hex_ch); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("non-hex digit found")); + } + if (i & 1) { + hex_byte <<= 4; + } else { + *out++ = hex_byte; + hex_byte = 0; + } + } + return mp_obj_new_str_type_from_vstr(MP_OBJ_TO_PTR(type_in), &vstr); +} + +static mp_obj_t bytes_hex_as_str(size_t n_args, const mp_obj_t *args) { + return mp_obj_bytes_hex(n_args, args, &mp_type_str); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bytes_hex_as_str_obj, 1, 2, bytes_hex_as_str); + +static MP_DEFINE_CONST_FUN_OBJ_2(bytes_fromhex_obj, mp_obj_bytes_fromhex); +static MP_DEFINE_CONST_CLASSMETHOD_OBJ(bytes_fromhex_classmethod_obj, MP_ROM_PTR(&bytes_fromhex_obj)); +#endif // MICROPY_PY_BUILTINS_BYTES_HEX + +mp_int_t mp_obj_str_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + if (flags == MP_BUFFER_READ) { + GET_STR_DATA_LEN(self_in, str_data, str_len); + bufinfo->buf = (void *)str_data; + bufinfo->len = str_len; + bufinfo->typecode = 'B'; // bytes should be unsigned, so should unicode byte-access + return 0; + } else { + // can't write to a string + return 1; + } +} + +void mp_obj_str_set_data(mp_obj_str_t *str, const byte *data, size_t len) { + str->data = data; + str->len = len; + str->hash = qstr_compute_hash(data, len); +} + +// This locals table is used for the following types: str, bytes, bytearray, array.array. +// Each type takes a different section (start to end offset) of this table. +static const mp_rom_map_elem_t array_bytearray_str_bytes_locals_table[] = { + #if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&mp_obj_array_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&mp_obj_array_extend_obj) }, + #endif + #if MICROPY_PY_BUILTINS_BYTES_HEX + { MP_ROM_QSTR(MP_QSTR_hex), MP_ROM_PTR(&bytes_hex_as_str_obj) }, + { MP_ROM_QSTR(MP_QSTR_fromhex), MP_ROM_PTR(&bytes_fromhex_classmethod_obj) }, + #endif + #if MICROPY_CPYTHON_COMPAT + { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&bytes_decode_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&str_find_obj) }, + { MP_ROM_QSTR(MP_QSTR_rfind), MP_ROM_PTR(&str_rfind_obj) }, + { MP_ROM_QSTR(MP_QSTR_index), MP_ROM_PTR(&str_index_obj) }, + { MP_ROM_QSTR(MP_QSTR_rindex), MP_ROM_PTR(&str_rindex_obj) }, + { MP_ROM_QSTR(MP_QSTR_join), MP_ROM_PTR(&str_join_obj) }, + { MP_ROM_QSTR(MP_QSTR_split), MP_ROM_PTR(&str_split_obj) }, + #if MICROPY_PY_BUILTINS_STR_SPLITLINES + { MP_ROM_QSTR(MP_QSTR_splitlines), MP_ROM_PTR(&str_splitlines_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_rsplit), MP_ROM_PTR(&str_rsplit_obj) }, + { MP_ROM_QSTR(MP_QSTR_startswith), MP_ROM_PTR(&str_startswith_obj) }, + { MP_ROM_QSTR(MP_QSTR_endswith), MP_ROM_PTR(&str_endswith_obj) }, + { MP_ROM_QSTR(MP_QSTR_strip), MP_ROM_PTR(&str_strip_obj) }, + { MP_ROM_QSTR(MP_QSTR_lstrip), MP_ROM_PTR(&str_lstrip_obj) }, + { MP_ROM_QSTR(MP_QSTR_rstrip), MP_ROM_PTR(&str_rstrip_obj) }, + { MP_ROM_QSTR(MP_QSTR_format), MP_ROM_PTR(&str_format_obj) }, + { MP_ROM_QSTR(MP_QSTR_replace), MP_ROM_PTR(&str_replace_obj) }, + #if MICROPY_PY_BUILTINS_STR_COUNT + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&str_count_obj) }, + #endif + #if MICROPY_PY_BUILTINS_STR_PARTITION + { MP_ROM_QSTR(MP_QSTR_partition), MP_ROM_PTR(&str_partition_obj) }, + { MP_ROM_QSTR(MP_QSTR_rpartition), MP_ROM_PTR(&str_rpartition_obj) }, + #endif + #if MICROPY_PY_BUILTINS_STR_CENTER + { MP_ROM_QSTR(MP_QSTR_center), MP_ROM_PTR(&str_center_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_lower), MP_ROM_PTR(&str_lower_obj) }, + { MP_ROM_QSTR(MP_QSTR_upper), MP_ROM_PTR(&str_upper_obj) }, + { MP_ROM_QSTR(MP_QSTR_isspace), MP_ROM_PTR(&str_isspace_obj) }, + { MP_ROM_QSTR(MP_QSTR_isalpha), MP_ROM_PTR(&str_isalpha_obj) }, + { MP_ROM_QSTR(MP_QSTR_isdigit), MP_ROM_PTR(&str_isdigit_obj) }, + { MP_ROM_QSTR(MP_QSTR_isupper), MP_ROM_PTR(&str_isupper_obj) }, + { MP_ROM_QSTR(MP_QSTR_islower), MP_ROM_PTR(&str_islower_obj) }, + #if MICROPY_CPYTHON_COMPAT + { MP_ROM_QSTR(MP_QSTR_encode), MP_ROM_PTR(&str_encode_obj) }, + #endif +}; + +#if MICROPY_CPYTHON_COMPAT +#define TABLE_ENTRIES_COMPAT 1 +#else +#define TABLE_ENTRIES_COMPAT 0 +#endif + +#if MICROPY_PY_BUILTINS_BYTES_HEX +#define TABLE_ENTRIES_HEX 2 +#else +#define TABLE_ENTRIES_HEX 0 +#endif + +#if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY +#define TABLE_ENTRIES_ARRAY 2 +#else +#define TABLE_ENTRIES_ARRAY 0 +#endif + +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_str_locals_dict, + array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_HEX + TABLE_ENTRIES_COMPAT, + MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - (TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_HEX + TABLE_ENTRIES_COMPAT)); + +#if TABLE_ENTRIES_COMPAT == 0 +#define mp_obj_bytes_locals_dict mp_obj_str_locals_dict +#else +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_bytes_locals_dict, + array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY, + MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - (TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_COMPAT)); +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_bytearray_locals_dict, + array_bytearray_str_bytes_locals_table, + MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - TABLE_ENTRIES_COMPAT); +#endif + +#if MICROPY_PY_ARRAY +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_array_locals_dict, + array_bytearray_str_bytes_locals_table, + TABLE_ENTRIES_ARRAY); +#endif + +#if MICROPY_PY_BUILTINS_MEMORYVIEW && MICROPY_PY_BUILTINS_BYTES_HEX +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_memoryview_locals_dict, + array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY, + 1); // Just the "hex" entry. +#endif + +#if !MICROPY_PY_BUILTINS_STR_UNICODE +static mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_str, + MP_QSTR_str, + MP_TYPE_FLAG_NONE, + make_new, mp_obj_str_make_new, + print, str_print, + binary_op, mp_obj_str_binary_op, + subscr, bytes_subscr, + iter, mp_obj_new_str_iterator, + buffer, mp_obj_str_get_buffer, + locals_dict, &mp_obj_str_locals_dict + ); +#endif // !MICROPY_PY_BUILTINS_STR_UNICODE + +// Reuses most methods from str +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bytes, + MP_QSTR_bytes, + MP_TYPE_FLAG_NONE, + make_new, bytes_make_new, + print, str_print, + binary_op, mp_obj_str_binary_op, + subscr, bytes_subscr, + iter, mp_obj_new_bytes_iterator, + buffer, mp_obj_str_get_buffer, + locals_dict, &mp_obj_bytes_locals_dict + ); + +// The zero-length bytes object, with data that includes a null-terminating byte +const mp_obj_str_t mp_const_empty_bytes_obj = {{&mp_type_bytes}, 0, 0, (const byte *)""}; + +// Create a str/bytes object using the given data. New memory is allocated and +// the data is copied across. This function should only be used if the type is bytes, +// or if the type is str and the string data is known to be not interned. +mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte *data, size_t len) { + mp_obj_str_t *o = mp_obj_malloc(mp_obj_str_t, type); + o->len = len; + if (data) { + o->hash = qstr_compute_hash(data, len); + byte *p = m_new(byte, len + 1); + o->data = p; + memcpy(p, data, len * sizeof(byte)); + p[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings + } + return MP_OBJ_FROM_PTR(o); +} + +// Create a str/bytes object using the given data. If the type is str and the string +// data is already interned, then a qstr object is returned. Otherwise new memory is +// allocated for the object and the data is copied across. +mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte *data, size_t len) { + if (type == &mp_type_str) { + return mp_obj_new_str((const char *)data, len); + #if MICROPY_PY_BUILTINS_BYTEARRAY + } else if (type == &mp_type_bytearray) { + return mp_obj_new_bytearray(len, data); + #endif + } else { + return mp_obj_new_bytes(data, len); + } +} + +// Create a str using a qstr to store the data; may use existing or new qstr. +mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len) { + return MP_OBJ_NEW_QSTR(qstr_from_strn(data, len)); +} + +// Create a str/bytes object from the given vstr. The vstr buffer is resized to +// the exact length required and then reused for the str/bytes object. The vstr +// is cleared and can safely be passed to vstr_free if it was heap allocated. +static mp_obj_t mp_obj_new_str_type_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) { + // if not a bytes object, look if a qstr with this data already exists + if (type == &mp_type_str) { + qstr q = qstr_find_strn(vstr->buf, vstr->len); + if (q != MP_QSTRnull) { + vstr_clear(vstr); + vstr->alloc = 0; + return MP_OBJ_NEW_QSTR(q); + } + } + + byte *data; + if (vstr->len + 1 == vstr->alloc) { + data = (byte *)vstr->buf; + } else { + data = (byte *)m_renew(char, vstr->buf, vstr->alloc, vstr->len + 1); + } + data[vstr->len] = '\0'; // add null byte + vstr->buf = NULL; + vstr->alloc = 0; + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (type == &mp_type_bytearray) { + return mp_obj_new_bytearray_by_ref(vstr->len, data); + } + #endif + mp_obj_str_t *o = mp_obj_malloc(mp_obj_str_t, type); + o->len = vstr->len; + o->hash = qstr_compute_hash(data, vstr->len); + o->data = data; + return MP_OBJ_FROM_PTR(o); +} + +mp_obj_t mp_obj_new_str_from_vstr(vstr_t *vstr) { + #if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + if (!utf8_check((byte *)vstr->buf, vstr->len)) { + mp_raise_msg(&mp_type_UnicodeError, NULL); + } + #endif // MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + return mp_obj_new_str_type_from_vstr(&mp_type_str, vstr); +} + +#if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK +mp_obj_t mp_obj_new_str_from_utf8_vstr(vstr_t *vstr) { + // bypasses utf8_check. + return mp_obj_new_str_type_from_vstr(&mp_type_str, vstr); +} +#endif // MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + +mp_obj_t mp_obj_new_bytes_from_vstr(vstr_t *vstr) { + return mp_obj_new_str_type_from_vstr(&mp_type_bytes, vstr); +} + +mp_obj_t mp_obj_new_str(const char *data, size_t len) { + #if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + if (!utf8_check((byte *)data, len)) { + mp_raise_msg(&mp_type_UnicodeError, NULL); + } + #endif + qstr q = qstr_find_strn(data, len); + if (q != MP_QSTRnull) { + // qstr with this data already exists + return MP_OBJ_NEW_QSTR(q); + } else { + // no existing qstr, don't make one + return mp_obj_new_str_copy(&mp_type_str, (const byte *)data, len); + } +} + +mp_obj_t mp_obj_str_intern(mp_obj_t str) { + GET_STR_DATA_LEN(str, data, len); + return mp_obj_new_str_via_qstr((const char *)data, len); +} + +mp_obj_t mp_obj_str_intern_checked(mp_obj_t obj) { + size_t len; + const char *data = mp_obj_str_get_data(obj, &len); + return mp_obj_new_str_via_qstr((const char *)data, len); +} + +mp_obj_t mp_obj_new_bytes(const byte *data, size_t len) { + return mp_obj_new_str_copy(&mp_type_bytes, data, len); +} + +bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) { + if (mp_obj_is_qstr(s1) && mp_obj_is_qstr(s2)) { + return s1 == s2; + } else { + GET_STR_HASH(s1, h1); + GET_STR_HASH(s2, h2); + // If any of hashes is 0, it means it's not valid + if (h1 != 0 && h2 != 0 && h1 != h2) { + return false; + } + GET_STR_DATA_LEN(s1, d1, l1); + GET_STR_DATA_LEN(s2, d2, l2); + if (l1 != l2) { + return false; + } + return memcmp(d1, d2, l1) == 0; + } +} + +static NORETURN void bad_implicit_conversion(mp_obj_t self_in) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("can't convert to str implicitly")); + #else + const qstr src_name = mp_obj_get_type(self_in)->name; + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert '%q' object to %q implicitly"), + src_name, src_name == MP_QSTR_str ? MP_QSTR_bytes : MP_QSTR_str); + #endif +} + +// use this if you will anyway convert the string to a qstr +// will be more efficient for the case where it's already a qstr +qstr mp_obj_str_get_qstr(mp_obj_t self_in) { + if (mp_obj_is_qstr(self_in)) { + return MP_OBJ_QSTR_VALUE(self_in); + } else if (mp_obj_is_exact_type(self_in, &mp_type_str)) { + mp_obj_str_t *self = MP_OBJ_TO_PTR(self_in); + return qstr_from_strn((char *)self->data, self->len); + } else { + bad_implicit_conversion(self_in); + } +} + +// only use this function if you need the str data to be zero terminated +// at the moment all strings are zero terminated to help with C ASCIIZ compatibility +const char *mp_obj_str_get_str(mp_obj_t self_in) { + if (mp_obj_is_str_or_bytes(self_in)) { + GET_STR_DATA_LEN(self_in, s, l); + (void)l; // len unused + return (const char *)s; + } else { + bad_implicit_conversion(self_in); + } +} + +const char *mp_obj_str_get_data(mp_obj_t self_in, size_t *len) { + if (mp_obj_is_str_or_bytes(self_in)) { + GET_STR_DATA_LEN(self_in, s, l); + *len = l; + return (const char *)s; + } else { + bad_implicit_conversion(self_in); + } +} + +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +const byte *mp_obj_str_get_data_no_check(mp_obj_t self_in, size_t *len) { + if (mp_obj_is_qstr(self_in)) { + return qstr_data(MP_OBJ_QSTR_VALUE(self_in), len); + } else { + MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE; + *len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(self_in))->len; + return ((mp_obj_str_t *)MP_OBJ_TO_PTR(self_in))->data; + } +} +#endif + +/******************************************************************************/ +/* str iterator */ + +typedef struct _mp_obj_str8_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_t str; + size_t cur; +} mp_obj_str8_it_t; + +#if !MICROPY_PY_BUILTINS_STR_UNICODE +static mp_obj_t str_it_iternext(mp_obj_t self_in) { + mp_obj_str8_it_t *self = MP_OBJ_TO_PTR(self_in); + GET_STR_DATA_LEN(self->str, str, len); + if (self->cur < len) { + mp_obj_t o_out = mp_obj_new_str_via_qstr((const char *)str + self->cur, 1); + self->cur += 1; + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +static mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_str8_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_str8_it_t *o = (mp_obj_str8_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = str_it_iternext; + o->str = str; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} +#endif + +static mp_obj_t bytes_it_iternext(mp_obj_t self_in) { + mp_obj_str8_it_t *self = MP_OBJ_TO_PTR(self_in); + GET_STR_DATA_LEN(self->str, str, len); + if (self->cur < len) { + mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT(str[self->cur]); + self->cur += 1; + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_str8_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_str8_it_t *o = (mp_obj_str8_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = bytes_it_iternext; + o->str = str; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objstr.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objstr.h new file mode 100644 index 00000000..028fc959 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objstr.h @@ -0,0 +1,122 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJSTR_H +#define MICROPY_INCLUDED_PY_OBJSTR_H + +#include "py/obj.h" +#include "py/objarray.h" + +typedef struct _mp_obj_str_t { + mp_obj_base_t base; + size_t hash; + // len == number of bytes used in data, alloc = len + 1 because (at the moment) we also append a null byte + size_t len; + const byte *data; +} mp_obj_str_t; + +// This static assert is used to ensure that mp_obj_str_t and mp_obj_array_t are compatible, +// meaning that their len and data/items entries are at the same offsets in the struct. +// This allows the same code to be used for str/bytes and bytearray. +#define MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE \ + MP_STATIC_ASSERT(offsetof(mp_obj_str_t, len) == offsetof(mp_obj_array_t, len) \ + && offsetof(mp_obj_str_t, data) == offsetof(mp_obj_array_t, items)) + +#define MP_DEFINE_STR_OBJ(obj_name, str) mp_obj_str_t obj_name = {{&mp_type_str}, 0, sizeof(str) - 1, (const byte *)str} + +// use this macro to extract the string hash +// warning: the hash can be 0, meaning invalid, and must then be explicitly computed from the data +#define GET_STR_HASH(str_obj_in, str_hash) \ + size_t str_hash; \ + if (mp_obj_is_qstr(str_obj_in)) { \ + str_hash = qstr_hash(MP_OBJ_QSTR_VALUE(str_obj_in)); \ + } else { \ + str_hash = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->hash; \ + } + +// use this macro to extract the string length +#define GET_STR_LEN(str_obj_in, str_len) \ + size_t str_len; \ + if (mp_obj_is_qstr(str_obj_in)) { \ + str_len = qstr_len(MP_OBJ_QSTR_VALUE(str_obj_in)); \ + } else { \ + str_len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->len; \ + } + +// use this macro to extract the string data and length +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +const byte *mp_obj_str_get_data_no_check(mp_obj_t self_in, size_t *len); +#define GET_STR_DATA_LEN(str_obj_in, str_data, str_len) \ + size_t str_len; \ + const byte *str_data = mp_obj_str_get_data_no_check(str_obj_in, &str_len); +#else +#define GET_STR_DATA_LEN(str_obj_in, str_data, str_len) \ + const byte *str_data; \ + size_t str_len; \ + if (mp_obj_is_qstr(str_obj_in)) { \ + str_data = qstr_data(MP_OBJ_QSTR_VALUE(str_obj_in), &str_len); \ + } else { \ + MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE; \ + str_len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->len; \ + str_data = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->data; \ + } +#endif + +mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); +void mp_str_print_json(const mp_print_t *print, const byte *str_data, size_t str_len); +mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); +mp_obj_t mp_obj_str_split(size_t n_args, const mp_obj_t *args); +mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte *data, size_t len); // for type=str, input data must be valid utf-8 +mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte *data, size_t len); // for type=str, will check utf-8 (raises UnicodeError) + +mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); +mp_int_t mp_obj_str_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); + +void mp_obj_str_set_data(mp_obj_str_t *str, const byte *data, size_t len); + +const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, size_t self_len, + mp_obj_t index, bool is_slice); +const byte *find_subbytes(const byte *haystack, size_t hlen, const byte *needle, size_t nlen, int direction); + +#define MP_DEFINE_BYTES_OBJ(obj_name, target, len) mp_obj_str_t obj_name = {{&mp_type_bytes}, 0, (len), (const byte *)(target)} + +mp_obj_t mp_obj_bytes_hex(size_t n_args, const mp_obj_t *args, const mp_obj_type_t *type); +mp_obj_t mp_obj_bytes_fromhex(mp_obj_t type_in, mp_obj_t data); + +extern const mp_obj_dict_t mp_obj_str_locals_dict; + +#if MICROPY_PY_BUILTINS_MEMORYVIEW && MICROPY_PY_BUILTINS_BYTES_HEX +extern const mp_obj_dict_t mp_obj_memoryview_locals_dict; +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY +extern const mp_obj_dict_t mp_obj_bytearray_locals_dict; +#endif + +#if MICROPY_PY_ARRAY +extern const mp_obj_dict_t mp_obj_array_locals_dict; +#endif + +#endif // MICROPY_INCLUDED_PY_OBJSTR_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objstringio.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objstringio.c new file mode 100644 index 00000000..a4dc8cfc --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objstringio.c @@ -0,0 +1,269 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2017 Paul Sokolovsky + * + * 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 "py/objstr.h" +#include "py/objstringio.h" +#include "py/runtime.h" +#include "py/stream.h" + +#if MICROPY_PY_IO + +#if MICROPY_CPYTHON_COMPAT +static void check_stringio_is_open(const mp_obj_stringio_t *o) { + if (o->vstr == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("I/O operation on closed file")); + } +} +#else +#define check_stringio_is_open(o) +#endif + +static void stringio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, self->base.type == &mp_type_stringio ? "" : "", self); +} + +static mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { + (void)errcode; + mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in); + check_stringio_is_open(o); + if (o->vstr->len <= o->pos) { // read to EOF, or seeked to EOF or beyond + return 0; + } + mp_uint_t remaining = o->vstr->len - o->pos; + if (size > remaining) { + size = remaining; + } + memcpy(buf, o->vstr->buf + o->pos, size); + o->pos += size; + return size; +} + +static void stringio_copy_on_write(mp_obj_stringio_t *o) { + const void *buf = o->vstr->buf; + o->vstr->buf = m_new(char, o->vstr->len); + o->vstr->fixed_buf = false; + o->ref_obj = MP_OBJ_NULL; + memcpy(o->vstr->buf, buf, o->vstr->len); +} + +static mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { + (void)errcode; + mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in); + check_stringio_is_open(o); + + if (o->vstr->fixed_buf) { + stringio_copy_on_write(o); + } + + mp_uint_t new_pos = o->pos + size; + if (new_pos < size) { + // Writing bytes will overflow o->pos beyond limit of mp_uint_t. + *errcode = MP_EFBIG; + return MP_STREAM_ERROR; + } + mp_uint_t org_len = o->vstr->len; + if (new_pos > o->vstr->alloc) { + // Take all what's already allocated... + o->vstr->len = o->vstr->alloc; + // ... and add more + vstr_add_len(o->vstr, new_pos - o->vstr->alloc); + } + // If there was a seek past EOF, clear the hole + if (o->pos > org_len) { + memset(o->vstr->buf + org_len, 0, o->pos - org_len); + } + memcpy(o->vstr->buf + o->pos, buf, size); + o->pos = new_pos; + if (new_pos > o->vstr->len) { + o->vstr->len = new_pos; + } + return size; +} + +static mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { + (void)errcode; + mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in); + switch (request) { + case MP_STREAM_SEEK: { + struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg; + mp_uint_t ref = 0; + switch (s->whence) { + case MP_SEEK_CUR: + ref = o->pos; + break; + case MP_SEEK_END: + ref = o->vstr->len; + break; + } + mp_uint_t new_pos = ref + s->offset; + + // For MP_SEEK_SET, offset is unsigned + if (s->whence != MP_SEEK_SET && s->offset < 0) { + if (new_pos > ref) { + // Negative offset from SEEK_CUR or SEEK_END went past 0. + // CPython sets position to 0, POSIX returns an EINVAL error + new_pos = 0; + } + } else if (new_pos < ref) { + // positive offset went beyond the limit of mp_uint_t + *errcode = MP_EINVAL; // replace with MP_EOVERFLOW when defined + return MP_STREAM_ERROR; + } + s->offset = o->pos = new_pos; + return 0; + } + case MP_STREAM_FLUSH: + return 0; + case MP_STREAM_CLOSE: + #if MICROPY_CPYTHON_COMPAT + vstr_free(o->vstr); + o->vstr = NULL; + #else + vstr_clear(o->vstr); + o->vstr->alloc = 0; + o->vstr->len = 0; + o->pos = 0; + #endif + return 0; + default: + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } +} + +#define STREAM_TO_CONTENT_TYPE(o) (((o)->base.type == &mp_type_stringio) ? &mp_type_str : &mp_type_bytes) + +static mp_obj_t stringio_getvalue(mp_obj_t self_in) { + mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in); + check_stringio_is_open(self); + // TODO: Try to avoid copying string + return mp_obj_new_str_of_type(STREAM_TO_CONTENT_TYPE(self), (byte *)self->vstr->buf, self->vstr->len); +} +static MP_DEFINE_CONST_FUN_OBJ_1(stringio_getvalue_obj, stringio_getvalue); + +static mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) { + mp_obj_stringio_t *o = mp_obj_malloc(mp_obj_stringio_t, type); + o->pos = 0; + o->ref_obj = MP_OBJ_NULL; + return o; +} + +static mp_obj_t stringio_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)n_kw; // TODO check n_kw==0 + + mp_uint_t sz = 16; + bool initdata = false; + mp_buffer_info_t bufinfo; + + mp_obj_stringio_t *o = stringio_new(type_in); + + if (n_args > 0) { + if (mp_obj_is_int(args[0])) { + sz = mp_obj_get_int(args[0]); + } else { + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + + if (mp_obj_is_str_or_bytes(args[0])) { + o->vstr = m_new_obj(vstr_t); + vstr_init_fixed_buf(o->vstr, bufinfo.len, bufinfo.buf); + o->vstr->len = bufinfo.len; + o->ref_obj = args[0]; + return MP_OBJ_FROM_PTR(o); + } + + sz = bufinfo.len; + initdata = true; + } + } + + o->vstr = vstr_new(sz); + + if (initdata) { + stringio_write(MP_OBJ_FROM_PTR(o), bufinfo.buf, bufinfo.len, NULL); + // Cur ptr is always at the beginning of buffer at the construction + o->pos = 0; + } + return MP_OBJ_FROM_PTR(o); +} + +static const mp_rom_map_elem_t stringio_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, + { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_getvalue), MP_ROM_PTR(&stringio_getvalue_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) }, +}; + +static MP_DEFINE_CONST_DICT(stringio_locals_dict, stringio_locals_dict_table); + +static const mp_stream_p_t stringio_stream_p = { + .read = stringio_read, + .write = stringio_write, + .ioctl = stringio_ioctl, + .is_text = true, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_stringio, + MP_QSTR_StringIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + make_new, stringio_make_new, + print, stringio_print, + protocol, &stringio_stream_p, + locals_dict, &stringio_locals_dict + ); + +#if MICROPY_PY_IO_BYTESIO +static const mp_stream_p_t bytesio_stream_p = { + .read = stringio_read, + .write = stringio_write, + .ioctl = stringio_ioctl, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bytesio, + MP_QSTR_BytesIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + make_new, stringio_make_new, + print, stringio_print, + protocol, &bytesio_stream_p, + locals_dict, &stringio_locals_dict + ); +#endif + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objstringio.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objstringio.h new file mode 100644 index 00000000..56738f4e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objstringio.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJSTRINGIO_H +#define MICROPY_INCLUDED_PY_OBJSTRINGIO_H + +#include "py/obj.h" + +typedef struct _mp_obj_stringio_t { + mp_obj_base_t base; + vstr_t *vstr; + // StringIO has single pointer used for both reading and writing + mp_uint_t pos; + // Underlying object buffered by this StringIO + mp_obj_t ref_obj; +} mp_obj_stringio_t; + +#endif // MICROPY_INCLUDED_PY_OBJSTRINGIO_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objstrunicode.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objstrunicode.c new file mode 100644 index 00000000..d7ce4fca --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objstrunicode.c @@ -0,0 +1,280 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2016 Paul Sokolovsky + * + * 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 "py/objstr.h" +#include "py/objlist.h" +#include "py/runtime.h" + +#if MICROPY_PY_BUILTINS_STR_UNICODE + +static mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); + +/******************************************************************************/ +/* str */ + +static void uni_print_quoted(const mp_print_t *print, const byte *str_data, uint str_len) { + // this escapes characters, but it will be very slow to print (calling print many times) + bool has_single_quote = false; + bool has_double_quote = false; + for (const byte *s = str_data, *top = str_data + str_len; !has_double_quote && s < top; s++) { + if (*s == '\'') { + has_single_quote = true; + } else if (*s == '"') { + has_double_quote = true; + } + } + unichar quote_char = '\''; + if (has_single_quote && !has_double_quote) { + quote_char = '"'; + } + mp_printf(print, "%c", quote_char); + const byte *s = str_data, *top = str_data + str_len; + while (s < top) { + unichar ch; + ch = utf8_get_char(s); + s = utf8_next_char(s); + if (ch == quote_char) { + mp_printf(print, "\\%c", quote_char); + } else if (ch == '\\') { + mp_print_str(print, "\\\\"); + } else if (32 <= ch && ch <= 126) { + mp_printf(print, "%c", ch); + } else if (ch == '\n') { + mp_print_str(print, "\\n"); + } else if (ch == '\r') { + mp_print_str(print, "\\r"); + } else if (ch == '\t') { + mp_print_str(print, "\\t"); + } else if (ch < 0x100) { + mp_printf(print, "\\x%02x", ch); + } else if (ch < 0x10000) { + mp_printf(print, "\\u%04x", ch); + } else { + mp_printf(print, "\\U%08x", ch); + } + } + mp_printf(print, "%c", quote_char); +} + +static void uni_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + GET_STR_DATA_LEN(self_in, str_data, str_len); + #if MICROPY_PY_JSON + if (kind == PRINT_JSON) { + mp_str_print_json(print, str_data, str_len); + return; + } + #endif + if (kind == PRINT_STR) { + print->print_strn(print->data, (const char *)str_data, str_len); + } else { + uni_print_quoted(print, str_data, str_len); + } +} + +static mp_obj_t uni_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + GET_STR_DATA_LEN(self_in, str_data, str_len); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(str_len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(utf8_charlen(str_data, str_len)); + default: + return MP_OBJ_NULL; // op not supported + } +} + +// Convert an index into a pointer to its lead byte. Out of bounds indexing will raise IndexError or +// be capped to the first/last character of the string, depending on is_slice. +const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, size_t self_len, + mp_obj_t index, bool is_slice) { + // All str functions also handle bytes objects, and they call str_index_to_ptr(), + // so it must handle bytes. + if (type == &mp_type_bytes + #if MICROPY_PY_BUILTINS_BYTEARRAY + || type == &mp_type_bytearray + #endif + ) { + // Taken from objstr.c:str_index_to_ptr() + size_t index_val = mp_get_index(type, self_len, index, is_slice); + return self_data + index_val; + } + + mp_int_t i; + // Copied from mp_get_index; I don't want bounds checking, just give me + // the integer as-is. (I can't bounds-check without scanning the whole + // string; an out-of-bounds index will be caught in the loops below.) + if (mp_obj_is_small_int(index)) { + i = MP_OBJ_SMALL_INT_VALUE(index); + } else if (!mp_obj_get_int_maybe(index, &i)) { + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("string indices must be integers, not %s"), mp_obj_get_type_str(index)); + } + const byte *s, *top = self_data + self_len; + if (i < 0) { + // Negative indexing is performed by counting from the end of the string. + for (s = top - 1; i; --s) { + if (s < self_data) { + if (is_slice) { + return self_data; + } + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("string index out of range")); + } + if (!UTF8_IS_CONT(*s)) { + ++i; + } + } + ++s; + } else { + // Positive indexing, correspondingly, counts from the start of the string. + // It's assumed that negative indexing will generally be used with small + // absolute values (eg str[-1], not str[-1000000]), which means it'll be + // more efficient this way. + s = self_data; + while (1) { + // First check out-of-bounds + if (s >= top) { + if (is_slice) { + return top; + } + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("string index out of range")); + } + // Then check completion + if (i-- == 0) { + break; + } + // Then skip UTF-8 char + ++s; + while (UTF8_IS_CONT(*s)) { + ++s; + } + } + } + return s; +} + +static mp_obj_t str_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + const mp_obj_type_t *type = mp_obj_get_type(self_in); + assert(type == &mp_type_str); + GET_STR_DATA_LEN(self_in, self_data, self_len); + if (value == MP_OBJ_SENTINEL) { + // load + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_obj_t ostart, ostop, ostep; + mp_obj_slice_t *slice = MP_OBJ_TO_PTR(index); + ostart = slice->start; + ostop = slice->stop; + ostep = slice->step; + + if (ostep != mp_const_none && ostep != MP_OBJ_NEW_SMALL_INT(1)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported")); + } + + const byte *pstart, *pstop; + if (ostart != mp_const_none) { + pstart = str_index_to_ptr(type, self_data, self_len, ostart, true); + } else { + pstart = self_data; + } + if (ostop != mp_const_none) { + // pstop will point just after the stop character. This depends on + // the \0 at the end of the string. + pstop = str_index_to_ptr(type, self_data, self_len, ostop, true); + } else { + pstop = self_data + self_len; + } + if (pstop < pstart) { + return MP_OBJ_NEW_QSTR(MP_QSTR_); + } + return mp_obj_new_str_of_type(type, (const byte *)pstart, pstop - pstart); + } + #endif + const byte *s = str_index_to_ptr(type, self_data, self_len, index, false); + int len = 1; + if (UTF8_IS_NONASCII(*s)) { + // Count the number of 1 bits (after the first) + for (char mask = 0x40; *s & mask; mask >>= 1) { + ++len; + } + } + return mp_obj_new_str_via_qstr((const char *)s, len); // This will create a one-character string + } else { + return MP_OBJ_NULL; // op not supported + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_str, + MP_QSTR_str, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_str_make_new, + print, uni_print, + unary_op, uni_unary_op, + binary_op, mp_obj_str_binary_op, + subscr, str_subscr, + iter, mp_obj_new_str_iterator, + buffer, mp_obj_str_get_buffer, + locals_dict, &mp_obj_str_locals_dict + ); + +/******************************************************************************/ +/* str iterator */ + +typedef struct _mp_obj_str_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_t str; + size_t cur; +} mp_obj_str_it_t; + +static mp_obj_t str_it_iternext(mp_obj_t self_in) { + mp_obj_str_it_t *self = MP_OBJ_TO_PTR(self_in); + GET_STR_DATA_LEN(self->str, str, len); + if (self->cur < len) { + const byte *cur = str + self->cur; + const byte *end = utf8_next_char(str + self->cur); + mp_obj_t o_out = mp_obj_new_str_via_qstr((const char *)cur, end - cur); + self->cur += end - cur; + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +static mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_str_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_str_it_t *o = (mp_obj_str_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = str_it_iternext; + o->str = str; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} + +#endif // MICROPY_PY_BUILTINS_STR_UNICODE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objtuple.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objtuple.c new file mode 100644 index 00000000..2cbcc0e5 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objtuple.c @@ -0,0 +1,299 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2017 Paul Sokolovsky + * + * 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 "py/objtuple.h" +#include "py/runtime.h" + +// type check is done on getiter method to allow tuple, namedtuple, attrtuple +#define mp_obj_is_tuple_compatible(o) (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o), iter) == mp_obj_tuple_getiter) + +/******************************************************************************/ +/* tuple */ + +void mp_obj_tuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + mp_obj_tuple_t *o = MP_OBJ_TO_PTR(o_in); + const char *item_separator = ", "; + if (MICROPY_PY_JSON && kind == PRINT_JSON) { + mp_print_str(print, "["); + #if MICROPY_PY_JSON_SEPARATORS + item_separator = MP_PRINT_GET_EXT(print)->item_separator; + #endif + } else { + mp_print_str(print, "("); + kind = PRINT_REPR; + } + for (size_t i = 0; i < o->len; i++) { + if (i > 0) { + mp_print_str(print, item_separator); + } + mp_obj_print_helper(print, o->items[i], kind); + } + if (MICROPY_PY_JSON && kind == PRINT_JSON) { + mp_print_str(print, "]"); + } else { + if (o->len == 1) { + mp_print_str(print, ","); + } + mp_print_str(print, ")"); + } +} + +static mp_obj_t mp_obj_tuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + switch (n_args) { + case 0: + // return a empty tuple + return mp_const_empty_tuple; + + case 1: + default: { + // 1 argument, an iterable from which we make a new tuple + if (mp_obj_is_type(args[0], &mp_type_tuple)) { + return args[0]; + } + + // TODO optimise for cases where we know the length of the iterator + + size_t alloc = 4; + size_t len = 0; + mp_obj_t *items = m_new(mp_obj_t, alloc); + + mp_obj_t iterable = mp_getiter(args[0], NULL); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (len >= alloc) { + items = m_renew(mp_obj_t, items, alloc, alloc * 2); + alloc *= 2; + } + items[len++] = item; + } + + mp_obj_t tuple = mp_obj_new_tuple(len, items); + m_del(mp_obj_t, items, alloc); + + return tuple; + } + } +} + +// Don't pass MP_BINARY_OP_NOT_EQUAL here +static mp_obj_t tuple_cmp_helper(mp_uint_t op, mp_obj_t self_in, mp_obj_t another_in) { + mp_check_self(mp_obj_is_tuple_compatible(self_in)); + const mp_obj_type_t *another_type = mp_obj_get_type(another_in); + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(another_type, iter) != mp_obj_tuple_getiter) { + // Slow path for user subclasses + another_in = mp_obj_cast_to_native_base(another_in, MP_OBJ_FROM_PTR(&mp_type_tuple)); + if (another_in == MP_OBJ_NULL) { + return MP_OBJ_NULL; + } + } + mp_obj_tuple_t *another = MP_OBJ_TO_PTR(another_in); + + return mp_obj_new_bool(mp_seq_cmp_objs(op, self->items, self->len, another->items, another->len)); +} + +mp_obj_t mp_obj_tuple_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(self->len != 0); + case MP_UNARY_OP_HASH: { + // start hash with pointer to empty tuple, to make it fairly unique + mp_int_t hash = (mp_int_t)mp_const_empty_tuple; + for (size_t i = 0; i < self->len; i++) { + hash += MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, self->items[i])); + } + return MP_OBJ_NEW_SMALL_INT(hash); + } + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(self->len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +mp_obj_t mp_obj_tuple_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { + mp_obj_tuple_t *o = MP_OBJ_TO_PTR(lhs); + switch (op) { + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_INPLACE_ADD: { + if (!mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(rhs)), MP_OBJ_FROM_PTR(&mp_type_tuple))) { + return MP_OBJ_NULL; // op not supported + } + mp_obj_tuple_t *p = MP_OBJ_TO_PTR(rhs); + mp_obj_tuple_t *s = MP_OBJ_TO_PTR(mp_obj_new_tuple(o->len + p->len, NULL)); + mp_seq_cat(s->items, o->items, o->len, p->items, p->len, mp_obj_t); + return MP_OBJ_FROM_PTR(s); + } + case MP_BINARY_OP_MULTIPLY: + case MP_BINARY_OP_INPLACE_MULTIPLY: { + mp_int_t n; + if (!mp_obj_get_int_maybe(rhs, &n)) { + return MP_OBJ_NULL; // op not supported + } + if (n <= 0) { + return mp_const_empty_tuple; + } + mp_obj_tuple_t *s = MP_OBJ_TO_PTR(mp_obj_new_tuple(o->len * n, NULL)); + mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); + return MP_OBJ_FROM_PTR(s); + } + case MP_BINARY_OP_EQUAL: + case MP_BINARY_OP_LESS: + case MP_BINARY_OP_LESS_EQUAL: + case MP_BINARY_OP_MORE: + case MP_BINARY_OP_MORE_EQUAL: + return tuple_cmp_helper(op, lhs, rhs); + + default: + return MP_OBJ_NULL; // op not supported + } +} + +mp_obj_t mp_obj_tuple_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + if (value == MP_OBJ_SENTINEL) { + // load + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported")); + } + mp_obj_tuple_t *res = MP_OBJ_TO_PTR(mp_obj_new_tuple(slice.stop - slice.start, NULL)); + mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t); + return MP_OBJ_FROM_PTR(res); + } + #endif + size_t index_value = mp_get_index(self->base.type, self->len, index, false); + return self->items[index_value]; + } else { + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t tuple_count(mp_obj_t self_in, mp_obj_t value) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_tuple)); + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + return mp_seq_count_obj(self->items, self->len, value); +} +static MP_DEFINE_CONST_FUN_OBJ_2(tuple_count_obj, tuple_count); + +static mp_obj_t tuple_index(size_t n_args, const mp_obj_t *args) { + mp_check_self(mp_obj_is_type(args[0], &mp_type_tuple)); + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(args[0]); + return mp_seq_index_obj(self->items, self->len, n_args, args); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(tuple_index_obj, 2, 4, tuple_index); + +static const mp_rom_map_elem_t tuple_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&tuple_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_index), MP_ROM_PTR(&tuple_index_obj) }, +}; + +static MP_DEFINE_CONST_DICT(tuple_locals_dict, tuple_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_tuple, + MP_QSTR_tuple, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_tuple_make_new, + print, mp_obj_tuple_print, + unary_op, mp_obj_tuple_unary_op, + binary_op, mp_obj_tuple_binary_op, + subscr, mp_obj_tuple_subscr, + iter, mp_obj_tuple_getiter, + locals_dict, &tuple_locals_dict + ); + +// the zero-length tuple +const mp_obj_tuple_t mp_const_empty_tuple_obj = {{&mp_type_tuple}, 0}; + +mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items) { + if (n == 0) { + return mp_const_empty_tuple; + } + mp_obj_tuple_t *o = mp_obj_malloc_var(mp_obj_tuple_t, items, mp_obj_t, n, &mp_type_tuple); + o->len = n; + if (items) { + for (size_t i = 0; i < n; i++) { + o->items[i] = items[i]; + } + } + return MP_OBJ_FROM_PTR(o); +} + +void mp_obj_tuple_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { + assert(mp_obj_is_tuple_compatible(self_in)); + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + *len = self->len; + *items = &self->items[0]; +} + +void mp_obj_tuple_del(mp_obj_t self_in) { + assert(mp_obj_is_type(self_in, &mp_type_tuple)); + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + m_del_var(mp_obj_tuple_t, items, mp_obj_t, self->len, self); +} + +/******************************************************************************/ +/* tuple iterator */ + +typedef struct _mp_obj_tuple_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_tuple_t *tuple; + size_t cur; +} mp_obj_tuple_it_t; + +static mp_obj_t tuple_it_iternext(mp_obj_t self_in) { + mp_obj_tuple_it_t *self = MP_OBJ_TO_PTR(self_in); + if (self->cur < self->tuple->len) { + mp_obj_t o_out = self->tuple->items[self->cur]; + self->cur += 1; + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +mp_obj_t mp_obj_tuple_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_tuple_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_tuple_it_t *o = (mp_obj_tuple_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = tuple_it_iternext; + o->tuple = MP_OBJ_TO_PTR(o_in); + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objtuple.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objtuple.h new file mode 100644 index 00000000..cc42aa6d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objtuple.h @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJTUPLE_H +#define MICROPY_INCLUDED_PY_OBJTUPLE_H + +#include "py/obj.h" + +typedef struct _mp_obj_tuple_t { + mp_obj_base_t base; + size_t len; + mp_obj_t items[]; +} mp_obj_tuple_t; + +typedef struct _mp_rom_obj_tuple_t { + mp_obj_base_t base; + size_t len; + mp_rom_obj_t items[]; +} mp_rom_obj_tuple_t; + +void mp_obj_tuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); +mp_obj_t mp_obj_tuple_unary_op(mp_unary_op_t op, mp_obj_t self_in); +mp_obj_t mp_obj_tuple_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs); +mp_obj_t mp_obj_tuple_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value); +mp_obj_t mp_obj_tuple_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf); + +extern const mp_obj_type_t mp_type_attrtuple; + +#define MP_DEFINE_ATTRTUPLE(tuple_obj_name, fields, nitems, ...) \ + const mp_rom_obj_tuple_t tuple_obj_name = { \ + .base = {&mp_type_attrtuple}, \ + .len = nitems, \ + .items = { __VA_ARGS__, MP_ROM_PTR((void *)fields) } \ + } + +#if MICROPY_PY_COLLECTIONS +void mp_obj_attrtuple_print_helper(const mp_print_t *print, const qstr *fields, mp_obj_tuple_t *o); +#endif + +mp_obj_t mp_obj_new_attrtuple(const qstr *fields, size_t n, const mp_obj_t *items); + +#endif // MICROPY_INCLUDED_PY_OBJTUPLE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objtype.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objtype.c new file mode 100644 index 00000000..b6d600e9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objtype.c @@ -0,0 +1,1467 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2018 Damien P. George + * Copyright (c) 2014-2018 Paul Sokolovsky + * + * 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 "py/objtype.h" +#include "py/runtime.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + +#define ENABLE_SPECIAL_ACCESSORS \ + (MICROPY_PY_DESCRIPTORS || MICROPY_PY_DELATTR_SETATTR || MICROPY_PY_BUILTINS_PROPERTY) + +static mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); + +/******************************************************************************/ +// instance object + +static int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_type_t **last_native_base) { + int count = 0; + for (;;) { + if (type == &mp_type_object) { + // Not a "real" type, end search here. + return count; + } else if (mp_obj_is_native_type(type)) { + // Native types don't have parents (at least not from our perspective) so end. + *last_native_base = type; + return count + 1; + } else if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { + // No parents so end search here. + return count; + #if MICROPY_MULTIPLE_INHERITANCE + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(type, parent))->type == &mp_type_tuple) { + // Multiple parents, search through them all recursively. + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(type, parent); + const mp_obj_t *item = parent_tuple->items; + const mp_obj_t *top = item + parent_tuple->len; + for (; item < top; ++item) { + assert(mp_obj_is_type(*item, &mp_type_type)); + const mp_obj_type_t *bt = (const mp_obj_type_t *)MP_OBJ_TO_PTR(*item); + count += instance_count_native_bases(bt, last_native_base); + } + return count; + #endif + } else { + // A single parent, use iteration to continue the search. + type = MP_OBJ_TYPE_GET_SLOT(type, parent); + } + } +} + +// This wrapper function is allows a subclass of a native type to call the +// __init__() method (corresponding to type->make_new) of the native type. +static mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(args[0]); + const mp_obj_type_t *native_base = NULL; + instance_count_native_bases(self->base.type, &native_base); + self->subobj[0] = MP_OBJ_TYPE_GET_SLOT(native_base, make_new)(native_base, n_args - 1, 0, args + 1); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper); + +#if !MICROPY_CPYTHON_COMPAT +static +#endif +mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *class, const mp_obj_type_t **native_base) { + size_t num_native_bases = instance_count_native_bases(class, native_base); + assert(num_native_bases < 2); + mp_obj_instance_t *o = mp_obj_malloc_var(mp_obj_instance_t, subobj, mp_obj_t, num_native_bases, class); + mp_map_init(&o->members, 0); + // Initialise the native base-class slot (should be 1 at most) with a valid + // object. It doesn't matter which object, so long as it can be uniquely + // distinguished from a native class that is initialised. + if (num_native_bases != 0) { + o->subobj[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj); + } + return o; +} + +// TODO +// This implements depth-first left-to-right MRO, which is not compliant with Python3 MRO +// http://python-history.blogspot.com/2010/06/method-resolution-order.html +// https://www.python.org/download/releases/2.3/mro/ +// +// will keep lookup->dest[0]'s value (should be MP_OBJ_NULL on invocation) if attribute +// is not found +// will set lookup->dest[0] to MP_OBJ_SENTINEL if special method was found in a native +// type base via slot id (as specified by lookup->slot_offset). As there can be only one +// native base, it's known that it applies to instance->subobj[0]. In most cases, we also +// don't need to know which type it was - because instance->subobj[0] is of that type. +// The only exception is when object is not yet constructed, then we need to know base +// native type to construct its instance->subobj[0] from. But this case is handled via +// instance_count_native_bases(), which returns a native base which it saw. +struct class_lookup_data { + mp_obj_instance_t *obj; + qstr attr; + size_t slot_offset; + mp_obj_t *dest; + bool is_type; +}; + +static void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_type_t *type) { + assert(lookup->dest[0] == MP_OBJ_NULL); + assert(lookup->dest[1] == MP_OBJ_NULL); + for (;;) { + DEBUG_printf("mp_obj_class_lookup: Looking up %s in %s\n", qstr_str(lookup->attr), qstr_str(type->name)); + // Optimize special method lookup for native types + // This avoids extra method_name => slot lookup. On the other hand, + // this should not be applied to class types, as will result in extra + // lookup either. + if (lookup->slot_offset != 0 && mp_obj_is_native_type(type)) { + // Check if there is a non-zero value in the specified slot index, + // with a special case for getiter where the slot won't be set + // for MP_TYPE_FLAG_ITER_IS_STREAM. + if (MP_OBJ_TYPE_HAS_SLOT_BY_OFFSET(type, lookup->slot_offset) || (lookup->slot_offset == MP_OBJ_TYPE_OFFSETOF_SLOT(iter) && type->flags & MP_TYPE_FLAG_ITER_IS_STREAM)) { + DEBUG_printf("mp_obj_class_lookup: Matched special meth slot (off=%d) for %s\n", + lookup->slot_offset, qstr_str(lookup->attr)); + lookup->dest[0] = MP_OBJ_SENTINEL; + return; + } + } + + if (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { + // search locals_dict (the set of methods/attributes) + assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)))); // MicroPython restriction, for now + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(lookup->attr), MP_MAP_LOOKUP); + if (elem != NULL) { + if (lookup->is_type) { + // If we look up a class method, we need to return original type for which we + // do a lookup, not a (base) type in which we found the class method. + const mp_obj_type_t *org_type = (const mp_obj_type_t *)lookup->obj; + mp_convert_member_lookup(MP_OBJ_NULL, org_type, elem->value, lookup->dest); + } else { + mp_obj_instance_t *obj = lookup->obj; + mp_obj_t obj_obj; + if (obj != NULL && mp_obj_is_native_type(type) && type != &mp_type_object /* object is not a real type */) { + // If we're dealing with native base class, then it applies to native sub-object + obj_obj = obj->subobj[0]; + } else { + obj_obj = MP_OBJ_FROM_PTR(obj); + } + mp_convert_member_lookup(obj_obj, type, elem->value, lookup->dest); + } + #if DEBUG_PRINT + DEBUG_printf("mp_obj_class_lookup: Returning: "); + mp_obj_print_helper(MICROPY_DEBUG_PRINTER, lookup->dest[0], PRINT_REPR); + if (lookup->dest[1] != MP_OBJ_NULL) { + // Don't try to repr() lookup->dest[1], as we can be called recursively + DEBUG_printf(" <%s @%p>", mp_obj_get_type_str(lookup->dest[1]), MP_OBJ_TO_PTR(lookup->dest[1])); + } + DEBUG_printf("\n"); + #endif + return; + } + } + + // Previous code block takes care about attributes defined in .locals_dict, + // but some attributes of native types may be handled using .load_attr method, + // so make sure we try to lookup those too. + if (lookup->obj != NULL && !lookup->is_type && mp_obj_is_native_type(type) && type != &mp_type_object /* object is not a real type */) { + mp_load_method_maybe(lookup->obj->subobj[0], lookup->attr, lookup->dest); + if (lookup->dest[0] != MP_OBJ_NULL) { + return; + } + } + + // attribute not found, keep searching base classes + + if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { + DEBUG_printf("mp_obj_class_lookup: No more parents\n"); + return; + #if MICROPY_MULTIPLE_INHERITANCE + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(type, parent))->type == &mp_type_tuple) { + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(type, parent); + const mp_obj_t *item = parent_tuple->items; + const mp_obj_t *top = item + parent_tuple->len - 1; + for (; item < top; ++item) { + assert(mp_obj_is_type(*item, &mp_type_type)); + mp_obj_type_t *bt = (mp_obj_type_t *)MP_OBJ_TO_PTR(*item); + if (bt == &mp_type_object) { + // Not a "real" type + continue; + } + mp_obj_class_lookup(lookup, bt); + if (lookup->dest[0] != MP_OBJ_NULL) { + return; + } + } + + // search last base (simple tail recursion elimination) + assert(mp_obj_is_type(*item, &mp_type_type)); + type = (mp_obj_type_t *)MP_OBJ_TO_PTR(*item); + #endif + } else { + type = MP_OBJ_TYPE_GET_SLOT(type, parent); + } + if (type == &mp_type_object) { + // Not a "real" type + return; + } + } +} + +static void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + qstr meth = (kind == PRINT_STR) ? MP_QSTR___str__ : MP_QSTR___repr__; + mp_obj_t member[2] = {MP_OBJ_NULL}; + struct class_lookup_data lookup = { + .obj = self, + .attr = meth, + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(print), + .dest = member, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self->base.type); + if (member[0] == MP_OBJ_NULL && kind == PRINT_STR) { + // If there's no __str__, fall back to __repr__ + lookup.attr = MP_QSTR___repr__; + lookup.slot_offset = 0; + mp_obj_class_lookup(&lookup, self->base.type); + } + + if (member[0] == MP_OBJ_SENTINEL) { + // Handle Exception subclasses specially + if (mp_obj_is_native_exception_instance(self->subobj[0])) { + if (kind != PRINT_STR) { + mp_print_str(print, qstr_str(self->base.type->name)); + } + mp_obj_print_helper(print, self->subobj[0], kind | PRINT_EXC_SUBCLASS); + } else { + mp_obj_print_helper(print, self->subobj[0], kind); + } + return; + } + + if (member[0] != MP_OBJ_NULL) { + mp_obj_t r = mp_call_function_1(member[0], self_in); + mp_obj_print_helper(print, r, PRINT_STR); + return; + } + + // TODO: CPython prints fully-qualified type name + mp_printf(print, "<%s object at %p>", mp_obj_get_type_str(self_in), self); +} + +static mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { + assert(mp_obj_is_instance_type(self)); + + // look for __new__ function + mp_obj_t init_fn[2] = {MP_OBJ_NULL}; + struct class_lookup_data lookup = { + .obj = NULL, + .attr = MP_QSTR___new__, + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(make_new), + .dest = init_fn, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self); + + const mp_obj_type_t *native_base = NULL; + mp_obj_instance_t *o; + if (init_fn[0] == MP_OBJ_NULL || init_fn[0] == MP_OBJ_SENTINEL) { + // Either there is no __new__() method defined or there is a native + // constructor. In both cases create a blank instance. + o = mp_obj_new_instance(self, &native_base); + + // Since type->make_new() implements both __new__() and __init__() in + // one go, of which the latter may be overridden by the Python subclass, + // we defer (see the end of this function) the call of the native + // constructor to give a chance for the Python __init__() method to call + // said native constructor. + + } else { + // Call Python class __new__ function with all args to create an instance + mp_obj_t new_ret; + if (n_args == 0 && n_kw == 0) { + mp_obj_t args2[1] = {MP_OBJ_FROM_PTR(self)}; + new_ret = mp_call_function_n_kw(init_fn[0], 1, 0, args2); + } else { + mp_obj_t *args2 = m_new(mp_obj_t, 1 + n_args + 2 * n_kw); + args2[0] = MP_OBJ_FROM_PTR(self); + memcpy(args2 + 1, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t)); + new_ret = mp_call_function_n_kw(init_fn[0], n_args + 1, n_kw, args2); + m_del(mp_obj_t, args2, 1 + n_args + 2 * n_kw); + } + + // https://docs.python.org/3.4/reference/datamodel.html#object.__new__ + // "If __new__() does not return an instance of cls, then the new + // instance's __init__() method will not be invoked." + if (mp_obj_get_type(new_ret) != self) { + return new_ret; + } + + // The instance returned by __new__() becomes the new object + o = MP_OBJ_TO_PTR(new_ret); + } + + // now call Python class __init__ function with all args + // This method has a chance to call super().__init__() to construct a + // possible native base class. + init_fn[0] = init_fn[1] = MP_OBJ_NULL; + lookup.obj = o; + lookup.attr = MP_QSTR___init__; + lookup.slot_offset = 0; + mp_obj_class_lookup(&lookup, self); + if (init_fn[0] != MP_OBJ_NULL) { + mp_obj_t init_ret; + if (n_args == 0 && n_kw == 0) { + init_ret = mp_call_method_n_kw(0, 0, init_fn); + } else { + mp_obj_t *args2 = m_new(mp_obj_t, 2 + n_args + 2 * n_kw); + args2[0] = init_fn[0]; + args2[1] = init_fn[1]; + memcpy(args2 + 2, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t)); + init_ret = mp_call_method_n_kw(n_args, n_kw, args2); + m_del(mp_obj_t, args2, 2 + n_args + 2 * n_kw); + } + if (init_ret != mp_const_none) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("__init__() should return None")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("__init__() should return None, not '%s'"), mp_obj_get_type_str(init_ret)); + #endif + } + } + + // If the type had a native base that was not explicitly initialised + // (constructed) by the Python __init__() method then construct it now. + if (native_base != NULL && o->subobj[0] == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) { + o->subobj[0] = MP_OBJ_TYPE_GET_SLOT(native_base, make_new)(native_base, n_args, n_kw, args); + } + + return MP_OBJ_FROM_PTR(o); +} + +// Qstrs for special methods are guaranteed to have a small value, so we use byte +// type to represent them. +// The (unescaped) names appear in `unsorted_str_list` in the QSTR +// generator script py/makeqstrdata.py to ensure they are assigned low numbers. +const byte mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { + [MP_UNARY_OP_BOOL] = MP_QSTR___bool__, + [MP_UNARY_OP_LEN] = MP_QSTR___len__, + [MP_UNARY_OP_HASH] = MP_QSTR___hash__, + [MP_UNARY_OP_INT_MAYBE] = MP_QSTR___int__, + #if MICROPY_PY_ALL_SPECIAL_METHODS + [MP_UNARY_OP_POSITIVE] = MP_QSTR___pos__, + [MP_UNARY_OP_NEGATIVE] = MP_QSTR___neg__, + [MP_UNARY_OP_INVERT] = MP_QSTR___invert__, + [MP_UNARY_OP_ABS] = MP_QSTR___abs__, + #endif + #if MICROPY_PY_BUILTINS_FLOAT + [MP_UNARY_OP_FLOAT_MAYBE] = MP_QSTR___float__, + #if MICROPY_PY_BUILTINS_COMPLEX + [MP_UNARY_OP_COMPLEX_MAYBE] = MP_QSTR___complex__, + #endif + #endif + #if MICROPY_PY_SYS_GETSIZEOF + [MP_UNARY_OP_SIZEOF] = MP_QSTR___sizeof__, + #endif +}; + +static mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + + #if MICROPY_PY_SYS_GETSIZEOF + if (MP_UNLIKELY(op == MP_UNARY_OP_SIZEOF)) { + // TODO: This doesn't count inherited objects (self->subobj) + const mp_obj_type_t *native_base; + size_t num_native_bases = instance_count_native_bases(mp_obj_get_type(self_in), &native_base); + + size_t sz = sizeof(*self) + sizeof(*self->subobj) * num_native_bases + + sizeof(*self->members.table) * self->members.alloc; + return MP_OBJ_NEW_SMALL_INT(sz); + } + #endif + + qstr op_name = mp_unary_op_method_name[op]; + /* Still try to lookup native slot + if (op_name == 0) { + return MP_OBJ_NULL; + } + */ + mp_obj_t member[2] = {MP_OBJ_NULL}; + struct class_lookup_data lookup = { + .obj = self, + .attr = op_name, + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(unary_op), + .dest = member, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self->base.type); + if (member[0] == MP_OBJ_SENTINEL) { + return mp_unary_op(op, self->subobj[0]); + } else if (member[0] != MP_OBJ_NULL) { + mp_obj_t val = mp_call_function_1(member[0], self_in); + + switch (op) { + case MP_UNARY_OP_HASH: + // __hash__ must return a small int + val = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int_truncated(val)); + break; + case MP_UNARY_OP_INT_MAYBE: + // Must return int + if (!mp_obj_is_int(val)) { + mp_raise_TypeError(NULL); + } + break; + default: + // No need to do anything + ; + } + return val; + } else { + if (op == MP_UNARY_OP_HASH) { + lookup.attr = MP_QSTR___eq__; + mp_obj_class_lookup(&lookup, self->base.type); + if (member[0] == MP_OBJ_NULL) { + // https://docs.python.org/3/reference/datamodel.html#object.__hash__ + // "User-defined classes have __eq__() and __hash__() methods by default; + // with them, all objects compare unequal (except with themselves) and + // x.__hash__() returns an appropriate value such that x == y implies + // both that x is y and hash(x) == hash(y)." + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)self_in); + } + // "A class that overrides __eq__() and does not define __hash__() will have its __hash__() implicitly set to None. + // When the __hash__() method of a class is None, instances of the class will raise an appropriate TypeError" + } + + return MP_OBJ_NULL; // op not supported + } +} + +// Binary-op enum values not listed here will have the default value of 0 in the +// table, corresponding to MP_QSTRnull, and are therefore unsupported (a lookup will +// fail). They can be added at the expense of code size for the qstr. +// Qstrs for special methods are guaranteed to have a small value, so we use byte +// type to represent them. +// The (unescaped) names appear in `unsorted_str_list` in the QSTR +// generator script py/makeqstrdata.py to ensure they are assigned low numbers. +const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { + [MP_BINARY_OP_LESS] = MP_QSTR___lt__, + [MP_BINARY_OP_MORE] = MP_QSTR___gt__, + [MP_BINARY_OP_EQUAL] = MP_QSTR___eq__, + [MP_BINARY_OP_LESS_EQUAL] = MP_QSTR___le__, + [MP_BINARY_OP_MORE_EQUAL] = MP_QSTR___ge__, + [MP_BINARY_OP_NOT_EQUAL] = MP_QSTR___ne__, + [MP_BINARY_OP_CONTAINS] = MP_QSTR___contains__, + + // If an inplace method is not found a normal method will be used as a fallback + [MP_BINARY_OP_INPLACE_ADD] = MP_QSTR___iadd__, + [MP_BINARY_OP_INPLACE_SUBTRACT] = MP_QSTR___isub__, + #if MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS + [MP_BINARY_OP_INPLACE_MULTIPLY] = MP_QSTR___imul__, + [MP_BINARY_OP_INPLACE_MAT_MULTIPLY] = MP_QSTR___imatmul__, + [MP_BINARY_OP_INPLACE_FLOOR_DIVIDE] = MP_QSTR___ifloordiv__, + [MP_BINARY_OP_INPLACE_TRUE_DIVIDE] = MP_QSTR___itruediv__, + [MP_BINARY_OP_INPLACE_MODULO] = MP_QSTR___imod__, + [MP_BINARY_OP_INPLACE_POWER] = MP_QSTR___ipow__, + [MP_BINARY_OP_INPLACE_OR] = MP_QSTR___ior__, + [MP_BINARY_OP_INPLACE_XOR] = MP_QSTR___ixor__, + [MP_BINARY_OP_INPLACE_AND] = MP_QSTR___iand__, + [MP_BINARY_OP_INPLACE_LSHIFT] = MP_QSTR___ilshift__, + [MP_BINARY_OP_INPLACE_RSHIFT] = MP_QSTR___irshift__, + #endif + + [MP_BINARY_OP_ADD] = MP_QSTR___add__, + [MP_BINARY_OP_SUBTRACT] = MP_QSTR___sub__, + #if MICROPY_PY_ALL_SPECIAL_METHODS + [MP_BINARY_OP_MULTIPLY] = MP_QSTR___mul__, + [MP_BINARY_OP_MAT_MULTIPLY] = MP_QSTR___matmul__, + [MP_BINARY_OP_FLOOR_DIVIDE] = MP_QSTR___floordiv__, + [MP_BINARY_OP_TRUE_DIVIDE] = MP_QSTR___truediv__, + [MP_BINARY_OP_MODULO] = MP_QSTR___mod__, + [MP_BINARY_OP_DIVMOD] = MP_QSTR___divmod__, + [MP_BINARY_OP_POWER] = MP_QSTR___pow__, + [MP_BINARY_OP_OR] = MP_QSTR___or__, + [MP_BINARY_OP_XOR] = MP_QSTR___xor__, + [MP_BINARY_OP_AND] = MP_QSTR___and__, + [MP_BINARY_OP_LSHIFT] = MP_QSTR___lshift__, + [MP_BINARY_OP_RSHIFT] = MP_QSTR___rshift__, + #endif + + #if MICROPY_PY_REVERSE_SPECIAL_METHODS + [MP_BINARY_OP_REVERSE_ADD] = MP_QSTR___radd__, + [MP_BINARY_OP_REVERSE_SUBTRACT] = MP_QSTR___rsub__, + #if MICROPY_PY_ALL_SPECIAL_METHODS + [MP_BINARY_OP_REVERSE_MULTIPLY] = MP_QSTR___rmul__, + [MP_BINARY_OP_REVERSE_MAT_MULTIPLY] = MP_QSTR___rmatmul__, + [MP_BINARY_OP_REVERSE_FLOOR_DIVIDE] = MP_QSTR___rfloordiv__, + [MP_BINARY_OP_REVERSE_TRUE_DIVIDE] = MP_QSTR___rtruediv__, + [MP_BINARY_OP_REVERSE_MODULO] = MP_QSTR___rmod__, + [MP_BINARY_OP_REVERSE_POWER] = MP_QSTR___rpow__, + [MP_BINARY_OP_REVERSE_OR] = MP_QSTR___ror__, + [MP_BINARY_OP_REVERSE_XOR] = MP_QSTR___rxor__, + [MP_BINARY_OP_REVERSE_AND] = MP_QSTR___rand__, + [MP_BINARY_OP_REVERSE_LSHIFT] = MP_QSTR___rlshift__, + [MP_BINARY_OP_REVERSE_RSHIFT] = MP_QSTR___rrshift__, + #endif + #endif +}; + +static mp_obj_t instance_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + // Note: For ducktyping, CPython does not look in the instance members or use + // __getattr__ or __getattribute__. It only looks in the class dictionary. + mp_obj_instance_t *lhs = MP_OBJ_TO_PTR(lhs_in); + qstr op_name = mp_binary_op_method_name[op]; + /* Still try to lookup native slot + if (op_name == 0) { + return MP_OBJ_NULL; + } + */ + mp_obj_t dest[3] = {MP_OBJ_NULL}; + struct class_lookup_data lookup = { + .obj = lhs, + .attr = op_name, + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(binary_op), + .dest = dest, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, lhs->base.type); + + mp_obj_t res; + if (dest[0] == MP_OBJ_SENTINEL) { + res = mp_binary_op(op, lhs->subobj[0], rhs_in); + } else if (dest[0] != MP_OBJ_NULL) { + dest[2] = rhs_in; + res = mp_call_method_n_kw(1, 0, dest); + res = op == MP_BINARY_OP_CONTAINS ? mp_obj_new_bool(mp_obj_is_true(res)) : res; + } else { + return MP_OBJ_NULL; // op not supported + } + + #if MICROPY_PY_BUILTINS_NOTIMPLEMENTED + // NotImplemented means "try other fallbacks (like calling __rop__ + // instead of __op__) and if nothing works, raise TypeError". + if (res == mp_const_notimplemented) { + return MP_OBJ_NULL; // op not supported + } + #endif + + return res; +} + +static void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + // logic: look in instance members then class locals + assert(mp_obj_is_instance_type(mp_obj_get_type(self_in))); + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + + // Note: This is fast-path'ed in the VM for the MP_BC_LOAD_ATTR operation. + mp_map_elem_t *elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + if (elem != NULL) { + // object member, always treated as a value + dest[0] = elem->value; + return; + } + #if MICROPY_CPYTHON_COMPAT + if (attr == MP_QSTR___dict__) { + // Create a new dict with a copy of the instance's map items. + // This creates, unlike CPython, a read-only __dict__ that can't be modified. + mp_obj_dict_t dict; + dict.base.type = &mp_type_dict; + dict.map = self->members; + dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(&dict)); + mp_obj_dict_t *dest_dict = MP_OBJ_TO_PTR(dest[0]); + dest_dict->map.is_fixed = 1; + return; + } + #endif + struct class_lookup_data lookup = { + .obj = self, + .attr = attr, + .slot_offset = 0, + .dest = dest, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self->base.type); + mp_obj_t member = dest[0]; + if (member != MP_OBJ_NULL) { + if (!(self->base.type->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS)) { + // Class doesn't have any special accessors to check so return straight away + return; + } + + #if MICROPY_PY_BUILTINS_PROPERTY + if (mp_obj_is_type(member, &mp_type_property)) { + // object member is a property; delegate the load to the property + // Note: This is an optimisation for code size and execution time. + // The proper way to do it is have the functionality just below + // in a __get__ method of the property object, and then it would + // be called by the descriptor code down below. But that way + // requires overhead for the nested mp_call's and overhead for + // the code. + const mp_obj_t *proxy = mp_obj_property_get(member); + if (proxy[0] == mp_const_none) { + mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("unreadable attribute")); + } else { + dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self_in); + } + return; + } + #endif + + #if MICROPY_PY_DESCRIPTORS + // found a class attribute; if it has a __get__ method then call it with the + // class instance and class as arguments and return the result + // Note that this is functionally correct but very slow: each load_attr + // requires an extra mp_load_method_maybe to check for the __get__. + mp_obj_t attr_get_method[4]; + mp_load_method_maybe(member, MP_QSTR___get__, attr_get_method); + if (attr_get_method[0] != MP_OBJ_NULL) { + attr_get_method[2] = self_in; + attr_get_method[3] = MP_OBJ_FROM_PTR(mp_obj_get_type(self_in)); + dest[0] = mp_call_method_n_kw(2, 0, attr_get_method); + } + #endif + return; + } + + // try __getattr__ + if (attr != MP_QSTR___getattr__) { + #if MICROPY_PY_DELATTR_SETATTR + // If the requested attr is __setattr__/__delattr__ then don't delegate the lookup + // to __getattr__. If we followed CPython's behaviour then __setattr__/__delattr__ + // would have already been found in the "object" base class. + if (attr == MP_QSTR___setattr__ || attr == MP_QSTR___delattr__) { + return; + } + #endif + + mp_obj_t dest2[3]; + mp_load_method_maybe(self_in, MP_QSTR___getattr__, dest2); + if (dest2[0] != MP_OBJ_NULL) { + // __getattr__ exists, call it and return its result + dest2[2] = MP_OBJ_NEW_QSTR(attr); + dest[0] = mp_call_method_n_kw(1, 0, dest2); + return; + } + } +} + +static bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + + if (!(self->base.type->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS)) { + // Class doesn't have any special accessors so skip their checks + goto skip_special_accessors; + } + + #if MICROPY_PY_BUILTINS_PROPERTY || MICROPY_PY_DESCRIPTORS + // With property and/or descriptors enabled we need to do a lookup + // first in the class dict for the attribute to see if the store should + // be delegated. + mp_obj_t member[2] = {MP_OBJ_NULL}; + struct class_lookup_data lookup = { + .obj = self, + .attr = attr, + .slot_offset = 0, + .dest = member, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self->base.type); + + if (member[0] != MP_OBJ_NULL) { + #if MICROPY_PY_BUILTINS_PROPERTY + if (mp_obj_is_type(member[0], &mp_type_property)) { + // attribute exists and is a property; delegate the store/delete + // Note: This is an optimisation for code size and execution time. + // The proper way to do it is have the functionality just below in + // a __set__/__delete__ method of the property object, and then it + // would be called by the descriptor code down below. But that way + // requires overhead for the nested mp_call's and overhead for + // the code. + const mp_obj_t *proxy = mp_obj_property_get(member[0]); + mp_obj_t dest[2] = {self_in, value}; + if (value == MP_OBJ_NULL) { + // delete attribute + if (proxy[2] == mp_const_none) { + // TODO better error message? + return false; + } else { + mp_call_function_n_kw(proxy[2], 1, 0, dest); + return true; + } + } else { + // store attribute + if (proxy[1] == mp_const_none) { + // TODO better error message? + return false; + } else { + mp_call_function_n_kw(proxy[1], 2, 0, dest); + return true; + } + } + } + #endif + + #if MICROPY_PY_DESCRIPTORS + // found a class attribute; if it has a __set__/__delete__ method then + // call it with the class instance (and value) as arguments + if (value == MP_OBJ_NULL) { + // delete attribute + mp_obj_t attr_delete_method[3]; + mp_load_method_maybe(member[0], MP_QSTR___delete__, attr_delete_method); + if (attr_delete_method[0] != MP_OBJ_NULL) { + attr_delete_method[2] = self_in; + mp_call_method_n_kw(1, 0, attr_delete_method); + return true; + } + } else { + // store attribute + mp_obj_t attr_set_method[4]; + mp_load_method_maybe(member[0], MP_QSTR___set__, attr_set_method); + if (attr_set_method[0] != MP_OBJ_NULL) { + attr_set_method[2] = self_in; + attr_set_method[3] = value; + mp_call_method_n_kw(2, 0, attr_set_method); + return true; + } + } + #endif + } + #endif + + #if MICROPY_PY_DELATTR_SETATTR + if (value == MP_OBJ_NULL) { + // delete attribute + // try __delattr__ first + mp_obj_t attr_delattr_method[3]; + mp_load_method_maybe(self_in, MP_QSTR___delattr__, attr_delattr_method); + if (attr_delattr_method[0] != MP_OBJ_NULL) { + // __delattr__ exists, so call it + attr_delattr_method[2] = MP_OBJ_NEW_QSTR(attr); + mp_call_method_n_kw(1, 0, attr_delattr_method); + return true; + } + } else { + // store attribute + // try __setattr__ first + mp_obj_t attr_setattr_method[4]; + mp_load_method_maybe(self_in, MP_QSTR___setattr__, attr_setattr_method); + if (attr_setattr_method[0] != MP_OBJ_NULL) { + // __setattr__ exists, so call it + attr_setattr_method[2] = MP_OBJ_NEW_QSTR(attr); + attr_setattr_method[3] = value; + mp_call_method_n_kw(2, 0, attr_setattr_method); + return true; + } + } + #endif + +skip_special_accessors: + + if (value == MP_OBJ_NULL) { + // delete attribute + mp_map_elem_t *elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_REMOVE_IF_FOUND); + return elem != NULL; + } else { + // store attribute + mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; + return true; + } +} + +static void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] == MP_OBJ_NULL) { + mp_obj_instance_load_attr(self_in, attr, dest); + } else { + if (mp_obj_instance_store_attr(self_in, attr, dest[1])) { + dest[0] = MP_OBJ_NULL; // indicate success + } + } +} + +static mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t member[4] = {MP_OBJ_NULL, MP_OBJ_NULL, index, value}; + struct class_lookup_data lookup = { + .obj = self, + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(subscr), + .dest = member, + .is_type = false, + }; + if (value == MP_OBJ_NULL) { + // delete item + lookup.attr = MP_QSTR___delitem__; + } else if (value == MP_OBJ_SENTINEL) { + // load item + lookup.attr = MP_QSTR___getitem__; + } else { + // store item + lookup.attr = MP_QSTR___setitem__; + } + mp_obj_class_lookup(&lookup, self->base.type); + if (member[0] == MP_OBJ_SENTINEL) { + return mp_obj_subscr(self->subobj[0], index, value); + } else if (member[0] != MP_OBJ_NULL) { + size_t n_args = value == MP_OBJ_NULL || value == MP_OBJ_SENTINEL ? 1 : 2; + mp_obj_t ret = mp_call_method_n_kw(n_args, 0, member); + if (value == MP_OBJ_SENTINEL) { + return ret; + } else { + return mp_const_none; + } + } else { + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t mp_obj_instance_get_call(mp_obj_t self_in, mp_obj_t *member) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + struct class_lookup_data lookup = { + .obj = self, + .attr = MP_QSTR___call__, + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(call), + .dest = member, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self->base.type); + return member[0]; +} + +bool mp_obj_instance_is_callable(mp_obj_t self_in) { + mp_obj_t member[2] = {MP_OBJ_NULL, MP_OBJ_NULL}; + return mp_obj_instance_get_call(self_in, member) != MP_OBJ_NULL; +} + +mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_obj_t member[2] = {MP_OBJ_NULL, MP_OBJ_NULL}; + mp_obj_t call = mp_obj_instance_get_call(self_in, member); + if (call == MP_OBJ_NULL) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object not callable")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object isn't callable"), mp_obj_get_type_str(self_in)); + #endif + } + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + if (call == MP_OBJ_SENTINEL) { + return mp_call_function_n_kw(self->subobj[0], n_args, n_kw, args); + } + + return mp_call_method_self_n_kw(member[0], member[1], n_args, n_kw, args); +} + +// Note that iter_buf may be NULL, and needs to be allocated if needed +mp_obj_t mp_obj_instance_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t member[2] = {MP_OBJ_NULL}; + struct class_lookup_data lookup = { + .obj = self, + .attr = MP_QSTR___iter__, + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(iter), + .dest = member, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self->base.type); + if (member[0] == MP_OBJ_NULL) { + return MP_OBJ_NULL; + } else if (member[0] == MP_OBJ_SENTINEL) { + const mp_obj_type_t *type = mp_obj_get_type(self->subobj[0]); + if (type->flags & MP_TYPE_FLAG_ITER_IS_ITERNEXT) { + return self->subobj[0]; + } else { + if (iter_buf == NULL) { + iter_buf = m_new_obj(mp_obj_iter_buf_t); + } + return ((mp_getiter_fun_t)MP_OBJ_TYPE_GET_SLOT(type, iter))(self->subobj[0], iter_buf); + } + } else { + return mp_call_method_n_kw(0, 0, member); + } +} + +static mp_int_t instance_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t member[2] = {MP_OBJ_NULL}; + struct class_lookup_data lookup = { + .obj = self, + .attr = MP_QSTR_, // don't actually look for a method + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(buffer), + .dest = member, + .is_type = false, + }; + mp_obj_class_lookup(&lookup, self->base.type); + if (member[0] == MP_OBJ_SENTINEL) { + const mp_obj_type_t *type = mp_obj_get_type(self->subobj[0]); + return MP_OBJ_TYPE_GET_SLOT(type, buffer)(self->subobj[0], bufinfo, flags); + } else { + return 1; // object does not support buffer protocol + } +} + +/******************************************************************************/ +// type object +// - the struct is mp_obj_type_t and is defined in obj.h so const types can be made +// - there is a constant mp_obj_type_t (called mp_type_type) for the 'type' object +// - creating a new class (a new type) creates a new mp_obj_type_t + +#if ENABLE_SPECIAL_ACCESSORS +static bool check_for_special_accessors(mp_obj_t key, mp_obj_t value) { + #if MICROPY_PY_DELATTR_SETATTR + if (key == MP_OBJ_NEW_QSTR(MP_QSTR___setattr__) || key == MP_OBJ_NEW_QSTR(MP_QSTR___delattr__)) { + return true; + } + #endif + #if MICROPY_PY_BUILTINS_PROPERTY + if (mp_obj_is_type(value, &mp_type_property)) { + return true; + } + #endif + #if MICROPY_PY_DESCRIPTORS + static const uint8_t to_check[] = { + MP_QSTR___get__, MP_QSTR___set__, MP_QSTR___delete__, + }; + for (size_t i = 0; i < MP_ARRAY_SIZE(to_check); ++i) { + mp_obj_t dest_temp[2]; + mp_load_method_protected(value, to_check[i], dest_temp, true); + if (dest_temp[0] != MP_OBJ_NULL) { + return true; + } + } + #endif + return false; +} +#endif + +static void type_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", self->name); +} + +static mp_obj_t type_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + + mp_arg_check_num(n_args, n_kw, 1, 3, false); + + switch (n_args) { + case 1: + return MP_OBJ_FROM_PTR(mp_obj_get_type(args[0])); + + case 3: + // args[0] = name + // args[1] = bases tuple + // args[2] = locals dict + return mp_obj_new_type(mp_obj_str_get_qstr(args[0]), args[1], args[2]); + + default: + mp_raise_TypeError(MP_ERROR_TEXT("type takes 1 or 3 arguments")); + } +} + +static mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // instantiate an instance of a class + + mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); + + if (!MP_OBJ_TYPE_HAS_SLOT(self, make_new)) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("can't create instance")); + #else + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("can't create '%q' instances"), self->name); + #endif + } + + // make new instance + mp_obj_t o = MP_OBJ_TYPE_GET_SLOT(self, make_new)(self, n_args, n_kw, args); + + // return new instance + return o; +} + +static void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + assert(mp_obj_is_type(self_in, &mp_type_type)); + mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); + + if (dest[0] == MP_OBJ_NULL) { + // load attribute + #if MICROPY_CPYTHON_COMPAT + if (attr == MP_QSTR___name__) { + dest[0] = MP_OBJ_NEW_QSTR(self->name); + return; + } + #if MICROPY_CPYTHON_COMPAT + if (attr == MP_QSTR___dict__) { + // Returns a read-only dict of the class attributes. + // If the internal locals is not fixed, a copy will be created. + const mp_obj_dict_t *dict = MP_OBJ_TYPE_GET_SLOT_OR_NULL(self, locals_dict); + if (!dict) { + dict = &mp_const_empty_dict_obj; + } + if (dict->map.is_fixed) { + dest[0] = MP_OBJ_FROM_PTR(dict); + } else { + dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(dict)); + mp_obj_dict_t *dict_copy = MP_OBJ_TO_PTR(dest[0]); + dict_copy->map.is_fixed = 1; + } + return; + } + #endif + if (attr == MP_QSTR___bases__) { + if (self == &mp_type_object) { + dest[0] = mp_const_empty_tuple; + return; + } + mp_obj_t parent_obj = MP_OBJ_TYPE_HAS_SLOT(self, parent) ? MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(self, parent)) : MP_OBJ_FROM_PTR(&mp_type_object); + #if MICROPY_MULTIPLE_INHERITANCE + if (mp_obj_is_type(parent_obj, &mp_type_tuple)) { + dest[0] = parent_obj; + return; + } + #endif + dest[0] = mp_obj_new_tuple(1, &parent_obj); + return; + } + #endif + struct class_lookup_data lookup = { + .obj = (mp_obj_instance_t *)self, + .attr = attr, + .slot_offset = 0, + .dest = dest, + .is_type = true, + }; + mp_obj_class_lookup(&lookup, self); + } else { + // delete/store attribute + + if (MP_OBJ_TYPE_HAS_SLOT(self, locals_dict)) { + assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(self, locals_dict)))); // MicroPython restriction, for now + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(self, locals_dict)->map; + if (locals_map->is_fixed) { + // can't apply delete/store to a fixed map + return; + } + if (dest[1] == MP_OBJ_NULL) { + // delete attribute + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_REMOVE_IF_FOUND); + if (elem != NULL) { + dest[0] = MP_OBJ_NULL; // indicate success + } + } else { + #if ENABLE_SPECIAL_ACCESSORS + // Check if we add any special accessor methods with this store + if (!(self->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS)) { + if (check_for_special_accessors(MP_OBJ_NEW_QSTR(attr), dest[1])) { + if (self->flags & MP_TYPE_FLAG_IS_SUBCLASSED) { + // This class is already subclassed so can't have special accessors added + mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("can't add special method to already-subclassed class")); + } + self->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; + } + } + #endif + + // store attribute + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + elem->value = dest[1]; + dest[0] = MP_OBJ_NULL; // indicate success + } + } + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_type, + MP_QSTR_type, + MP_TYPE_FLAG_NONE, + make_new, type_make_new, + print, type_print, + call, type_call, + attr, type_attr + ); + +mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { + // Verify input objects have expected type + if (!mp_obj_is_type(bases_tuple, &mp_type_tuple)) { + mp_raise_TypeError(NULL); + } + if (!mp_obj_is_dict_or_ordereddict(locals_dict)) { + mp_raise_TypeError(NULL); + } + + // TODO might need to make a copy of locals_dict; at least that's how CPython does it + + // Basic validation of base classes + uint16_t base_flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE + | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE + | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST + | MP_TYPE_FLAG_ITER_IS_GETITER + | MP_TYPE_FLAG_INSTANCE_TYPE; + size_t bases_len; + mp_obj_t *bases_items; + mp_obj_tuple_get(bases_tuple, &bases_len, &bases_items); + for (size_t i = 0; i < bases_len; i++) { + if (!mp_obj_is_type(bases_items[i], &mp_type_type)) { + mp_raise_TypeError(NULL); + } + mp_obj_type_t *t = MP_OBJ_TO_PTR(bases_items[i]); + // TODO: Verify with CPy, tested on function type + if (!MP_OBJ_TYPE_HAS_SLOT(t, make_new)) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("type isn't an acceptable base type")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("type '%q' isn't an acceptable base type"), t->name); + #endif + } + #if ENABLE_SPECIAL_ACCESSORS + if (mp_obj_is_instance_type(t)) { + t->flags |= MP_TYPE_FLAG_IS_SUBCLASSED; + base_flags |= t->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; + } + #endif + } + + const void *base_protocol = NULL; + if (bases_len > 0) { + base_protocol = MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_type_t *)MP_OBJ_TO_PTR(bases_items[0])), protocol); + } + + // Allocate a variable-sized mp_obj_type_t with as many slots as we need + // (currently 10, plus 1 for base, plus 1 for base-protocol). + // Note: mp_obj_type_t is (2 + 3 + #slots) words, so going from 11 to 12 slots + // moves from 4 to 5 gc blocks. + mp_obj_type_t *o = m_new_obj_var0(mp_obj_type_t, slots, void *, 10 + (bases_len ? 1 : 0) + (base_protocol ? 1 : 0)); + o->base.type = &mp_type_type; + o->flags = base_flags; + o->name = name; + MP_OBJ_TYPE_SET_SLOT(o, make_new, mp_obj_instance_make_new, 0); + MP_OBJ_TYPE_SET_SLOT(o, print, instance_print, 1); + MP_OBJ_TYPE_SET_SLOT(o, call, mp_obj_instance_call, 2); + MP_OBJ_TYPE_SET_SLOT(o, unary_op, instance_unary_op, 3); + MP_OBJ_TYPE_SET_SLOT(o, binary_op, instance_binary_op, 4); + MP_OBJ_TYPE_SET_SLOT(o, attr, mp_obj_instance_attr, 5); + MP_OBJ_TYPE_SET_SLOT(o, subscr, instance_subscr, 6); + MP_OBJ_TYPE_SET_SLOT(o, iter, mp_obj_instance_getiter, 7); + MP_OBJ_TYPE_SET_SLOT(o, buffer, instance_get_buffer, 8); + + mp_obj_dict_t *locals_ptr = MP_OBJ_TO_PTR(locals_dict); + MP_OBJ_TYPE_SET_SLOT(o, locals_dict, locals_ptr, 9); + + if (bases_len > 0) { + if (bases_len >= 2) { + #if MICROPY_MULTIPLE_INHERITANCE + MP_OBJ_TYPE_SET_SLOT(o, parent, MP_OBJ_TO_PTR(bases_tuple), 10); + #else + mp_raise_NotImplementedError(MP_ERROR_TEXT("multiple inheritance not supported")); + #endif + } else { + MP_OBJ_TYPE_SET_SLOT(o, parent, MP_OBJ_TO_PTR(bases_items[0]), 10); + } + + // Inherit protocol from a base class. This allows to define an + // abstract base class which would translate C-level protocol to + // Python method calls, and any subclass inheriting from it will + // support this feature. + if (base_protocol) { + MP_OBJ_TYPE_SET_SLOT(o, protocol, base_protocol, 11); + } + } + + #if ENABLE_SPECIAL_ACCESSORS + // Check if the class has any special accessor methods + if (!(o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS)) { + for (size_t i = 0; i < locals_ptr->map.alloc; i++) { + if (mp_map_slot_is_filled(&locals_ptr->map, i)) { + const mp_map_elem_t *elem = &locals_ptr->map.table[i]; + if (check_for_special_accessors(elem->key, elem->value)) { + o->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; + break; + } + } + } + } + #endif + + const mp_obj_type_t *native_base; + size_t num_native_bases = instance_count_native_bases(o, &native_base); + if (num_native_bases > 1) { + mp_raise_TypeError(MP_ERROR_TEXT("multiple bases have instance lay-out conflict")); + } + + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(o, locals_dict)->map; + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); + if (elem != NULL) { + // __new__ slot exists; check if it is a function + if (mp_obj_is_fun(elem->value)) { + // __new__ is a function, wrap it in a staticmethod decorator + elem->value = static_class_method_make_new(&mp_type_staticmethod, 1, 0, &elem->value); + } + } + + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +// super object + +typedef struct _mp_obj_super_t { + mp_obj_base_t base; + mp_obj_t type; + mp_obj_t obj; +} mp_obj_super_t; + +static void super_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_super_t *self = MP_OBJ_TO_PTR(self_in); + mp_print_str(print, "type, PRINT_STR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, self->obj, PRINT_STR); + mp_print_str(print, ">"); +} + +static mp_obj_t super_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + // 0 arguments are turned into 2 in the compiler + // 1 argument is not yet implemented + mp_arg_check_num(n_args, n_kw, 2, 2, false); + if (!mp_obj_is_type(args[0], &mp_type_type)) { + mp_raise_TypeError(NULL); + } + mp_obj_super_t *o = m_new_obj(mp_obj_super_t); + *o = (mp_obj_super_t) {{type_in}, args[0], args[1]}; + return MP_OBJ_FROM_PTR(o); +} + +static void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + + assert(mp_obj_is_type(self_in, &mp_type_super)); + mp_obj_super_t *self = MP_OBJ_TO_PTR(self_in); + + assert(mp_obj_is_type(self->type, &mp_type_type)); + + mp_obj_type_t *type = MP_OBJ_TO_PTR(self->type); + + struct class_lookup_data lookup = { + .obj = MP_OBJ_TO_PTR(self->obj), + .attr = attr, + .slot_offset = 0, + .dest = dest, + .is_type = false, + }; + + // Allow a call super().__init__() to reach any native base classes. + if (attr == MP_QSTR___init__) { + lookup.slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(make_new); + } + + if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { + // no parents, do nothing + #if MICROPY_MULTIPLE_INHERITANCE + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(type, parent))->type == &mp_type_tuple) { + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(type, parent); + size_t len = parent_tuple->len; + const mp_obj_t *items = parent_tuple->items; + for (size_t i = 0; i < len; i++) { + assert(mp_obj_is_type(items[i], &mp_type_type)); + if (MP_OBJ_TO_PTR(items[i]) == &mp_type_object) { + // The "object" type will be searched at the end of this function, + // and we don't want to lookup native methods in object. + continue; + } + + mp_obj_class_lookup(&lookup, (mp_obj_type_t *)MP_OBJ_TO_PTR(items[i])); + if (dest[0] != MP_OBJ_NULL) { + break; + } + } + #endif + } else if (MP_OBJ_TYPE_GET_SLOT(type, parent) != &mp_type_object) { + mp_obj_class_lookup(&lookup, MP_OBJ_TYPE_GET_SLOT(type, parent)); + } + + if (dest[0] != MP_OBJ_NULL) { + if (dest[0] == MP_OBJ_SENTINEL) { + // Looked up native __init__ so defer to it + dest[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj); + dest[1] = self->obj; + } + return; + } + + // Reset slot_offset so we don't look up any native methods in object, + // because object never takes up the native base-class slot. + lookup.slot_offset = 0; + + mp_obj_class_lookup(&lookup, &mp_type_object); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_super, + MP_QSTR_super, + MP_TYPE_FLAG_NONE, + make_new, super_make_new, + print, super_print, + attr, super_attr + ); + +void mp_load_super_method(qstr attr, mp_obj_t *dest) { + mp_obj_super_t super = {{&mp_type_super}, dest[1], dest[2]}; + mp_load_method(MP_OBJ_FROM_PTR(&super), attr, dest); +} + +/******************************************************************************/ +// subclassing and built-ins specific to types + +// object and classinfo should be type objects +// (but the function will fail gracefully if they are not) +bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) { + for (;;) { + if (object == classinfo) { + return true; + } + + // not equivalent classes, keep searching base classes + + // object should always be a type object, but just return false if it's not + if (!mp_obj_is_type(object, &mp_type_type)) { + return false; + } + + const mp_obj_type_t *self = MP_OBJ_TO_PTR(object); + + if (!MP_OBJ_TYPE_HAS_SLOT(self, parent)) { + // type has no parents + return false; + #if MICROPY_MULTIPLE_INHERITANCE + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(self, parent))->type == &mp_type_tuple) { + // get the base objects (they should be type objects) + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(self, parent); + const mp_obj_t *item = parent_tuple->items; + const mp_obj_t *top = item + parent_tuple->len - 1; + + // iterate through the base objects + for (; item < top; ++item) { + if (mp_obj_is_subclass_fast(*item, classinfo)) { + return true; + } + } + + // search last base (simple tail recursion elimination) + object = *item; + #endif + } else { + // type has 1 parent + object = MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(self, parent)); + } + } +} + +static mp_obj_t mp_obj_is_subclass(mp_obj_t object, mp_obj_t classinfo) { + size_t len; + mp_obj_t *items; + if (mp_obj_is_type(classinfo, &mp_type_type)) { + len = 1; + items = &classinfo; + } else if (mp_obj_is_type(classinfo, &mp_type_tuple)) { + mp_obj_tuple_get(classinfo, &len, &items); + } else { + mp_raise_TypeError(MP_ERROR_TEXT("issubclass() arg 2 must be a class or a tuple of classes")); + } + + for (size_t i = 0; i < len; i++) { + // We explicitly check for 'object' here since no-one explicitly derives from it + if (items[i] == MP_OBJ_FROM_PTR(&mp_type_object) || mp_obj_is_subclass_fast(object, items[i])) { + return mp_const_true; + } + } + return mp_const_false; +} + +static mp_obj_t mp_builtin_issubclass(mp_obj_t object, mp_obj_t classinfo) { + if (!mp_obj_is_type(object, &mp_type_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("issubclass() arg 1 must be a class")); + } + return mp_obj_is_subclass(object, classinfo); +} + +MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_issubclass_obj, mp_builtin_issubclass); + +static mp_obj_t mp_builtin_isinstance(mp_obj_t object, mp_obj_t classinfo) { + return mp_obj_is_subclass(MP_OBJ_FROM_PTR(mp_obj_get_type(object)), classinfo); +} + +MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj, mp_builtin_isinstance); + +mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type) { + const mp_obj_type_t *self_type = mp_obj_get_type(self_in); + + if (MP_OBJ_FROM_PTR(self_type) == native_type) { + return self_in; + } else if (!mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(self_type), native_type)) { + return MP_OBJ_NULL; + } else { + mp_obj_instance_t *self = (mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in); + return self->subobj[0]; + } +} + +/******************************************************************************/ +// staticmethod and classmethod types (probably should go in a different file) + +static mp_obj_t static_class_method_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { + assert(self == &mp_type_staticmethod || self == &mp_type_classmethod); + + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_static_class_method_t *o = m_new_obj(mp_obj_static_class_method_t); + *o = (mp_obj_static_class_method_t) {{self}, args[0]}; + return MP_OBJ_FROM_PTR(o); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_staticmethod, + MP_QSTR_staticmethod, + MP_TYPE_FLAG_NONE, + make_new, static_class_method_make_new + ); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_classmethod, + MP_QSTR_classmethod, + MP_TYPE_FLAG_NONE, + make_new, static_class_method_make_new + ); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objtype.h b/non_catalog_apps/mp_flipper/lib/micropython/py/objtype.h new file mode 100644 index 00000000..839cc6d1 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objtype.h @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_OBJTYPE_H +#define MICROPY_INCLUDED_PY_OBJTYPE_H + +#include "py/obj.h" + +// instance object +// creating an instance of a class makes one of these objects +typedef struct _mp_obj_instance_t { + mp_obj_base_t base; + mp_map_t members; + mp_obj_t subobj[]; + // TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them +} mp_obj_instance_t; + +#if MICROPY_CPYTHON_COMPAT +// this is needed for object.__new__ +mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *cls, const mp_obj_type_t **native_base); +#endif + +// these need to be exposed so mp_obj_is_callable can work correctly +bool mp_obj_instance_is_callable(mp_obj_t self_in); +mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); + +#define mp_obj_is_instance_type(type) ((type)->flags & MP_TYPE_FLAG_INSTANCE_TYPE) +#define mp_obj_is_native_type(type) (!((type)->flags & MP_TYPE_FLAG_INSTANCE_TYPE)) + +// this needs to be exposed for mp_getiter +mp_obj_t mp_obj_instance_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf); + +#endif // MICROPY_INCLUDED_PY_OBJTYPE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/objzip.c b/non_catalog_apps/mp_flipper/lib/micropython/py/objzip.c new file mode 100644 index 00000000..dd2b39ee --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/objzip.c @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/objtuple.h" +#include "py/runtime.h" + +typedef struct _mp_obj_zip_t { + mp_obj_base_t base; + size_t n_iters; + mp_obj_t iters[]; +} mp_obj_zip_t; + +static mp_obj_t zip_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, false); + + mp_obj_zip_t *o = mp_obj_malloc_var(mp_obj_zip_t, iters, mp_obj_t, n_args, type); + o->n_iters = n_args; + for (size_t i = 0; i < n_args; i++) { + o->iters[i] = mp_getiter(args[i], NULL); + } + return MP_OBJ_FROM_PTR(o); +} + +static mp_obj_t zip_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &mp_type_zip)); + mp_obj_zip_t *self = MP_OBJ_TO_PTR(self_in); + if (self->n_iters == 0) { + return MP_OBJ_STOP_ITERATION; + } + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(self->n_iters, NULL)); + + for (size_t i = 0; i < self->n_iters; i++) { + mp_obj_t next = mp_iternext(self->iters[i]); + if (next == MP_OBJ_STOP_ITERATION) { + mp_obj_tuple_del(MP_OBJ_FROM_PTR(tuple)); + return MP_OBJ_STOP_ITERATION; + } + tuple->items[i] = next; + } + return MP_OBJ_FROM_PTR(tuple); +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_zip, + MP_QSTR_zip, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, zip_make_new, + iter, zip_iternext + ); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/opmethods.c b/non_catalog_apps/mp_flipper/lib/micropython/py/opmethods.c new file mode 100644 index 00000000..32ab187b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/opmethods.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" +#include "py/builtin.h" + +static mp_obj_t op_getitem(mp_obj_t self_in, mp_obj_t key_in) { + const mp_obj_type_t *type = mp_obj_get_type(self_in); + // Note: assumes type must have subscr (only used by dict). + return MP_OBJ_TYPE_GET_SLOT(type, subscr)(self_in, key_in, MP_OBJ_SENTINEL); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_op_getitem_obj, op_getitem); + +static mp_obj_t op_setitem(mp_obj_t self_in, mp_obj_t key_in, mp_obj_t value_in) { + const mp_obj_type_t *type = mp_obj_get_type(self_in); + // Note: assumes type must have subscr (only used by dict). + return MP_OBJ_TYPE_GET_SLOT(type, subscr)(self_in, key_in, value_in); +} +MP_DEFINE_CONST_FUN_OBJ_3(mp_op_setitem_obj, op_setitem); + +static mp_obj_t op_delitem(mp_obj_t self_in, mp_obj_t key_in) { + const mp_obj_type_t *type = mp_obj_get_type(self_in); + // Note: assumes type must have subscr (only used by dict). + return MP_OBJ_TYPE_GET_SLOT(type, subscr)(self_in, key_in, MP_OBJ_NULL); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_op_delitem_obj, op_delitem); + +static mp_obj_t op_contains(mp_obj_t lhs_in, mp_obj_t rhs_in) { + const mp_obj_type_t *type = mp_obj_get_type(lhs_in); + // Note: assumes type must have binary_op (only used by set/frozenset). + return MP_OBJ_TYPE_GET_SLOT(type, binary_op)(MP_BINARY_OP_CONTAINS, lhs_in, rhs_in); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_op_contains_obj, op_contains); diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/pairheap.c b/non_catalog_apps/mp_flipper/lib/micropython/py/pairheap.c new file mode 100644 index 00000000..d3a011c4 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/pairheap.c @@ -0,0 +1,147 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Damien P. George + * + * 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 "py/pairheap.h" + +// The mp_pairheap_t.next pointer can take one of the following values: +// - NULL: the node is the top of the heap +// - LSB set: the node is the last of the children and points to its parent node +// - other: the node is a child and not the last child +// The macros below help manage this pointer. +#define NEXT_MAKE_RIGHTMOST_PARENT(parent) ((void *)((uintptr_t)(parent) | 1)) +#define NEXT_IS_RIGHTMOST_PARENT(next) ((uintptr_t)(next) & 1) +#define NEXT_GET_RIGHTMOST_PARENT(next) ((void *)((uintptr_t)(next) & ~1)) + +// O(1), stable +mp_pairheap_t *mp_pairheap_meld(mp_pairheap_lt_t lt, mp_pairheap_t *heap1, mp_pairheap_t *heap2) { + if (heap1 == NULL) { + return heap2; + } + if (heap2 == NULL) { + return heap1; + } + if (lt(heap1, heap2)) { + if (heap1->child == NULL) { + heap1->child = heap2; + } else { + heap1->child_last->next = heap2; + } + heap1->child_last = heap2; + heap2->next = NEXT_MAKE_RIGHTMOST_PARENT(heap1); + return heap1; + } else { + heap1->next = heap2->child; + heap2->child = heap1; + if (heap1->next == NULL) { + heap2->child_last = heap1; + heap1->next = NEXT_MAKE_RIGHTMOST_PARENT(heap2); + } + return heap2; + } +} + +// amortised O(log N), stable +mp_pairheap_t *mp_pairheap_pairing(mp_pairheap_lt_t lt, mp_pairheap_t *child) { + if (child == NULL) { + return NULL; + } + mp_pairheap_t *heap = NULL; + while (!NEXT_IS_RIGHTMOST_PARENT(child)) { + mp_pairheap_t *n1 = child; + child = child->next; + n1->next = NULL; + if (!NEXT_IS_RIGHTMOST_PARENT(child)) { + mp_pairheap_t *n2 = child; + child = child->next; + n2->next = NULL; + n1 = mp_pairheap_meld(lt, n1, n2); + } + heap = mp_pairheap_meld(lt, heap, n1); + } + heap->next = NULL; + return heap; +} + +// amortised O(log N), stable +mp_pairheap_t *mp_pairheap_delete(mp_pairheap_lt_t lt, mp_pairheap_t *heap, mp_pairheap_t *node) { + // Simple case of the top being the node to delete + if (node == heap) { + mp_pairheap_t *child = heap->child; + node->child = NULL; + return mp_pairheap_pairing(lt, child); + } + + // Case where node is not in the heap + if (node->next == NULL) { + return heap; + } + + // Find parent of node + mp_pairheap_t *parent = node; + while (!NEXT_IS_RIGHTMOST_PARENT(parent->next)) { + parent = parent->next; + } + parent = NEXT_GET_RIGHTMOST_PARENT(parent->next); + + // Replace node with pairing of its children + mp_pairheap_t *next; + if (node == parent->child && node->child == NULL) { + if (NEXT_IS_RIGHTMOST_PARENT(node->next)) { + parent->child = NULL; + } else { + parent->child = node->next; + } + node->next = NULL; + return heap; + } else if (node == parent->child) { + mp_pairheap_t *child = node->child; + next = node->next; + node->child = NULL; + node->next = NULL; + node = mp_pairheap_pairing(lt, child); + parent->child = node; + } else { + mp_pairheap_t *n = parent->child; + while (node != n->next) { + n = n->next; + } + mp_pairheap_t *child = node->child; + next = node->next; + node->child = NULL; + node->next = NULL; + node = mp_pairheap_pairing(lt, child); + if (node == NULL) { + node = n; + } else { + n->next = node; + } + } + node->next = next; + if (NEXT_IS_RIGHTMOST_PARENT(next)) { + parent->child_last = node; + } + return heap; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/pairheap.h b/non_catalog_apps/mp_flipper/lib/micropython/py/pairheap.h new file mode 100644 index 00000000..68b8b0f7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/pairheap.h @@ -0,0 +1,100 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_PAIRHEAP_H +#define MICROPY_INCLUDED_PY_PAIRHEAP_H + +// This is an implementation of a pairing heap. It is stable and has deletion +// support. Only the less-than operation needs to be defined on elements. +// +// See original paper for details: +// Michael L. Fredman, Robert Sedjewick, Daniel D. Sleator, and Robert E. Tarjan. +// The Pairing Heap: A New Form of Self-Adjusting Heap. +// Algorithmica 1:111-129, 1986. +// https://www.cs.cmu.edu/~sleator/papers/pairing-heaps.pdf + +#include +#include "py/obj.h" + +// This struct forms the nodes of the heap and is intended to be extended, by +// placing it first in another struct, to include additional information for the +// element stored in the heap. It includes "base" so it can be a MicroPython +// object allocated on the heap and the GC can automatically trace all nodes by +// following the tree structure. +typedef struct _mp_pairheap_t { + mp_obj_base_t base; + struct _mp_pairheap_t *child; + struct _mp_pairheap_t *child_last; + struct _mp_pairheap_t *next; +} mp_pairheap_t; + +// This is the function for the less-than operation on nodes/elements. +typedef int (*mp_pairheap_lt_t)(mp_pairheap_t *, mp_pairheap_t *); + +// Core functions. +mp_pairheap_t *mp_pairheap_meld(mp_pairheap_lt_t lt, mp_pairheap_t *heap1, mp_pairheap_t *heap2); +mp_pairheap_t *mp_pairheap_pairing(mp_pairheap_lt_t lt, mp_pairheap_t *child); +mp_pairheap_t *mp_pairheap_delete(mp_pairheap_lt_t lt, mp_pairheap_t *heap, mp_pairheap_t *node); + +// Create a new heap. +static inline mp_pairheap_t *mp_pairheap_new(mp_pairheap_lt_t lt) { + (void)lt; + return NULL; +} + +// Initialise a single pairing-heap node so it is ready to push on to a heap. +static inline void mp_pairheap_init_node(mp_pairheap_lt_t lt, mp_pairheap_t *node) { + (void)lt; + node->child = NULL; + node->next = NULL; +} + +// Test if the heap is empty. +static inline bool mp_pairheap_is_empty(mp_pairheap_lt_t lt, mp_pairheap_t *heap) { + (void)lt; + return heap == NULL; +} + +// Peek at the top of the heap. Will return NULL if empty. +static inline mp_pairheap_t *mp_pairheap_peek(mp_pairheap_lt_t lt, mp_pairheap_t *heap) { + (void)lt; + return heap; +} + +// Push new node onto existing heap. Returns the new heap. +static inline mp_pairheap_t *mp_pairheap_push(mp_pairheap_lt_t lt, mp_pairheap_t *heap, mp_pairheap_t *node) { + assert(node->child == NULL && node->next == NULL); + return mp_pairheap_meld(lt, node, heap); // node is first to be stable +} + +// Pop the top off the heap, which must not be empty. Returns the new heap. +static inline mp_pairheap_t *mp_pairheap_pop(mp_pairheap_lt_t lt, mp_pairheap_t *heap) { + assert(heap->next == NULL); + mp_pairheap_t *child = heap->child; + heap->child = NULL; + return mp_pairheap_pairing(lt, child); +} + +#endif // MICROPY_INCLUDED_PY_PAIRHEAP_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/parse.c b/non_catalog_apps/mp_flipper/lib/micropython/py/parse.c new file mode 100644 index 00000000..1392303e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/parse.c @@ -0,0 +1,1392 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 Damien P. George + * + * 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 // for ssize_t +#include +#include + +#include "py/lexer.h" +#include "py/parse.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "py/objint.h" +#include "py/objstr.h" +#include "py/builtin.h" + +#if MICROPY_ENABLE_COMPILER + +#define RULE_ACT_ARG_MASK (0x0f) +#define RULE_ACT_KIND_MASK (0x30) +#define RULE_ACT_ALLOW_IDENT (0x40) +#define RULE_ACT_ADD_BLANK (0x80) +#define RULE_ACT_OR (0x10) +#define RULE_ACT_AND (0x20) +#define RULE_ACT_LIST (0x30) + +#define RULE_ARG_KIND_MASK (0xf000) +#define RULE_ARG_ARG_MASK (0x0fff) +#define RULE_ARG_TOK (0x1000) +#define RULE_ARG_RULE (0x2000) +#define RULE_ARG_OPT_RULE (0x3000) + +// *FORMAT-OFF* + +enum { +// define rules with a compile function +#define DEF_RULE(rule, comp, kind, ...) RULE_##rule, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + RULE_const_object, // special node for a constant, generic Python object + +// define rules without a compile function +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) RULE_##rule, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +}; + +// Define an array of actions corresponding to each rule +static const uint8_t rule_act_table[] = { +#define or(n) (RULE_ACT_OR | n) +#define and(n) (RULE_ACT_AND | n) +#define and_ident(n) (RULE_ACT_AND | n | RULE_ACT_ALLOW_IDENT) +#define and_blank(n) (RULE_ACT_AND | n | RULE_ACT_ADD_BLANK) +#define one_or_more (RULE_ACT_LIST | 2) +#define list (RULE_ACT_LIST | 1) +#define list_with_end (RULE_ACT_LIST | 3) + +#define DEF_RULE(rule, comp, kind, ...) kind, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + + 0, // RULE_const_object + +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) kind, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + +#undef or +#undef and +#undef and_ident +#undef and_blank +#undef one_or_more +#undef list +#undef list_with_end +}; + +// Define the argument data for each rule, as a combined array +static const uint16_t rule_arg_combined_table[] = { +#define tok(t) (RULE_ARG_TOK | MP_TOKEN_##t) +#define rule(r) (RULE_ARG_RULE | RULE_##r) +#define opt_rule(r) (RULE_ARG_OPT_RULE | RULE_##r) + +#define DEF_RULE(rule, comp, kind, ...) __VA_ARGS__, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) __VA_ARGS__, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + +#undef tok +#undef rule +#undef opt_rule +}; + +// Macro to create a list of N identifiers where N is the number of variable arguments to the macro +#define RULE_EXPAND(x) x +#define RULE_PADDING(rule, ...) RULE_PADDING2(rule, __VA_ARGS__, RULE_PADDING_IDS(rule)) +#define RULE_PADDING2(rule, ...) RULE_EXPAND(RULE_PADDING3(rule, __VA_ARGS__)) +#define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) __VA_ARGS__ +#define RULE_PADDING_IDS(r) PAD13_##r, PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, + +// Use an enum to create constants specifying how much room a rule takes in rule_arg_combined_table +enum { +#define DEF_RULE(rule, comp, kind, ...) RULE_PADDING(rule, __VA_ARGS__) +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) RULE_PADDING(rule, __VA_ARGS__) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +}; + +// Macro to compute the start of a rule in rule_arg_combined_table +#define RULE_ARG_OFFSET(rule, ...) RULE_ARG_OFFSET2(rule, __VA_ARGS__, RULE_ARG_OFFSET_IDS(rule)) +#define RULE_ARG_OFFSET2(rule, ...) RULE_EXPAND(RULE_ARG_OFFSET3(rule, __VA_ARGS__)) +#define RULE_ARG_OFFSET3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) _14 +#define RULE_ARG_OFFSET_IDS(r) PAD13_##r, PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, PAD0_##r, + +// Use the above enum values to create a table of offsets for each rule's arg +// data, which indexes rule_arg_combined_table. The offsets require 9 bits of +// storage but only the lower 8 bits are stored here. The 9th bit is computed +// in get_rule_arg using the FIRST_RULE_WITH_OFFSET_ABOVE_255 constant. +static const uint8_t rule_arg_offset_table[] = { +#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) & 0xff, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + 0, // RULE_const_object +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) & 0xff, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +}; + +// Define a constant that's used to determine the 9th bit of the values in rule_arg_offset_table +static const size_t FIRST_RULE_WITH_OFFSET_ABOVE_255 = +#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) >= 0x100 ? RULE_##rule : +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) >= 0x100 ? RULE_##rule : +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +0; + +#if MICROPY_DEBUG_PARSE_RULE_NAME +// Define an array of rule names corresponding to each rule +static const char *const rule_name_table[] = { +#define DEF_RULE(rule, comp, kind, ...) #rule, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + "", // RULE_const_object +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) #rule, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +}; +#endif + +// *FORMAT-ON* + +typedef struct _rule_stack_t { + size_t src_line : (8 * sizeof(size_t) - 8); // maximum bits storing source line number + size_t rule_id : 8; // this must be large enough to fit largest rule number + size_t arg_i; // this dictates the maximum nodes in a "list" of things +} rule_stack_t; + +typedef struct _mp_parse_chunk_t { + size_t alloc; + union { + size_t used; + struct _mp_parse_chunk_t *next; + } union_; + byte data[]; +} mp_parse_chunk_t; + +typedef struct _parser_t { + size_t rule_stack_alloc; + size_t rule_stack_top; + rule_stack_t *rule_stack; + + size_t result_stack_alloc; + size_t result_stack_top; + mp_parse_node_t *result_stack; + + mp_lexer_t *lexer; + + mp_parse_tree_t tree; + mp_parse_chunk_t *cur_chunk; + + #if MICROPY_COMP_CONST + mp_map_t consts; + #endif +} parser_t; + +static void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args); + +static const uint16_t *get_rule_arg(uint8_t r_id) { + size_t off = rule_arg_offset_table[r_id]; + if (r_id >= FIRST_RULE_WITH_OFFSET_ABOVE_255) { + off |= 0x100; + } + return &rule_arg_combined_table[off]; +} + +static void *parser_alloc(parser_t *parser, size_t num_bytes) { + // use a custom memory allocator to store parse nodes sequentially in large chunks + + mp_parse_chunk_t *chunk = parser->cur_chunk; + + if (chunk != NULL && chunk->union_.used + num_bytes > chunk->alloc) { + // not enough room at end of previously allocated chunk so try to grow + mp_parse_chunk_t *new_data = (mp_parse_chunk_t *)m_renew_maybe(byte, chunk, + sizeof(mp_parse_chunk_t) + chunk->alloc, + sizeof(mp_parse_chunk_t) + chunk->alloc + num_bytes, false); + if (new_data == NULL) { + // could not grow existing memory; shrink it to fit previous + (void)m_renew_maybe(byte, chunk, sizeof(mp_parse_chunk_t) + chunk->alloc, + sizeof(mp_parse_chunk_t) + chunk->union_.used, false); + chunk->alloc = chunk->union_.used; + chunk->union_.next = parser->tree.chunk; + parser->tree.chunk = chunk; + chunk = NULL; + } else { + // could grow existing memory + chunk->alloc += num_bytes; + } + } + + if (chunk == NULL) { + // no previous chunk, allocate a new chunk + size_t alloc = MICROPY_ALLOC_PARSE_CHUNK_INIT; + if (alloc < num_bytes) { + alloc = num_bytes; + } + chunk = (mp_parse_chunk_t *)m_new(byte, sizeof(mp_parse_chunk_t) + alloc); + chunk->alloc = alloc; + chunk->union_.used = 0; + parser->cur_chunk = chunk; + } + + byte *ret = chunk->data + chunk->union_.used; + chunk->union_.used += num_bytes; + return ret; +} + +#if MICROPY_COMP_CONST_TUPLE +static void parser_free_parse_node_struct(parser_t *parser, mp_parse_node_struct_t *pns) { + mp_parse_chunk_t *chunk = parser->cur_chunk; + if (chunk->data <= (byte *)pns && (byte *)pns < chunk->data + chunk->union_.used) { + size_t num_bytes = sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + chunk->union_.used -= num_bytes; + } +} +#endif + +static void push_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t arg_i) { + if (parser->rule_stack_top >= parser->rule_stack_alloc) { + rule_stack_t *rs = m_renew(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc + MICROPY_ALLOC_PARSE_RULE_INC); + parser->rule_stack = rs; + parser->rule_stack_alloc += MICROPY_ALLOC_PARSE_RULE_INC; + } + rule_stack_t *rs = &parser->rule_stack[parser->rule_stack_top++]; + rs->src_line = src_line; + rs->rule_id = rule_id; + rs->arg_i = arg_i; +} + +static void push_rule_from_arg(parser_t *parser, size_t arg) { + assert((arg & RULE_ARG_KIND_MASK) == RULE_ARG_RULE || (arg & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE); + size_t rule_id = arg & RULE_ARG_ARG_MASK; + push_rule(parser, parser->lexer->tok_line, rule_id, 0); +} + +static uint8_t pop_rule(parser_t *parser, size_t *arg_i, size_t *src_line) { + parser->rule_stack_top -= 1; + uint8_t rule_id = parser->rule_stack[parser->rule_stack_top].rule_id; + *arg_i = parser->rule_stack[parser->rule_stack_top].arg_i; + *src_line = parser->rule_stack[parser->rule_stack_top].src_line; + return rule_id; +} + +#if MICROPY_COMP_CONST_TUPLE +static uint8_t peek_rule(parser_t *parser, size_t n) { + assert(parser->rule_stack_top > n); + return parser->rule_stack[parser->rule_stack_top - 1 - n].rule_id; +} +#endif + +bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { + if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { + *o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn)); + return true; + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + *o = mp_parse_node_extract_const_object(pns); + return mp_obj_is_int(*o); + } else { + return false; + } +} + +#if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST +static bool mp_parse_node_is_const(mp_parse_node_t pn) { + if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { + // Small integer. + return true; + } else if (MP_PARSE_NODE_IS_LEAF(pn)) { + // Possible str, or constant literal. + uintptr_t kind = MP_PARSE_NODE_LEAF_KIND(pn); + if (kind == MP_PARSE_NODE_STRING) { + return true; + } else if (kind == MP_PARSE_NODE_TOKEN) { + uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); + return arg == MP_TOKEN_KW_NONE + || arg == MP_TOKEN_KW_FALSE + || arg == MP_TOKEN_KW_TRUE + || arg == MP_TOKEN_ELLIPSIS; + } + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { + // Constant object. + return true; + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_atom_paren)) { + // Possible empty tuple. + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + return MP_PARSE_NODE_IS_NULL(pns->nodes[0]); + } + return false; +} + +static mp_obj_t mp_parse_node_convert_to_obj(mp_parse_node_t pn) { + assert(mp_parse_node_is_const(pn)); + if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { + mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + #if MICROPY_DYNAMIC_COMPILER + mp_uint_t sign_mask = -((mp_uint_t)1 << (mp_dynamic_compiler.small_int_bits - 1)); + if (!((arg & sign_mask) == 0 || (arg & sign_mask) == sign_mask)) { + // Integer doesn't fit in a small-int, so create a multi-precision int object. + return mp_obj_new_int_from_ll(arg); + } + #endif + return MP_OBJ_NEW_SMALL_INT(arg); + } else if (MP_PARSE_NODE_IS_LEAF(pn)) { + uintptr_t kind = MP_PARSE_NODE_LEAF_KIND(pn); + uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); + if (kind == MP_PARSE_NODE_STRING) { + return MP_OBJ_NEW_QSTR(arg); + } else { + assert(MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN); + switch (arg) { + case MP_TOKEN_KW_NONE: + return mp_const_none; + case MP_TOKEN_KW_FALSE: + return mp_const_false; + case MP_TOKEN_KW_TRUE: + return mp_const_true; + default: + assert(arg == MP_TOKEN_ELLIPSIS); + return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); + } + } + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + return mp_parse_node_extract_const_object(pns); + } else { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_atom_paren)); + assert(MP_PARSE_NODE_IS_NULL(((mp_parse_node_struct_t *)pn)->nodes[0])); + return mp_const_empty_tuple; + } +} +#endif + +static bool parse_node_is_const_bool(mp_parse_node_t pn, bool value) { + // Returns true if 'pn' is a constant whose boolean value is equivalent to 'value' + #if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST + return mp_parse_node_is_const(pn) && mp_obj_is_true(mp_parse_node_convert_to_obj(pn)) == value; + #else + return MP_PARSE_NODE_IS_TOKEN_KIND(pn, value ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE) + || (MP_PARSE_NODE_IS_SMALL_INT(pn) && !!MP_PARSE_NODE_LEAF_SMALL_INT(pn) == value); + #endif +} + +bool mp_parse_node_is_const_false(mp_parse_node_t pn) { + return parse_node_is_const_bool(pn, false); +} + +bool mp_parse_node_is_const_true(mp_parse_node_t pn) { + return parse_node_is_const_bool(pn, true); +} + +size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes) { + if (MP_PARSE_NODE_IS_NULL(*pn)) { + *nodes = NULL; + return 0; + } else if (MP_PARSE_NODE_IS_LEAF(*pn)) { + *nodes = pn; + return 1; + } else { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)(*pn); + if (MP_PARSE_NODE_STRUCT_KIND(pns) != pn_kind) { + *nodes = pn; + return 1; + } else { + *nodes = pns->nodes; + return MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + } + } +} + +#if MICROPY_DEBUG_PRINTERS +void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t indent) { + if (MP_PARSE_NODE_IS_STRUCT(pn)) { + mp_printf(print, "[% 4d] ", (int)((mp_parse_node_struct_t *)pn)->source_line); + } else { + mp_printf(print, " "); + } + for (size_t i = 0; i < indent; i++) { + mp_printf(print, " "); + } + if (MP_PARSE_NODE_IS_NULL(pn)) { + mp_printf(print, "NULL\n"); + } else if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { + mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + mp_printf(print, "int(" INT_FMT ")\n", arg); + } else if (MP_PARSE_NODE_IS_LEAF(pn)) { + uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); + switch (MP_PARSE_NODE_LEAF_KIND(pn)) { + case MP_PARSE_NODE_ID: + mp_printf(print, "id(%s)\n", qstr_str(arg)); + break; + case MP_PARSE_NODE_STRING: + mp_printf(print, "str(%s)\n", qstr_str(arg)); + break; + default: + assert(MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN); + mp_printf(print, "tok(%u)\n", (uint)arg); + break; + } + } else { + // node must be a mp_parse_node_struct_t + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + if (MP_PARSE_NODE_STRUCT_KIND(pns) == RULE_const_object) { + mp_obj_t obj = mp_parse_node_extract_const_object(pns); + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + mp_printf(print, "literal const(%016llx)=", obj); + #else + mp_printf(print, "literal const(%p)=", obj); + #endif + mp_obj_print_helper(print, obj, PRINT_REPR); + mp_printf(print, "\n"); + } else { + size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + #if MICROPY_DEBUG_PARSE_RULE_NAME + mp_printf(print, "%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); + #else + mp_printf(print, "rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); + #endif + for (size_t i = 0; i < n; i++) { + mp_parse_node_print(print, pns->nodes[i], indent + 2); + } + } + } +} +#endif // MICROPY_DEBUG_PRINTERS + +/* +static void result_stack_show(const mp_print_t *print, parser_t *parser) { + mp_printf(print, "result stack, most recent first\n"); + for (ssize_t i = parser->result_stack_top - 1; i >= 0; i--) { + mp_parse_node_print(print, parser->result_stack[i], 0); + } +} +*/ + +static mp_parse_node_t pop_result(parser_t *parser) { + assert(parser->result_stack_top > 0); + return parser->result_stack[--parser->result_stack_top]; +} + +static mp_parse_node_t peek_result(parser_t *parser, size_t pos) { + assert(parser->result_stack_top > pos); + return parser->result_stack[parser->result_stack_top - 1 - pos]; +} + +static void push_result_node(parser_t *parser, mp_parse_node_t pn) { + if (parser->result_stack_top >= parser->result_stack_alloc) { + mp_parse_node_t *stack = m_renew(mp_parse_node_t, parser->result_stack, parser->result_stack_alloc, parser->result_stack_alloc + MICROPY_ALLOC_PARSE_RESULT_INC); + parser->result_stack = stack; + parser->result_stack_alloc += MICROPY_ALLOC_PARSE_RESULT_INC; + } + parser->result_stack[parser->result_stack_top++] = pn; +} + +static mp_parse_node_t make_node_const_object(parser_t *parser, size_t src_line, mp_obj_t obj) { + mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_obj_t)); + pn->source_line = src_line; + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + // nodes are 32-bit pointers, but need to store 64-bit object + pn->kind_num_nodes = RULE_const_object | (2 << 8); + pn->nodes[0] = (uint64_t)obj; + pn->nodes[1] = (uint64_t)obj >> 32; + #else + pn->kind_num_nodes = RULE_const_object | (1 << 8); + pn->nodes[0] = (uintptr_t)obj; + #endif + return (mp_parse_node_t)pn; +} + +// Create a parse node representing a constant object, possibly optimising the case of +// an integer, by putting the (small) integer value directly in the parse node itself. +static mp_parse_node_t make_node_const_object_optimised(parser_t *parser, size_t src_line, mp_obj_t obj) { + if (mp_obj_is_small_int(obj)) { + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(obj); + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + // A parse node is only 32-bits and the small-int value must fit in 31-bits + if (((val ^ (val << 1)) & 0xffffffff80000000) != 0) { + return make_node_const_object(parser, src_line, obj); + } + #endif + #if MICROPY_DYNAMIC_COMPILER + // Check that the integer value fits in target runtime's small-int + mp_uint_t sign_mask = -((mp_uint_t)1 << (mp_dynamic_compiler.small_int_bits - 1)); + if (!((val & sign_mask) == 0 || (val & sign_mask) == sign_mask)) { + return make_node_const_object(parser, src_line, obj); + } + #endif + return mp_parse_node_new_small_int(val); + } else { + return make_node_const_object(parser, src_line, obj); + } +} + +static void push_result_token(parser_t *parser, uint8_t rule_id) { + mp_parse_node_t pn; + mp_lexer_t *lex = parser->lexer; + if (lex->tok_kind == MP_TOKEN_NAME) { + qstr id = qstr_from_strn(lex->vstr.buf, lex->vstr.len); + #if MICROPY_COMP_CONST + // if name is a standalone identifier, look it up in the table of dynamic constants + mp_map_elem_t *elem; + if (rule_id == RULE_atom + && (elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP)) != NULL) { + pn = make_node_const_object_optimised(parser, lex->tok_line, elem->value); + } else { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id); + } + #else + (void)rule_id; + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id); + #endif + } else if (lex->tok_kind == MP_TOKEN_INTEGER) { + mp_obj_t o = mp_parse_num_integer(lex->vstr.buf, lex->vstr.len, 0, lex); + pn = make_node_const_object_optimised(parser, lex->tok_line, o); + } else if (lex->tok_kind == MP_TOKEN_FLOAT_OR_IMAG) { + mp_obj_t o = mp_parse_num_float(lex->vstr.buf, lex->vstr.len, true, lex); + pn = make_node_const_object(parser, lex->tok_line, o); + } else if (lex->tok_kind == MP_TOKEN_STRING) { + // Don't automatically intern all strings. Doc strings (which are usually large) + // will be discarded by the compiler, and so we shouldn't intern them. + qstr qst = MP_QSTRnull; + if (lex->vstr.len <= MICROPY_ALLOC_PARSE_INTERN_STRING_LEN) { + // intern short strings + qst = qstr_from_strn(lex->vstr.buf, lex->vstr.len); + } else { + // check if this string is already interned + qst = qstr_find_strn(lex->vstr.buf, lex->vstr.len); + } + if (qst != MP_QSTRnull) { + // qstr exists, make a leaf node + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_STRING, qst); + } else { + // not interned, make a node holding a pointer to the string object + mp_obj_t o = mp_obj_new_str_copy(&mp_type_str, (const byte *)lex->vstr.buf, lex->vstr.len); + pn = make_node_const_object(parser, lex->tok_line, o); + } + } else if (lex->tok_kind == MP_TOKEN_BYTES) { + // make a node holding a pointer to the bytes object + mp_obj_t o = mp_obj_new_bytes((const byte *)lex->vstr.buf, lex->vstr.len); + pn = make_node_const_object(parser, lex->tok_line, o); + } else { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, lex->tok_kind); + } + push_result_node(parser, pn); +} + +#if MICROPY_COMP_CONST_FOLDING + +#if MICROPY_COMP_MODULE_CONST +static const mp_rom_map_elem_t mp_constants_table[] = { + #if MICROPY_PY_ERRNO + { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_module_errno) }, + #endif + #if MICROPY_PY_UCTYPES + { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) }, + #endif + // Extra constants as defined by a port + MICROPY_PORT_CONSTANTS +}; +static MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); +#endif + +#if MICROPY_COMP_CONST_FOLDING_COMPILER_WORKAROUND +// Some versions of the xtensa-esp32-elf-gcc compiler generate wrong code if this +// function is static, so provide a hook for them to work around this problem. +MP_NOINLINE +#endif +static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *num_args) { + if (rule_id == RULE_or_test + || rule_id == RULE_and_test) { + // folding for binary logical ops: or and + size_t copy_to = *num_args; + for (size_t i = copy_to; i > 0;) { + mp_parse_node_t pn = peek_result(parser, --i); + parser->result_stack[parser->result_stack_top - copy_to] = pn; + if (i == 0) { + // always need to keep the last value + break; + } + if (rule_id == RULE_or_test) { + if (mp_parse_node_is_const_true(pn)) { + // + break; + } else if (!mp_parse_node_is_const_false(pn)) { + copy_to -= 1; + } + } else { + // RULE_and_test + if (mp_parse_node_is_const_false(pn)) { + break; + } else if (!mp_parse_node_is_const_true(pn)) { + copy_to -= 1; + } + } + } + copy_to -= 1; // copy_to now contains number of args to pop + + // pop and discard all the short-circuited expressions + for (size_t i = 0; i < copy_to; ++i) { + pop_result(parser); + } + *num_args -= copy_to; + + // we did a complete folding if there's only 1 arg left + return *num_args == 1; + + } else if (rule_id == RULE_not_test_2) { + // folding for unary logical op: not + mp_parse_node_t pn = peek_result(parser, 0); + if (mp_parse_node_is_const_false(pn)) { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, MP_TOKEN_KW_TRUE); + } else if (mp_parse_node_is_const_true(pn)) { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, MP_TOKEN_KW_FALSE); + } else { + return false; + } + pop_result(parser); + push_result_node(parser, pn); + return true; + } + + return false; +} + +static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { + // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4 + // it does not do partial folding, eg 1 + 2 + x -> 3 + x + + mp_obj_t arg0; + if (rule_id == RULE_expr + || rule_id == RULE_xor_expr + || rule_id == RULE_and_expr + || rule_id == RULE_power) { + // folding for binary ops: | ^ & ** + mp_parse_node_t pn = peek_result(parser, num_args - 1); + if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + return false; + } + mp_binary_op_t op; + if (rule_id == RULE_expr) { + op = MP_BINARY_OP_OR; + } else if (rule_id == RULE_xor_expr) { + op = MP_BINARY_OP_XOR; + } else if (rule_id == RULE_and_expr) { + op = MP_BINARY_OP_AND; + } else { + op = MP_BINARY_OP_POWER; + } + for (ssize_t i = num_args - 2; i >= 0; --i) { + pn = peek_result(parser, i); + mp_obj_t arg1; + if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + return false; + } + if (op == MP_BINARY_OP_POWER && mp_obj_int_sign(arg1) < 0) { + // ** can't have negative rhs + return false; + } + arg0 = mp_binary_op(op, arg0, arg1); + } + } else if (rule_id == RULE_shift_expr + || rule_id == RULE_arith_expr + || rule_id == RULE_term) { + // folding for binary ops: << >> + - * @ / % // + mp_parse_node_t pn = peek_result(parser, num_args - 1); + if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + return false; + } + for (ssize_t i = num_args - 2; i >= 1; i -= 2) { + pn = peek_result(parser, i - 1); + mp_obj_t arg1; + if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + return false; + } + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i)); + if (tok == MP_TOKEN_OP_AT || tok == MP_TOKEN_OP_SLASH) { + // Can't fold @ or / + return false; + } + mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); + int rhs_sign = mp_obj_int_sign(arg1); + if (op <= MP_BINARY_OP_RSHIFT) { + // << and >> can't have negative rhs + if (rhs_sign < 0) { + return false; + } + } else if (op >= MP_BINARY_OP_FLOOR_DIVIDE) { + // % and // can't have zero rhs + if (rhs_sign == 0) { + return false; + } + } + arg0 = mp_binary_op(op, arg0, arg1); + } + } else if (rule_id == RULE_factor_2) { + // folding for unary ops: + - ~ + mp_parse_node_t pn = peek_result(parser, 0); + if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + return false; + } + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, 1)); + mp_unary_op_t op; + if (tok == MP_TOKEN_OP_TILDE) { + op = MP_UNARY_OP_INVERT; + } else { + assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); // should be + op = MP_UNARY_OP_POSITIVE + (tok - MP_TOKEN_OP_PLUS); + } + arg0 = mp_unary_op(op, arg0); + + #if MICROPY_COMP_CONST + } else if (rule_id == RULE_expr_stmt) { + mp_parse_node_t pn1 = peek_result(parser, 0); + if (!MP_PARSE_NODE_IS_NULL(pn1) + && !(MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_augassign) + || MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_assign_list))) { + // this node is of the form = + mp_parse_node_t pn0 = peek_result(parser, 1); + if (MP_PARSE_NODE_IS_ID(pn0) + && MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_atom_expr_normal) + && MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t *)pn1)->nodes[0]) + && MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t *)pn1)->nodes[0]) == MP_QSTR_const + && MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t *)pn1)->nodes[1], RULE_trailer_paren) + ) { + // code to assign dynamic constants: id = const(value) + + // get the id + qstr id = MP_PARSE_NODE_LEAF_ARG(pn0); + + // get the value + mp_parse_node_t pn_value = ((mp_parse_node_struct_t *)((mp_parse_node_struct_t *)pn1)->nodes[1])->nodes[0]; + if (!mp_parse_node_is_const(pn_value)) { + mp_obj_t exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + MP_ERROR_TEXT("not a constant")); + mp_obj_exception_add_traceback(exc, parser->lexer->source_name, + ((mp_parse_node_struct_t *)pn1)->source_line, MP_QSTRnull); + nlr_raise(exc); + } + mp_obj_t value = mp_parse_node_convert_to_obj(pn_value); + + // store the value in the table of dynamic constants + mp_map_elem_t *elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + assert(elem->value == MP_OBJ_NULL); + elem->value = value; + + // If the constant starts with an underscore then treat it as a private + // variable and don't emit any code to store the value to the id. + if (qstr_str(id)[0] == '_') { + pop_result(parser); // pop const(value) + pop_result(parser); // pop id + push_result_rule(parser, 0, RULE_pass_stmt, 0); // replace with "pass" + return true; + } + + // replace const(value) with value + pop_result(parser); + push_result_node(parser, pn_value); + + // finished folding this assignment, but we still want it to be part of the tree + return false; + } + } + return false; + #endif + + #if MICROPY_COMP_MODULE_CONST + } else if (rule_id == RULE_atom_expr_normal) { + mp_parse_node_t pn0 = peek_result(parser, 1); + mp_parse_node_t pn1 = peek_result(parser, 0); + if (!(MP_PARSE_NODE_IS_ID(pn0) + && MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_trailer_period))) { + return false; + } + // id1.id2 + // look it up in constant table, see if it can be replaced with an integer + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn1; + assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); + qstr q_base = MP_PARSE_NODE_LEAF_ARG(pn0); + qstr q_attr = MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]); + mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)&mp_constants_map, MP_OBJ_NEW_QSTR(q_base), MP_MAP_LOOKUP); + if (elem == NULL) { + return false; + } + mp_obj_t dest[2]; + mp_load_method_maybe(elem->value, q_attr, dest); + if (!(dest[0] != MP_OBJ_NULL && mp_obj_is_int(dest[0]) && dest[1] == MP_OBJ_NULL)) { + return false; + } + arg0 = dest[0]; + #endif + + } else { + return false; + } + + // success folding this rule + + for (size_t i = num_args; i > 0; i--) { + pop_result(parser); + } + push_result_node(parser, make_node_const_object_optimised(parser, 0, arg0)); + + return true; +} + +#endif // MICROPY_COMP_CONST_FOLDING + +#if MICROPY_COMP_CONST_TUPLE +static bool build_tuple_from_stack(parser_t *parser, size_t src_line, size_t num_args) { + for (size_t i = num_args; i > 0;) { + mp_parse_node_t pn = peek_result(parser, --i); + if (!mp_parse_node_is_const(pn)) { + return false; + } + } + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_args, NULL)); + for (size_t i = num_args; i > 0;) { + mp_parse_node_t pn = pop_result(parser); + tuple->items[--i] = mp_parse_node_convert_to_obj(pn); + if (MP_PARSE_NODE_IS_STRUCT(pn)) { + parser_free_parse_node_struct(parser, (mp_parse_node_struct_t *)pn); + } + } + push_result_node(parser, make_node_const_object(parser, src_line, MP_OBJ_FROM_PTR(tuple))); + return true; +} + +static bool build_tuple(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args) { + if (rule_id == RULE_testlist_comp) { + if (peek_rule(parser, 0) == RULE_atom_paren) { + // Tuple of the form "(a,)". + return build_tuple_from_stack(parser, src_line, num_args); + } + } + if (rule_id == RULE_testlist_comp_3c) { + assert(peek_rule(parser, 0) == RULE_testlist_comp_3b); + assert(peek_rule(parser, 1) == RULE_testlist_comp); + if (peek_rule(parser, 2) == RULE_atom_paren) { + // Tuple of the form "(a, b)". + if (build_tuple_from_stack(parser, src_line, num_args)) { + parser->rule_stack_top -= 2; // discard 2 rules + return true; + } + } + } + if (rule_id == RULE_testlist_star_expr + || rule_id == RULE_testlist + || rule_id == RULE_subscriptlist) { + // Tuple of the form: + // - x = a, b + // - return a, b + // - for x in a, b: pass + // - x[a, b] + return build_tuple_from_stack(parser, src_line, num_args); + } + + return false; +} +#endif + +static void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args) { + // Simplify and optimise certain rules, to reduce memory usage and simplify the compiler. + if (rule_id == RULE_atom_paren) { + // Remove parenthesis around a single expression if possible. + // This atom_paren rule always has a single argument, and after this + // optimisation that argument is either NULL or testlist_comp. + mp_parse_node_t pn = peek_result(parser, 0); + if (MP_PARSE_NODE_IS_NULL(pn)) { + // need to keep parenthesis for () + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_testlist_comp)) { + // need to keep parenthesis for (a, b, ...) + } else { + // parenthesis around a single expression, so it's just the expression + return; + } + } else if (rule_id == RULE_testlist_comp) { + // The testlist_comp rule can be the sole argument to either atom_parent + // or atom_bracket, for (...) and [...] respectively. + assert(num_args == 2); + mp_parse_node_t pn = peek_result(parser, 0); + if (MP_PARSE_NODE_IS_STRUCT(pn)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + if (MP_PARSE_NODE_STRUCT_KIND(pns) == RULE_testlist_comp_3b) { + // tuple of one item, with trailing comma + pop_result(parser); + --num_args; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == RULE_testlist_comp_3c) { + // tuple of many items, convert testlist_comp_3c to testlist_comp + pop_result(parser); + assert(pn == peek_result(parser, 0)); + pns->kind_num_nodes = rule_id | MP_PARSE_NODE_STRUCT_NUM_NODES(pns) << 8; + return; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == RULE_comp_for) { + // generator expression + } else { + // tuple with 2 items + } + } else { + // tuple with 2 items + } + } else if (rule_id == RULE_testlist_comp_3c) { + // steal first arg of outer testlist_comp rule + ++num_args; + } + + #if MICROPY_COMP_CONST_FOLDING + if (fold_logical_constants(parser, rule_id, &num_args)) { + // we folded this rule so return straight away + return; + } + if (fold_constants(parser, rule_id, num_args)) { + // we folded this rule so return straight away + return; + } + #endif + + #if MICROPY_COMP_CONST_TUPLE + if (build_tuple(parser, src_line, rule_id, num_args)) { + // we built a tuple from this rule so return straight away + return; + } + #endif + + mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * num_args); + pn->source_line = src_line; + pn->kind_num_nodes = (rule_id & 0xff) | (num_args << 8); + for (size_t i = num_args; i > 0; i--) { + pn->nodes[i - 1] = pop_result(parser); + } + if (rule_id == RULE_testlist_comp_3c) { + // need to push something non-null to replace stolen first arg of testlist_comp + push_result_node(parser, (mp_parse_node_t)pn); + } + push_result_node(parser, (mp_parse_node_t)pn); +} + +mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { + // Set exception handler to free the lexer if an exception is raised. + MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, mp_lexer_free, lex); + nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback); + + // initialise parser and allocate memory for its stacks + + parser_t parser; + + parser.rule_stack_alloc = MICROPY_ALLOC_PARSE_RULE_INIT; + parser.rule_stack_top = 0; + parser.rule_stack = m_new(rule_stack_t, parser.rule_stack_alloc); + + parser.result_stack_alloc = MICROPY_ALLOC_PARSE_RESULT_INIT; + parser.result_stack_top = 0; + parser.result_stack = m_new(mp_parse_node_t, parser.result_stack_alloc); + + parser.lexer = lex; + + parser.tree.chunk = NULL; + parser.cur_chunk = NULL; + + #if MICROPY_COMP_CONST + mp_map_init(&parser.consts, 0); + #endif + + // work out the top-level rule to use, and push it on the stack + size_t top_level_rule; + switch (input_kind) { + case MP_PARSE_SINGLE_INPUT: + top_level_rule = RULE_single_input; + break; + case MP_PARSE_EVAL_INPUT: + top_level_rule = RULE_eval_input; + break; + default: + top_level_rule = RULE_file_input; + } + push_rule(&parser, lex->tok_line, top_level_rule, 0); + + // parse! + + bool backtrack = false; + + for (;;) { + next_rule: + if (parser.rule_stack_top == 0) { + break; + } + + // Pop the next rule to process it + size_t i; // state for the current rule + size_t rule_src_line; // source line for the first token matched by the current rule + uint8_t rule_id = pop_rule(&parser, &i, &rule_src_line); + uint8_t rule_act = rule_act_table[rule_id]; + const uint16_t *rule_arg = get_rule_arg(rule_id); + size_t n = rule_act & RULE_ACT_ARG_MASK; + + #if 0 + // debugging + printf("depth=" UINT_FMT " ", parser.rule_stack_top); + for (int j = 0; j < parser.rule_stack_top; ++j) { + printf(" "); + } + printf("%s n=" UINT_FMT " i=" UINT_FMT " bt=%d\n", rule_name_table[rule_id], n, i, backtrack); + #endif + + switch (rule_act & RULE_ACT_KIND_MASK) { + case RULE_ACT_OR: + if (i > 0 && !backtrack) { + goto next_rule; + } else { + backtrack = false; + } + for (; i < n; ++i) { + uint16_t kind = rule_arg[i] & RULE_ARG_KIND_MASK; + if (kind == RULE_ARG_TOK) { + if (lex->tok_kind == (rule_arg[i] & RULE_ARG_ARG_MASK)) { + push_result_token(&parser, rule_id); + mp_lexer_to_next(lex); + goto next_rule; + } + } else { + assert(kind == RULE_ARG_RULE); + if (i + 1 < n) { + push_rule(&parser, rule_src_line, rule_id, i + 1); // save this or-rule + } + push_rule_from_arg(&parser, rule_arg[i]); // push child of or-rule + goto next_rule; + } + } + backtrack = true; + break; + + case RULE_ACT_AND: { + + // failed, backtrack if we can, else syntax error + if (backtrack) { + assert(i > 0); + if ((rule_arg[i - 1] & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE) { + // an optional rule that failed, so continue with next arg + push_result_node(&parser, MP_PARSE_NODE_NULL); + backtrack = false; + } else { + // a mandatory rule that failed, so propagate backtrack + if (i > 1) { + // already eaten tokens so can't backtrack + goto syntax_error; + } else { + goto next_rule; + } + } + } + + // progress through the rule + for (; i < n; ++i) { + if ((rule_arg[i] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { + // need to match a token + mp_token_kind_t tok_kind = rule_arg[i] & RULE_ARG_ARG_MASK; + if (lex->tok_kind == tok_kind) { + // matched token + if (tok_kind == MP_TOKEN_NAME) { + push_result_token(&parser, rule_id); + } + mp_lexer_to_next(lex); + } else { + // failed to match token + if (i > 0) { + // already eaten tokens so can't backtrack + goto syntax_error; + } else { + // this rule failed, so backtrack + backtrack = true; + goto next_rule; + } + } + } else { + push_rule(&parser, rule_src_line, rule_id, i + 1); // save this and-rule + push_rule_from_arg(&parser, rule_arg[i]); // push child of and-rule + goto next_rule; + } + } + + assert(i == n); + + // matched the rule, so now build the corresponding parse_node + + #if !MICROPY_ENABLE_DOC_STRING + // this code discards lonely statements, such as doc strings + if (input_kind != MP_PARSE_SINGLE_INPUT && rule_id == RULE_expr_stmt && peek_result(&parser, 0) == MP_PARSE_NODE_NULL) { + mp_parse_node_t p = peek_result(&parser, 1); + if ((MP_PARSE_NODE_IS_LEAF(p) && !MP_PARSE_NODE_IS_ID(p)) + || MP_PARSE_NODE_IS_STRUCT_KIND(p, RULE_const_object)) { + pop_result(&parser); // MP_PARSE_NODE_NULL + pop_result(&parser); // const expression (leaf or RULE_const_object) + // Pushing the "pass" rule here will overwrite any RULE_const_object + // entry that was on the result stack, allowing the GC to reclaim + // the memory from the const object when needed. + push_result_rule(&parser, rule_src_line, RULE_pass_stmt, 0); + break; + } + } + #endif + + // count number of arguments for the parse node + i = 0; + size_t num_not_nil = 0; + for (size_t x = n; x > 0;) { + --x; + if ((rule_arg[x] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { + mp_token_kind_t tok_kind = rule_arg[x] & RULE_ARG_ARG_MASK; + if (tok_kind == MP_TOKEN_NAME) { + // only tokens which were names are pushed to stack + i += 1; + num_not_nil += 1; + } + } else { + // rules are always pushed + if (peek_result(&parser, i) != MP_PARSE_NODE_NULL) { + num_not_nil += 1; + } + i += 1; + } + } + + if (num_not_nil == 1 && (rule_act & RULE_ACT_ALLOW_IDENT)) { + // this rule has only 1 argument and should not be emitted + mp_parse_node_t pn = MP_PARSE_NODE_NULL; + for (size_t x = 0; x < i; ++x) { + mp_parse_node_t pn2 = pop_result(&parser); + if (pn2 != MP_PARSE_NODE_NULL) { + pn = pn2; + } + } + push_result_node(&parser, pn); + } else { + // this rule must be emitted + + if (rule_act & RULE_ACT_ADD_BLANK) { + // and add an extra blank node at the end (used by the compiler to store data) + push_result_node(&parser, MP_PARSE_NODE_NULL); + i += 1; + } + + push_result_rule(&parser, rule_src_line, rule_id, i); + } + break; + } + + default: { + assert((rule_act & RULE_ACT_KIND_MASK) == RULE_ACT_LIST); + + // n=2 is: item item* + // n=1 is: item (sep item)* + // n=3 is: item (sep item)* [sep] + bool had_trailing_sep; + if (backtrack) { + list_backtrack: + had_trailing_sep = false; + if (n == 2) { + if (i == 1) { + // fail on item, first time round; propagate backtrack + goto next_rule; + } else { + // fail on item, in later rounds; finish with this rule + backtrack = false; + } + } else { + if (i == 1) { + // fail on item, first time round; propagate backtrack + goto next_rule; + } else if ((i & 1) == 1) { + // fail on item, in later rounds; have eaten tokens so can't backtrack + if (n == 3) { + // list allows trailing separator; finish parsing list + had_trailing_sep = true; + backtrack = false; + } else { + // list doesn't allowing trailing separator; fail + goto syntax_error; + } + } else { + // fail on separator; finish parsing list + backtrack = false; + } + } + } else { + for (;;) { + size_t arg = rule_arg[i & 1 & n]; + if ((arg & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { + if (lex->tok_kind == (arg & RULE_ARG_ARG_MASK)) { + if (i & 1 & n) { + // separators which are tokens are not pushed to result stack + } else { + push_result_token(&parser, rule_id); + } + mp_lexer_to_next(lex); + // got element of list, so continue parsing list + i += 1; + } else { + // couldn't get element of list + i += 1; + backtrack = true; + goto list_backtrack; + } + } else { + assert((arg & RULE_ARG_KIND_MASK) == RULE_ARG_RULE); + push_rule(&parser, rule_src_line, rule_id, i + 1); // save this list-rule + push_rule_from_arg(&parser, arg); // push child of list-rule + goto next_rule; + } + } + } + assert(i >= 1); + + // compute number of elements in list, result in i + i -= 1; + if ((n & 1) && (rule_arg[1] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { + // don't count separators when they are tokens + i = (i + 1) / 2; + } + + if (i == 1) { + // list matched single item + if (had_trailing_sep) { + // if there was a trailing separator, make a list of a single item + push_result_rule(&parser, rule_src_line, rule_id, i); + } else { + // just leave single item on stack (ie don't wrap in a list) + } + } else { + push_result_rule(&parser, rule_src_line, rule_id, i); + } + break; + } + } + } + + #if MICROPY_COMP_CONST + mp_map_deinit(&parser.consts); + #endif + + // truncate final chunk and link into chain of chunks + if (parser.cur_chunk != NULL) { + (void)m_renew_maybe(byte, parser.cur_chunk, + sizeof(mp_parse_chunk_t) + parser.cur_chunk->alloc, + sizeof(mp_parse_chunk_t) + parser.cur_chunk->union_.used, + false); + parser.cur_chunk->alloc = parser.cur_chunk->union_.used; + parser.cur_chunk->union_.next = parser.tree.chunk; + parser.tree.chunk = parser.cur_chunk; + } + + if ( + lex->tok_kind != MP_TOKEN_END // check we are at the end of the token stream + || parser.result_stack_top == 0 // check that we got a node (can fail on empty input) + ) { + syntax_error:; + mp_obj_t exc; + if (lex->tok_kind == MP_TOKEN_INDENT) { + exc = mp_obj_new_exception_msg(&mp_type_IndentationError, + MP_ERROR_TEXT("unexpected indent")); + } else if (lex->tok_kind == MP_TOKEN_DEDENT_MISMATCH) { + exc = mp_obj_new_exception_msg(&mp_type_IndentationError, + MP_ERROR_TEXT("unindent doesn't match any outer indent level")); + #if MICROPY_PY_FSTRINGS + } else if (lex->tok_kind == MP_TOKEN_MALFORMED_FSTRING) { + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + MP_ERROR_TEXT("malformed f-string")); + } else if (lex->tok_kind == MP_TOKEN_FSTRING_RAW) { + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + MP_ERROR_TEXT("raw f-strings are not supported")); + #endif + } else { + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + MP_ERROR_TEXT("invalid syntax")); + } + // add traceback to give info about file name and location + // we don't have a 'block' name, so just pass the NULL qstr to indicate this + mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTRnull); + nlr_raise(exc); + } + + // get the root parse node that we created + assert(parser.result_stack_top == 1); + parser.tree.root = parser.result_stack[0]; + + // free the memory that we don't need anymore + m_del(rule_stack_t, parser.rule_stack, parser.rule_stack_alloc); + m_del(mp_parse_node_t, parser.result_stack, parser.result_stack_alloc); + + // Deregister exception handler and free the lexer. + nlr_pop_jump_callback(true); + + return parser.tree; +} + +void mp_parse_tree_clear(mp_parse_tree_t *tree) { + mp_parse_chunk_t *chunk = tree->chunk; + while (chunk != NULL) { + mp_parse_chunk_t *next = chunk->union_.next; + m_del(byte, chunk, sizeof(mp_parse_chunk_t) + chunk->alloc); + chunk = next; + } + tree->chunk = NULL; // Avoid dangling pointer that may live on stack +} + +#endif // MICROPY_ENABLE_COMPILER diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/parse.h b/non_catalog_apps/mp_flipper/lib/micropython/py/parse.h new file mode 100644 index 00000000..5531e35c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/parse.h @@ -0,0 +1,116 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_PARSE_H +#define MICROPY_INCLUDED_PY_PARSE_H + +#include +#include + +#include "py/obj.h" + +struct _mp_lexer_t; + +// a mp_parse_node_t is: +// - 0000...0000: no node +// - xxxx...xxx1: a small integer; bits 1 and above are the signed value, 2's complement +// - xxxx...xx00: pointer to mp_parse_node_struct_t +// - xx...xx0010: an identifier; bits 4 and above are the qstr +// - xx...xx0110: a string; bits 4 and above are the qstr holding the value +// - xx...xx1010: a token; bits 4 and above are mp_token_kind_t + +#define MP_PARSE_NODE_NULL (0) +#define MP_PARSE_NODE_SMALL_INT (0x1) +#define MP_PARSE_NODE_ID (0x02) +#define MP_PARSE_NODE_STRING (0x06) +#define MP_PARSE_NODE_TOKEN (0x0a) + +typedef uintptr_t mp_parse_node_t; // must be pointer size + +typedef struct _mp_parse_node_struct_t { + uint32_t source_line; // line number in source file + uint32_t kind_num_nodes; // parse node kind, and number of nodes + mp_parse_node_t nodes[]; // nodes +} mp_parse_node_struct_t; + +// macros for mp_parse_node_t usage +// some of these evaluate their argument more than once + +#define MP_PARSE_NODE_IS_NULL(pn) ((pn) == MP_PARSE_NODE_NULL) +#define MP_PARSE_NODE_IS_LEAF(pn) ((pn) & 3) +#define MP_PARSE_NODE_IS_STRUCT(pn) ((pn) != MP_PARSE_NODE_NULL && ((pn) & 3) == 0) +#define MP_PARSE_NODE_IS_STRUCT_KIND(pn, k) ((pn) != MP_PARSE_NODE_NULL && ((pn) & 3) == 0 && MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t *)(pn)) == (k)) + +#define MP_PARSE_NODE_IS_SMALL_INT(pn) (((pn) & 0x1) == MP_PARSE_NODE_SMALL_INT) +#define MP_PARSE_NODE_IS_ID(pn) (((pn) & 0x0f) == MP_PARSE_NODE_ID) +#define MP_PARSE_NODE_IS_TOKEN(pn) (((pn) & 0x0f) == MP_PARSE_NODE_TOKEN) +#define MP_PARSE_NODE_IS_TOKEN_KIND(pn, k) ((pn) == (MP_PARSE_NODE_TOKEN | ((k) << 4))) + +#define MP_PARSE_NODE_LEAF_KIND(pn) ((pn) & 0x0f) +#define MP_PARSE_NODE_LEAF_ARG(pn) (((uintptr_t)(pn)) >> 4) +#define MP_PARSE_NODE_LEAF_SMALL_INT(pn) (((mp_int_t)(intptr_t)(pn)) >> 1) +#define MP_PARSE_NODE_STRUCT_KIND(pns) ((pns)->kind_num_nodes & 0xff) +#define MP_PARSE_NODE_STRUCT_NUM_NODES(pns) ((pns)->kind_num_nodes >> 8) + +static inline mp_parse_node_t mp_parse_node_new_small_int(mp_int_t val) { + return (mp_parse_node_t)(MP_PARSE_NODE_SMALL_INT | ((mp_uint_t)val << 1)); +} + +static inline mp_parse_node_t mp_parse_node_new_leaf(size_t kind, mp_int_t arg) { + return (mp_parse_node_t)(kind | ((mp_uint_t)arg << 4)); +} + +static inline mp_obj_t mp_parse_node_extract_const_object(mp_parse_node_struct_t *pns) { + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + // nodes are 32-bit pointers, but need to extract 64-bit object + return (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32); + #else + return (mp_obj_t)pns->nodes[0]; + #endif +} + +bool mp_parse_node_is_const_false(mp_parse_node_t pn); +bool mp_parse_node_is_const_true(mp_parse_node_t pn); +bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o); +size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes); +void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t indent); + +typedef enum { + MP_PARSE_SINGLE_INPUT, + MP_PARSE_FILE_INPUT, + MP_PARSE_EVAL_INPUT, +} mp_parse_input_kind_t; + +typedef struct _mp_parse_t { + mp_parse_node_t root; + struct _mp_parse_chunk_t *chunk; +} mp_parse_tree_t; + +// the parser will raise an exception if an error occurred +// the parser will free the lexer before it returns +mp_parse_tree_t mp_parse(struct _mp_lexer_t *lex, mp_parse_input_kind_t input_kind); +void mp_parse_tree_clear(mp_parse_tree_t *tree); + +#endif // MICROPY_INCLUDED_PY_PARSE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/parsenum.c b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenum.c new file mode 100644 index 00000000..b33ffb6f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenum.c @@ -0,0 +1,426 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/runtime.h" +#include "py/parsenumbase.h" +#include "py/parsenum.h" +#include "py/smallint.h" + +#if MICROPY_PY_BUILTINS_FLOAT +#include +#endif + +static NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { + // if lex!=NULL then the parser called us and we need to convert the + // exception's type from ValueError to SyntaxError and add traceback info + if (lex != NULL) { + ((mp_obj_base_t *)MP_OBJ_TO_PTR(exc))->type = &mp_type_SyntaxError; + mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTRnull); + } + nlr_raise(exc); +} + +mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, mp_lexer_t *lex) { + const byte *restrict str = (const byte *)str_; + const byte *restrict top = str + len; + bool neg = false; + mp_obj_t ret_val; + + // check radix base + if ((base != 0 && base < 2) || base > 36) { + // this won't be reached if lex!=NULL + mp_raise_ValueError(MP_ERROR_TEXT("int() arg 2 must be >= 2 and <= 36")); + } + + // skip leading space + for (; str < top && unichar_isspace(*str); str++) { + } + + // parse optional sign + if (str < top) { + if (*str == '+') { + str++; + } else if (*str == '-') { + str++; + neg = true; + } + } + + // parse optional base prefix + str += mp_parse_num_base((const char *)str, top - str, &base); + + // string should be an integer number + mp_int_t int_val = 0; + const byte *restrict str_val_start = str; + for (; str < top; str++) { + // get next digit as a value + mp_uint_t dig = *str; + if ('0' <= dig && dig <= '9') { + dig -= '0'; + } else if (dig == '_') { + continue; + } else { + dig |= 0x20; // make digit lower-case + if ('a' <= dig && dig <= 'z') { + dig -= 'a' - 10; + } else { + // unknown character + break; + } + } + if (dig >= (mp_uint_t)base) { + break; + } + + // add next digi and check for overflow + if (mp_small_int_mul_overflow(int_val, base)) { + goto overflow; + } + int_val = int_val * base + dig; + if (!MP_SMALL_INT_FITS(int_val)) { + goto overflow; + } + } + + // negate value if needed + if (neg) { + int_val = -int_val; + } + + // create the small int + ret_val = MP_OBJ_NEW_SMALL_INT(int_val); + +have_ret_val: + // check we parsed something + if (str == str_val_start) { + goto value_error; + } + + // skip trailing space + for (; str < top && unichar_isspace(*str); str++) { + } + + // check we reached the end of the string + if (str != top) { + goto value_error; + } + + // return the object + return ret_val; + +overflow: + // reparse using long int + { + const char *s2 = (const char *)str_val_start; + ret_val = mp_obj_new_int_from_str_len(&s2, top - str_val_start, neg, base); + str = (const byte *)s2; + goto have_ret_val; + } + +value_error: + { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_obj_t exc = mp_obj_new_exception_msg(&mp_type_ValueError, + MP_ERROR_TEXT("invalid syntax for integer")); + raise_exc(exc, lex); + #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL + mp_obj_t exc = mp_obj_new_exception_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("invalid syntax for integer with base %d"), base); + raise_exc(exc, lex); + #else + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 50, &print); + mp_printf(&print, "invalid syntax for integer with base %d: ", base); + mp_str_print_quoted(&print, str_val_start, top - str_val_start, true); + mp_obj_t exc = mp_obj_new_exception_arg1(&mp_type_ValueError, + mp_obj_new_str_from_utf8_vstr(&vstr)); + raise_exc(exc, lex); + #endif + } +} + +enum { + REAL_IMAG_STATE_START = 0, + REAL_IMAG_STATE_HAVE_REAL = 1, + REAL_IMAG_STATE_HAVE_IMAG = 2, +}; + +typedef enum { + PARSE_DEC_IN_INTG, + PARSE_DEC_IN_FRAC, + PARSE_DEC_IN_EXP, +} parse_dec_in_t; + +#if MICROPY_PY_BUILTINS_FLOAT +// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing +// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float +// EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float +// Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n +// so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's +// exponent). +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define DEC_VAL_MAX 1e20F +#define SMALL_NORMAL_VAL (1e-37F) +#define SMALL_NORMAL_EXP (-37) +#define EXACT_POWER_OF_10 (9) +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define DEC_VAL_MAX 1e200 +#define SMALL_NORMAL_VAL (1e-307) +#define SMALL_NORMAL_EXP (-307) +#define EXACT_POWER_OF_10 (22) +#endif + +// Break out inner digit accumulation routine to ease trailing zero deferral. +static void accept_digit(mp_float_t *p_dec_val, int dig, int *p_exp_extra, int in) { + // Core routine to ingest an additional digit. + if (*p_dec_val < DEC_VAL_MAX) { + // dec_val won't overflow so keep accumulating + *p_dec_val = 10 * *p_dec_val + dig; + if (in == PARSE_DEC_IN_FRAC) { + --(*p_exp_extra); + } + } else { + // dec_val might overflow and we anyway can't represent more digits + // of precision, so ignore the digit and just adjust the exponent + if (in == PARSE_DEC_IN_INTG) { + ++(*p_exp_extra); + } + } +} +#endif // MICROPY_PY_BUILTINS_FLOAT + +#if MICROPY_PY_BUILTINS_COMPLEX +mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex) +#else +mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex) +#endif +{ + #if MICROPY_PY_BUILTINS_FLOAT + + const char *top = str + len; + mp_float_t dec_val = 0; + bool dec_neg = false; + + #if MICROPY_PY_BUILTINS_COMPLEX + unsigned int real_imag_state = REAL_IMAG_STATE_START; + mp_float_t dec_real = 0; +parse_start: + #endif + + // skip leading space + for (; str < top && unichar_isspace(*str); str++) { + } + + // parse optional sign + if (str < top) { + if (*str == '+') { + str++; + } else if (*str == '-') { + str++; + dec_neg = true; + } + } + + const char *str_val_start = str; + + // determine what the string is + if (str < top && (str[0] | 0x20) == 'i') { + // string starts with 'i', should be 'inf' or 'infinity' (case insensitive) + if (str + 2 < top && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'f') { + // inf + str += 3; + dec_val = (mp_float_t)INFINITY; + if (str + 4 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'i' && (str[3] | 0x20) == 't' && (str[4] | 0x20) == 'y') { + // infinity + str += 5; + } + } + } else if (str < top && (str[0] | 0x20) == 'n') { + // string starts with 'n', should be 'nan' (case insensitive) + if (str + 2 < top && (str[1] | 0x20) == 'a' && (str[2] | 0x20) == 'n') { + // NaN + str += 3; + dec_val = MICROPY_FLOAT_C_FUN(nan)(""); + } + } else { + // string should be a decimal number + parse_dec_in_t in = PARSE_DEC_IN_INTG; + bool exp_neg = false; + int exp_val = 0; + int exp_extra = 0; + int trailing_zeros_intg = 0, trailing_zeros_frac = 0; + while (str < top) { + unsigned int dig = *str++; + if ('0' <= dig && dig <= '9') { + dig -= '0'; + if (in == PARSE_DEC_IN_EXP) { + // don't overflow exp_val when adding next digit, instead just truncate + // it and the resulting float will still be correct, either inf or 0.0 + // (use INT_MAX/2 to allow adding exp_extra at the end without overflow) + if (exp_val < (INT_MAX / 2 - 9) / 10) { + exp_val = 10 * exp_val + dig; + } + } else { + if (dig == 0 || dec_val >= DEC_VAL_MAX) { + // Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them. + // Also, once we reach DEC_VAL_MAX, treat every additional digit as a trailing zero. + if (in == PARSE_DEC_IN_INTG) { + ++trailing_zeros_intg; + } else { + ++trailing_zeros_frac; + } + } else { + // Time to un-defer any trailing zeros. Intg zeros first. + while (trailing_zeros_intg) { + accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_INTG); + --trailing_zeros_intg; + } + while (trailing_zeros_frac) { + accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_FRAC); + --trailing_zeros_frac; + } + accept_digit(&dec_val, dig, &exp_extra, in); + } + } + } else if (in == PARSE_DEC_IN_INTG && dig == '.') { + in = PARSE_DEC_IN_FRAC; + } else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) { + in = PARSE_DEC_IN_EXP; + if (str < top) { + if (str[0] == '+') { + str++; + } else if (str[0] == '-') { + str++; + exp_neg = true; + } + } + if (str == top) { + goto value_error; + } + } else if (dig == '_') { + continue; + } else { + // unknown character + str--; + break; + } + } + + // work out the exponent + if (exp_neg) { + exp_val = -exp_val; + } + + // apply the exponent, making sure it's not a subnormal value + exp_val += exp_extra + trailing_zeros_intg; + if (exp_val < SMALL_NORMAL_EXP) { + exp_val -= SMALL_NORMAL_EXP; + dec_val *= SMALL_NORMAL_VAL; + } + + // At this point, we need to multiply the mantissa by its base 10 exponent. If possible, + // we would rather manipulate numbers that have an exact representation in IEEE754. It + // turns out small positive powers of 10 do, whereas small negative powers of 10 don't. + // So in that case, we'll yield a division of exact values rather than a multiplication + // of slightly erroneous values. + if (exp_val < 0 && exp_val >= -EXACT_POWER_OF_10) { + dec_val /= MICROPY_FLOAT_C_FUN(pow)(10, -exp_val); + } else { + dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val); + } + } + + if (allow_imag && str < top && (*str | 0x20) == 'j') { + #if MICROPY_PY_BUILTINS_COMPLEX + if (str == str_val_start) { + // Convert "j" to "1j". + dec_val = 1; + } + ++str; + real_imag_state |= REAL_IMAG_STATE_HAVE_IMAG; + #else + raise_exc(mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("complex values not supported")), lex); + #endif + } + + // negate value if needed + if (dec_neg) { + dec_val = -dec_val; + } + + // check we parsed something + if (str == str_val_start) { + goto value_error; + } + + // skip trailing space + for (; str < top && unichar_isspace(*str); str++) { + } + + // check we reached the end of the string + if (str != top) { + #if MICROPY_PY_BUILTINS_COMPLEX + if (force_complex && real_imag_state == REAL_IMAG_STATE_START) { + // If we've only seen a real so far, keep parsing for the imaginary part. + dec_real = dec_val; + dec_val = 0; + real_imag_state |= REAL_IMAG_STATE_HAVE_REAL; + goto parse_start; + } + #endif + goto value_error; + } + + #if MICROPY_PY_BUILTINS_COMPLEX + if (real_imag_state == REAL_IMAG_STATE_HAVE_REAL) { + // We're on the second part, but didn't get the expected imaginary number. + goto value_error; + } + #endif + + // return the object + + #if MICROPY_PY_BUILTINS_COMPLEX + if (real_imag_state != REAL_IMAG_STATE_START) { + return mp_obj_new_complex(dec_real, dec_val); + } else if (force_complex) { + return mp_obj_new_complex(dec_val, 0); + } + #endif + + return mp_obj_new_float(dec_val); + +value_error: + raise_exc(mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("invalid syntax for number")), lex); + + #else + raise_exc(mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("decimal numbers not supported")), lex); + #endif +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/parsenum.h b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenum.h new file mode 100644 index 00000000..f444632d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenum.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_PARSENUM_H +#define MICROPY_INCLUDED_PY_PARSENUM_H + +#include "py/mpconfig.h" +#include "py/lexer.h" +#include "py/obj.h" + +// these functions raise a SyntaxError if lex!=NULL, else a ValueError + +mp_obj_t mp_parse_num_integer(const char *restrict str, size_t len, int base, mp_lexer_t *lex); + +#if MICROPY_PY_BUILTINS_COMPLEX +mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex); + +static inline mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex) { + return mp_parse_num_decimal(str, len, allow_imag, false, lex); +} + +static inline mp_obj_t mp_parse_num_complex(const char *str, size_t len, mp_lexer_t *lex) { + return mp_parse_num_decimal(str, len, true, true, lex); +} +#else +mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex); +#endif + +#endif // MICROPY_INCLUDED_PY_PARSENUM_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/parsenumbase.c b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenumbase.c new file mode 100644 index 00000000..94523a66 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenumbase.c @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpconfig.h" +#include "py/misc.h" +#include "py/parsenumbase.h" + +// find real radix base, and strip preceding '0x', '0o' and '0b' +// puts base in *base, and returns number of bytes to skip the prefix +size_t mp_parse_num_base(const char *str, size_t len, int *base) { + const byte *p = (const byte *)str; + if (len <= 1) { + goto no_prefix; + } + unichar c = *(p++); + if ((*base == 0 || *base == 16) && c == '0') { + c = *(p++); + if ((c | 32) == 'x') { + *base = 16; + } else if (*base == 0 && (c | 32) == 'o') { + *base = 8; + } else if (*base == 0 && (c | 32) == 'b') { + *base = 2; + } else { + if (*base == 0) { + *base = 10; + } + p -= 2; + } + } else if (*base == 8 && c == '0') { + c = *(p++); + if ((c | 32) != 'o') { + p -= 2; + } + } else if (*base == 2 && c == '0') { + c = *(p++); + if ((c | 32) != 'b') { + p -= 2; + } + } else { + p--; + no_prefix: + if (*base == 0) { + *base = 10; + } + } + return p - (const byte *)str; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/parsenumbase.h b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenumbase.h new file mode 100644 index 00000000..3a525f99 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/parsenumbase.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_PARSENUMBASE_H +#define MICROPY_INCLUDED_PY_PARSENUMBASE_H + +#include "py/mpconfig.h" + +size_t mp_parse_num_base(const char *str, size_t len, int *base); + +#endif // MICROPY_INCLUDED_PY_PARSENUMBASE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/persistentcode.c b/non_catalog_apps/mp_flipper/lib/micropython/py/persistentcode.c new file mode 100644 index 00000000..5088c05f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/persistentcode.c @@ -0,0 +1,669 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Damien P. George + * + * 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 "py/reader.h" +#include "py/nativeglue.h" +#include "py/persistentcode.h" +#include "py/bc0.h" +#include "py/objstr.h" +#include "py/mpthread.h" + +#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE + +#include "py/smallint.h" + +// makeqstrdata.py has a fixed list of qstrs at the start that we can assume +// are available with know indices on all MicroPython implementations, and +// avoid needing to duplicate the string data in the .mpy file. This is the +// last one in that list (anything with a qstr less than or equal to this is +// assumed to be in the list). +#define QSTR_LAST_STATIC MP_QSTR_zip + +#if MICROPY_DYNAMIC_COMPILER +#define MPY_FEATURE_ARCH_DYNAMIC mp_dynamic_compiler.native_arch +#else +#define MPY_FEATURE_ARCH_DYNAMIC MPY_FEATURE_ARCH +#endif + +typedef struct _bytecode_prelude_t { + uint n_state; + uint n_exc_stack; + uint scope_flags; + uint n_pos_args; + uint n_kwonly_args; + uint n_def_pos_args; + uint code_info_size; +} bytecode_prelude_t; + +#endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE + +#if MICROPY_PERSISTENT_CODE_LOAD + +#include "py/parsenum.h" + +static int read_byte(mp_reader_t *reader); +static size_t read_uint(mp_reader_t *reader); + +#if MICROPY_EMIT_MACHINE_CODE + +typedef struct _reloc_info_t { + mp_reader_t *reader; + mp_module_context_t *context; + uint8_t *rodata; + uint8_t *bss; +} reloc_info_t; + +void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) { + // Relocate native code + reloc_info_t *ri = ri_in; + uint8_t op; + uintptr_t *addr_to_adjust = NULL; + while ((op = read_byte(ri->reader)) != 0xff) { + if (op & 1) { + // Point to new location to make adjustments + size_t addr = read_uint(ri->reader); + if ((addr & 1) == 0) { + // Point to somewhere in text + addr_to_adjust = &((uintptr_t *)text)[addr >> 1]; + } else { + // Point to somewhere in rodata + addr_to_adjust = &((uintptr_t *)ri->rodata)[addr >> 1]; + } + } + op >>= 1; + uintptr_t dest; + size_t n = 1; + if (op <= 5) { + if (op & 1) { + // Read in number of adjustments to make + n = read_uint(ri->reader); + } + op >>= 1; + if (op == 0) { + // Destination is text + dest = reloc_text; + } else if (op == 1) { + // Destination is rodata + dest = (uintptr_t)ri->rodata; + } else { + // Destination is bss + dest = (uintptr_t)ri->bss; + } + } else if (op == 6) { + // Destination is qstr_table + dest = (uintptr_t)ri->context->constants.qstr_table; + } else if (op == 7) { + // Destination is obj_table + dest = (uintptr_t)ri->context->constants.obj_table; + } else if (op == 8) { + // Destination is mp_fun_table itself + dest = (uintptr_t)&mp_fun_table; + } else { + // Destination is an entry in mp_fun_table + dest = ((uintptr_t *)&mp_fun_table)[op - 9]; + } + while (n--) { + *addr_to_adjust++ += dest; + } + } +} + +#endif + +static int read_byte(mp_reader_t *reader) { + return reader->readbyte(reader->data); +} + +static void read_bytes(mp_reader_t *reader, byte *buf, size_t len) { + while (len-- > 0) { + *buf++ = reader->readbyte(reader->data); + } +} + +static size_t read_uint(mp_reader_t *reader) { + size_t unum = 0; + for (;;) { + byte b = reader->readbyte(reader->data); + unum = (unum << 7) | (b & 0x7f); + if ((b & 0x80) == 0) { + break; + } + } + return unum; +} + +static qstr load_qstr(mp_reader_t *reader) { + size_t len = read_uint(reader); + if (len & 1) { + // static qstr + return len >> 1; + } + len >>= 1; + char *str = m_new(char, len); + read_bytes(reader, (byte *)str, len); + read_byte(reader); // read and discard null terminator + qstr qst = qstr_from_strn(str, len); + m_del(char, str, len); + return qst; +} + +static mp_obj_t load_obj(mp_reader_t *reader) { + byte obj_type = read_byte(reader); + #if MICROPY_EMIT_MACHINE_CODE + if (obj_type == MP_PERSISTENT_OBJ_FUN_TABLE) { + return MP_OBJ_FROM_PTR(&mp_fun_table); + } else + #endif + if (obj_type == MP_PERSISTENT_OBJ_NONE) { + return mp_const_none; + } else if (obj_type == MP_PERSISTENT_OBJ_FALSE) { + return mp_const_false; + } else if (obj_type == MP_PERSISTENT_OBJ_TRUE) { + return mp_const_true; + } else if (obj_type == MP_PERSISTENT_OBJ_ELLIPSIS) { + return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); + } else { + size_t len = read_uint(reader); + if (len == 0 && obj_type == MP_PERSISTENT_OBJ_BYTES) { + read_byte(reader); // skip null terminator + return mp_const_empty_bytes; + } else if (obj_type == MP_PERSISTENT_OBJ_TUPLE) { + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(len, NULL)); + for (size_t i = 0; i < len; ++i) { + tuple->items[i] = load_obj(reader); + } + return MP_OBJ_FROM_PTR(tuple); + } + vstr_t vstr; + vstr_init_len(&vstr, len); + read_bytes(reader, (byte *)vstr.buf, len); + if (obj_type == MP_PERSISTENT_OBJ_STR || obj_type == MP_PERSISTENT_OBJ_BYTES) { + read_byte(reader); // skip null terminator + if (obj_type == MP_PERSISTENT_OBJ_STR) { + return mp_obj_new_str_from_utf8_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } + } else if (obj_type == MP_PERSISTENT_OBJ_INT) { + return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); + } else { + assert(obj_type == MP_PERSISTENT_OBJ_FLOAT || obj_type == MP_PERSISTENT_OBJ_COMPLEX); + return mp_parse_num_float(vstr.buf, vstr.len, obj_type == MP_PERSISTENT_OBJ_COMPLEX, NULL); + } + } +} + +static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *context) { + // Load function kind and data length + size_t kind_len = read_uint(reader); + int kind = (kind_len & 3) + MP_CODE_BYTECODE; + bool has_children = !!(kind_len & 4); + size_t fun_data_len = kind_len >> 3; + + #if !MICROPY_EMIT_MACHINE_CODE + if (kind != MP_CODE_BYTECODE) { + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); + } + #endif + + uint8_t *fun_data = NULL; + #if MICROPY_EMIT_MACHINE_CODE + size_t prelude_offset = 0; + mp_uint_t native_scope_flags = 0; + mp_uint_t native_n_pos_args = 0; + mp_uint_t native_type_sig = 0; + #endif + + if (kind == MP_CODE_BYTECODE) { + // Allocate memory for the bytecode + fun_data = m_new(uint8_t, fun_data_len); + // Load bytecode + read_bytes(reader, fun_data, fun_data_len); + + #if MICROPY_EMIT_MACHINE_CODE + } else { + // Allocate memory for native data and load it + size_t fun_alloc; + MP_PLAT_ALLOC_EXEC(fun_data_len, (void **)&fun_data, &fun_alloc); + read_bytes(reader, fun_data, fun_data_len); + + if (kind == MP_CODE_NATIVE_PY) { + // Read prelude offset within fun_data, and extract scope flags. + prelude_offset = read_uint(reader); + const byte *ip = fun_data + prelude_offset; + MP_BC_PRELUDE_SIG_DECODE(ip); + native_scope_flags = scope_flags; + } else { + // Load basic scope info for viper and asm. + native_scope_flags = read_uint(reader); + if (kind == MP_CODE_NATIVE_ASM) { + native_n_pos_args = read_uint(reader); + native_type_sig = read_uint(reader); + } + } + #endif + } + + size_t n_children = 0; + mp_raw_code_t **children = NULL; + + #if MICROPY_EMIT_MACHINE_CODE + // Load optional BSS/rodata for viper. + uint8_t *rodata = NULL; + uint8_t *bss = NULL; + if (kind == MP_CODE_NATIVE_VIPER) { + size_t rodata_size = 0; + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { + rodata_size = read_uint(reader); + } + + size_t bss_size = 0; + if (native_scope_flags & MP_SCOPE_FLAG_VIPERBSS) { + bss_size = read_uint(reader); + } + + if (rodata_size + bss_size != 0) { + bss_size = (uintptr_t)MP_ALIGN(bss_size, sizeof(uintptr_t)); + uint8_t *data = m_new0(uint8_t, bss_size + rodata_size); + bss = data; + rodata = bss + bss_size; + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { + read_bytes(reader, rodata, rodata_size); + } + + // Viper code with BSS/rodata should not have any children. + // Reuse the children pointer to reference the BSS/rodata + // memory so that it is not reclaimed by the GC. + assert(!has_children); + children = (void *)data; + } + } + #endif + + // Load children if any. + if (has_children) { + n_children = read_uint(reader); + children = m_new(mp_raw_code_t *, n_children + (kind == MP_CODE_NATIVE_PY)); + for (size_t i = 0; i < n_children; ++i) { + children[i] = load_raw_code(reader, context); + } + } + + // Create raw_code and return it + mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); + if (kind == MP_CODE_BYTECODE) { + const byte *ip = fun_data; + MP_BC_PRELUDE_SIG_DECODE(ip); + // Assign bytecode to raw code object + mp_emit_glue_assign_bytecode(rc, fun_data, + children, + #if MICROPY_PERSISTENT_CODE_SAVE + fun_data_len, + n_children, + #endif + scope_flags); + + #if MICROPY_EMIT_MACHINE_CODE + } else { + const uint8_t *prelude_ptr; + #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE + if (kind == MP_CODE_NATIVE_PY) { + // Executable code cannot be accessed byte-wise on this architecture, so copy + // the prelude to a separate memory region that is byte-wise readable. + void *buf = fun_data + prelude_offset; + size_t n = fun_data_len - prelude_offset; + prelude_ptr = memcpy(m_new(uint8_t, n), buf, n); + } + #endif + + // Relocate and commit code to executable address space + reloc_info_t ri = {reader, context, rodata, bss}; + #if defined(MP_PLAT_COMMIT_EXEC) + void *opt_ri = (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; + fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); + #else + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { + #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE + // If native code needs relocations then it's not guaranteed that a pointer to + // the head of `buf` (containing the machine code) will be retained for the GC + // to trace. This is because native functions can start inside `buf` and so + // it's possible that the only GC-reachable pointers are pointers inside `buf`. + // So put this `buf` on a list of reachable root pointers. + if (MP_STATE_PORT(track_reloc_code_list) == MP_OBJ_NULL) { + MP_STATE_PORT(track_reloc_code_list) = mp_obj_new_list(0, NULL); + } + mp_obj_list_append(MP_STATE_PORT(track_reloc_code_list), MP_OBJ_FROM_PTR(fun_data)); + #endif + // Do the relocations. + mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data); + } + #endif + + if (kind == MP_CODE_NATIVE_PY) { + #if !MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE + prelude_ptr = fun_data + prelude_offset; + #endif + if (n_children == 0) { + children = (void *)prelude_ptr; + } else { + children[n_children] = (void *)prelude_ptr; + } + } + + // Assign native code to raw code object + mp_emit_glue_assign_native(rc, kind, + fun_data, fun_data_len, + children, + #if MICROPY_PERSISTENT_CODE_SAVE + n_children, + prelude_offset, + #endif + native_scope_flags, native_n_pos_args, native_type_sig + ); + #endif + } + return rc; +} + +void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { + // Set exception handler to close the reader if an exception is raised. + MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, reader->close, reader->data); + nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback); + + byte header[4]; + read_bytes(reader, header, sizeof(header)); + byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); + if (header[0] != 'M' + || header[1] != MPY_VERSION + || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION) + || header[3] > MP_SMALL_INT_BITS) { + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); + } + if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { + if (!MPY_FEATURE_ARCH_TEST(arch)) { + if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) { + // On supported ports this can be resolved by enabling feature, eg + // mpconfigboard.h: MICROPY_EMIT_THUMB (1) + mp_raise_ValueError(MP_ERROR_TEXT("native code in .mpy unsupported")); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch")); + } + } + } + + size_t n_qstr = read_uint(reader); + size_t n_obj = read_uint(reader); + mp_module_context_alloc_tables(cm->context, n_qstr, n_obj); + + // Load qstrs. + for (size_t i = 0; i < n_qstr; ++i) { + cm->context->constants.qstr_table[i] = load_qstr(reader); + } + + // Load constant objects. + for (size_t i = 0; i < n_obj; ++i) { + cm->context->constants.obj_table[i] = load_obj(reader); + } + + // Load top-level module. + cm->rc = load_raw_code(reader, cm->context); + + #if MICROPY_PERSISTENT_CODE_SAVE + cm->has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE; + cm->n_qstr = n_qstr; + cm->n_obj = n_obj; + #endif + + // Deregister exception handler and close the reader. + nlr_pop_jump_callback(true); +} + +void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *context) { + mp_reader_t reader; + mp_reader_new_mem(&reader, buf, len, 0); + mp_raw_code_load(&reader, context); +} + +#if MICROPY_HAS_FILE_READER + +void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *context) { + mp_reader_t reader; + mp_reader_new_file(&reader, filename); + mp_raw_code_load(&reader, context); +} + +#endif // MICROPY_HAS_FILE_READER + +#endif // MICROPY_PERSISTENT_CODE_LOAD + +#if MICROPY_PERSISTENT_CODE_SAVE + +#include "py/objstr.h" + +static void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) { + print->print_strn(print->data, (const char *)data, len); +} + +#define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7) +static void mp_print_uint(mp_print_t *print, size_t n) { + byte buf[BYTES_FOR_INT]; + byte *p = buf + sizeof(buf); + *--p = n & 0x7f; + n >>= 7; + for (; n != 0; n >>= 7) { + *--p = 0x80 | (n & 0x7f); + } + print->print_strn(print->data, (char *)p, buf + sizeof(buf) - p); +} + +static void save_qstr(mp_print_t *print, qstr qst) { + if (qst <= QSTR_LAST_STATIC) { + // encode static qstr + mp_print_uint(print, qst << 1 | 1); + return; + } + size_t len; + const byte *str = qstr_data(qst, &len); + mp_print_uint(print, len << 1); + mp_print_bytes(print, str, len + 1); // +1 to store null terminator +} + +static void save_obj(mp_print_t *print, mp_obj_t o) { + #if MICROPY_EMIT_MACHINE_CODE + if (o == MP_OBJ_FROM_PTR(&mp_fun_table)) { + byte obj_type = MP_PERSISTENT_OBJ_FUN_TABLE; + mp_print_bytes(print, &obj_type, 1); + } else + #endif + if (mp_obj_is_str_or_bytes(o)) { + byte obj_type; + if (mp_obj_is_str(o)) { + obj_type = MP_PERSISTENT_OBJ_STR; + } else { + obj_type = MP_PERSISTENT_OBJ_BYTES; + } + size_t len; + const char *str = mp_obj_str_get_data(o, &len); + mp_print_bytes(print, &obj_type, 1); + mp_print_uint(print, len); + mp_print_bytes(print, (const byte *)str, len + 1); // +1 to store null terminator + } else if (o == mp_const_none) { + byte obj_type = MP_PERSISTENT_OBJ_NONE; + mp_print_bytes(print, &obj_type, 1); + } else if (o == mp_const_false) { + byte obj_type = MP_PERSISTENT_OBJ_FALSE; + mp_print_bytes(print, &obj_type, 1); + } else if (o == mp_const_true) { + byte obj_type = MP_PERSISTENT_OBJ_TRUE; + mp_print_bytes(print, &obj_type, 1); + } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) { + byte obj_type = MP_PERSISTENT_OBJ_ELLIPSIS; + mp_print_bytes(print, &obj_type, 1); + } else if (mp_obj_is_type(o, &mp_type_tuple)) { + size_t len; + mp_obj_t *items; + mp_obj_tuple_get(o, &len, &items); + byte obj_type = MP_PERSISTENT_OBJ_TUPLE; + mp_print_bytes(print, &obj_type, 1); + mp_print_uint(print, len); + for (size_t i = 0; i < len; ++i) { + save_obj(print, items[i]); + } + } else { + // we save numbers using a simplistic text representation + // TODO could be improved + byte obj_type; + if (mp_obj_is_int(o)) { + obj_type = MP_PERSISTENT_OBJ_INT; + #if MICROPY_PY_BUILTINS_COMPLEX + } else if (mp_obj_is_type(o, &mp_type_complex)) { + obj_type = MP_PERSISTENT_OBJ_COMPLEX; + #endif + } else { + assert(mp_obj_is_float(o)); + obj_type = MP_PERSISTENT_OBJ_FLOAT; + } + vstr_t vstr; + mp_print_t pr; + vstr_init_print(&vstr, 10, &pr); + mp_obj_print_helper(&pr, o, PRINT_REPR); + mp_print_bytes(print, &obj_type, 1); + mp_print_uint(print, vstr.len); + mp_print_bytes(print, (const byte *)vstr.buf, vstr.len); + vstr_clear(&vstr); + } +} + +static void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) { + // Save function kind and data length + mp_print_uint(print, (rc->fun_data_len << 3) | ((rc->n_children != 0) << 2) | (rc->kind - MP_CODE_BYTECODE)); + + // Save function code. + mp_print_bytes(print, rc->fun_data, rc->fun_data_len); + + #if MICROPY_EMIT_MACHINE_CODE + if (rc->kind == MP_CODE_NATIVE_PY) { + // Save prelude size + mp_print_uint(print, rc->prelude_offset); + } else if (rc->kind == MP_CODE_NATIVE_VIPER || rc->kind == MP_CODE_NATIVE_ASM) { + // Save basic scope info for viper and asm + // Viper/asm functions don't support generator, variable args, or default keyword args + // so (scope_flags & MP_SCOPE_FLAG_ALL_SIG) for these functions is always 0. + mp_print_uint(print, 0); + #if MICROPY_EMIT_INLINE_ASM + if (rc->kind == MP_CODE_NATIVE_ASM) { + mp_print_uint(print, rc->asm_n_pos_args); + mp_print_uint(print, rc->asm_type_sig); + } + #endif + } + #endif + + if (rc->n_children) { + mp_print_uint(print, rc->n_children); + for (size_t i = 0; i < rc->n_children; ++i) { + save_raw_code(print, rc->children[i]); + } + } +} + +void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { + // header contains: + // byte 'M' + // byte version + // byte native arch (and sub-version if native) + // byte number of bits in a small int + byte header[4] = { + 'M', + MPY_VERSION, + cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0, + #if MICROPY_DYNAMIC_COMPILER + mp_dynamic_compiler.small_int_bits, + #else + MP_SMALL_INT_BITS, + #endif + }; + mp_print_bytes(print, header, sizeof(header)); + + // Number of entries in constant table. + mp_print_uint(print, cm->n_qstr); + mp_print_uint(print, cm->n_obj); + + // Save qstrs. + for (size_t i = 0; i < cm->n_qstr; ++i) { + save_qstr(print, cm->context->constants.qstr_table[i]); + } + + // Save constant objects. + for (size_t i = 0; i < cm->n_obj; ++i) { + save_obj(print, (mp_obj_t)cm->context->constants.obj_table[i]); + } + + // Save outer raw code, which will save all its child raw codes. + save_raw_code(print, cm->rc); +} + +#if MICROPY_PERSISTENT_CODE_SAVE_FILE + +#include +#include +#include + +static void fd_print_strn(void *env, const char *str, size_t len) { + int fd = (intptr_t)env; + MP_THREAD_GIL_EXIT(); + ssize_t ret = write(fd, str, len); + MP_THREAD_GIL_ENTER(); + (void)ret; +} + +void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename) { + MP_THREAD_GIL_EXIT(); + int fd = open(qstr_str(filename), O_WRONLY | O_CREAT | O_TRUNC, 0644); + MP_THREAD_GIL_ENTER(); + if (fd < 0) { + mp_raise_OSError_with_filename(errno, qstr_str(filename)); + } + mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn}; + mp_raw_code_save(cm, &fd_print); + MP_THREAD_GIL_EXIT(); + close(fd); + MP_THREAD_GIL_ENTER(); +} + +#endif // MICROPY_PERSISTENT_CODE_SAVE_FILE + +#endif // MICROPY_PERSISTENT_CODE_SAVE + +#if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE +// An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them. +MP_REGISTER_ROOT_POINTER(mp_obj_t track_reloc_code_list); +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/persistentcode.h b/non_catalog_apps/mp_flipper/lib/micropython/py/persistentcode.h new file mode 100644 index 00000000..d2b310f2 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/persistentcode.h @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_PERSISTENTCODE_H +#define MICROPY_INCLUDED_PY_PERSISTENTCODE_H + +#include "py/mpprint.h" +#include "py/reader.h" +#include "py/emitglue.h" + +// The current version of .mpy files. A bytecode-only .mpy file can be loaded +// as long as MPY_VERSION matches, but a native .mpy (i.e. one with an arch +// set) must also match MPY_SUB_VERSION. This allows 3 additional updates to +// the native ABI per bytecode revision. +#define MPY_VERSION 6 +#define MPY_SUB_VERSION 3 + +// Macros to encode/decode sub-version to/from the feature byte. This replaces +// the bits previously used to encode the flags (map caching and unicode) +// which are no longer used starting at .mpy version 6. +#define MPY_FEATURE_ENCODE_SUB_VERSION(version) (version) +#define MPY_FEATURE_DECODE_SUB_VERSION(feat) ((feat) & 3) + +// Macros to encode/decode native architecture to/from the feature byte +#define MPY_FEATURE_ENCODE_ARCH(arch) ((arch) << 2) +#define MPY_FEATURE_DECODE_ARCH(feat) ((feat) >> 2) + +// Define the host architecture +#if MICROPY_EMIT_X86 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) +#elif MICROPY_EMIT_X64 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) +#elif MICROPY_EMIT_THUMB + #if defined(__thumb2__) + #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMDP) + #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMSP) + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EM) + #endif + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6M) + #endif + #define MPY_FEATURE_ARCH_TEST(x) (MP_NATIVE_ARCH_ARMV6M <= (x) && (x) <= MPY_FEATURE_ARCH) +#elif MICROPY_EMIT_ARM + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6) +#elif MICROPY_EMIT_XTENSA + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) +#elif MICROPY_EMIT_XTENSAWIN + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSAWIN) +#else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_NONE) +#endif + +#ifndef MPY_FEATURE_ARCH_TEST +#define MPY_FEATURE_ARCH_TEST(x) ((x) == MPY_FEATURE_ARCH) +#endif + +// 16-bit little-endian integer with the second and third bytes of supported .mpy files +#define MPY_FILE_HEADER_INT (MPY_VERSION \ + | (MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8) + +enum { + MP_NATIVE_ARCH_NONE = 0, + MP_NATIVE_ARCH_X86, + MP_NATIVE_ARCH_X64, + MP_NATIVE_ARCH_ARMV6, + MP_NATIVE_ARCH_ARMV6M, + MP_NATIVE_ARCH_ARMV7M, + MP_NATIVE_ARCH_ARMV7EM, + MP_NATIVE_ARCH_ARMV7EMSP, + MP_NATIVE_ARCH_ARMV7EMDP, + MP_NATIVE_ARCH_XTENSA, + MP_NATIVE_ARCH_XTENSAWIN, +}; + +enum { + MP_PERSISTENT_OBJ_FUN_TABLE = 0, + MP_PERSISTENT_OBJ_NONE, + MP_PERSISTENT_OBJ_FALSE, + MP_PERSISTENT_OBJ_TRUE, + MP_PERSISTENT_OBJ_ELLIPSIS, + MP_PERSISTENT_OBJ_STR, + MP_PERSISTENT_OBJ_BYTES, + MP_PERSISTENT_OBJ_INT, + MP_PERSISTENT_OBJ_FLOAT, + MP_PERSISTENT_OBJ_COMPLEX, + MP_PERSISTENT_OBJ_TUPLE, +}; + +void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *ctx); +void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *ctx); +void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *ctx); + +void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print); +void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename); + +void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text); + +#endif // MICROPY_INCLUDED_PY_PERSISTENTCODE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/profile.c b/non_catalog_apps/mp_flipper/lib/micropython/py/profile.c new file mode 100644 index 00000000..92f414ac --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/profile.c @@ -0,0 +1,977 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) SatoshiLabs + * + * 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 "py/profile.h" +#include "py/bc0.h" +#include "py/gc.h" +#include "py/objfun.h" + +#if MICROPY_PY_SYS_SETTRACE + +#if !MICROPY_PERSISTENT_CODE_SAVE +// The settrace feature requires that we maintain additional metadata on the raw +// code object which is normally only done when writing .mpy files. +#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_PERSISTENT_CODE_SAVE to be enabled" +#endif + +#define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) +#define QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) + +static uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) { + const mp_bytecode_prelude_t *prelude = &rc->prelude; + return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc); +} + +void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude) { + const byte *ip = bytecode; + + MP_BC_PRELUDE_SIG_DECODE(ip); + prelude->n_state = n_state; + prelude->n_exc_stack = n_exc_stack; + prelude->scope_flags = scope_flags; + prelude->n_pos_args = n_pos_args; + prelude->n_kwonly_args = n_kwonly_args; + prelude->n_def_pos_args = n_def_pos_args; + + MP_BC_PRELUDE_SIZE_DECODE(ip); + + prelude->line_info_top = ip + n_info; + prelude->opcodes = ip + n_info + n_cell; + + prelude->qstr_block_name_idx = mp_decode_uint_value(ip); + for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) { + ip = mp_decode_uint_skip(ip); + } + prelude->line_info = ip; +} + +/******************************************************************************/ +// code object + +static void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_printf(print, + "", + QSTR_MAP(o->context, prelude->qstr_block_name_idx), + o, + QSTR_MAP(o->context, 0), + rc->line_of_definition + ); +} + +static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) { + mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL)); + + size_t const_no = 0; + for (size_t i = 0; i < rc->n_children; ++i) { + mp_obj_t code = mp_obj_new_code(context, rc->children[i]); + if (code == MP_OBJ_NULL) { + m_malloc_fail(sizeof(mp_obj_code_t)); + } + consts->items[const_no++] = code; + } + consts->items[const_no++] = mp_const_none; + + return consts; +} + +static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { + // const mp_bytecode_prelude_t *prelude = &rc->prelude; + uint start = 0; + uint stop = rc->fun_data_len - start; + + uint last_lineno = mp_prof_bytecode_lineno(rc, start); + uint lasti = 0; + + const uint buffer_chunk_size = (stop - start) >> 2; // heuristic magic + uint buffer_size = buffer_chunk_size; + byte *buffer = m_new(byte, buffer_size); + uint buffer_index = 0; + + for (uint i = start; i < stop; ++i) { + uint lineno = mp_prof_bytecode_lineno(rc, i); + size_t line_diff = lineno - last_lineno; + if (line_diff > 0) { + uint instr_diff = (i - start) - lasti; + + assert(instr_diff < 256); + assert(line_diff < 256); + + if (buffer_index + 2 > buffer_size) { + buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size); + buffer_size = buffer_size + buffer_chunk_size; + } + last_lineno = lineno; + lasti = i - start; + buffer[buffer_index++] = instr_diff; + buffer[buffer_index++] = line_diff; + } + } + + mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index); + m_del(byte, buffer, buffer_size); + return o; +} + +static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + switch (attr) { + case MP_QSTR_co_code: + dest[0] = mp_obj_new_bytes( + (void *)prelude->opcodes, + rc->fun_data_len - (prelude->opcodes - (const byte *)rc->fun_data) + ); + break; + case MP_QSTR_co_consts: + dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc)); + break; + case MP_QSTR_co_filename: + dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, 0)); + break; + case MP_QSTR_co_firstlineno: + dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0)); + break; + case MP_QSTR_co_name: + dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, prelude->qstr_block_name_idx)); + break; + case MP_QSTR_co_names: + dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); + break; + case MP_QSTR_co_lnotab: + if (!o->lnotab) { + o->lnotab = raw_code_lnotab(rc); + } + dest[0] = o->lnotab; + break; + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_settrace_codeobj, + MP_QSTR_code, + MP_TYPE_FLAG_NONE, + print, code_print, + attr, code_attr + ); + +mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc) { + mp_obj_code_t *o = m_new_obj_maybe(mp_obj_code_t); + if (o == NULL) { + return MP_OBJ_NULL; + } + o->base.type = &mp_type_settrace_codeobj; + o->context = context; + o->rc = rc; + o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? + o->lnotab = MP_OBJ_NULL; + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +// frame object + +static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(o_in); + mp_obj_code_t *code = frame->code; + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_printf(print, + "", + frame, + QSTR_MAP(code->context, 0), + frame->lineno, + QSTR_MAP(code->context, prelude->qstr_block_name_idx) + ); +} + +static void frame_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + + mp_obj_frame_t *o = MP_OBJ_TO_PTR(self_in); + + switch (attr) { + case MP_QSTR_f_back: + dest[0] = mp_const_none; + if (o->code_state->prev_state) { + dest[0] = MP_OBJ_FROM_PTR(o->code_state->prev_state->frame); + } + break; + case MP_QSTR_f_code: + dest[0] = MP_OBJ_FROM_PTR(o->code); + break; + case MP_QSTR_f_globals: + dest[0] = MP_OBJ_FROM_PTR(o->code_state->fun_bc->context->module.globals); + break; + case MP_QSTR_f_lasti: + dest[0] = MP_OBJ_NEW_SMALL_INT(o->lasti); + break; + case MP_QSTR_f_lineno: + dest[0] = MP_OBJ_NEW_SMALL_INT(o->lineno); + break; + } +} + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_frame, + MP_QSTR_frame, + MP_TYPE_FLAG_NONE, + print, frame_print, + attr, frame_attr + ); + +mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) { + if (gc_is_locked()) { + return MP_OBJ_NULL; + } + + mp_obj_frame_t *o = m_new_obj_maybe(mp_obj_frame_t); + if (o == NULL) { + return MP_OBJ_NULL; + } + + mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->context, code_state->fun_bc->rc)); + if (code == NULL) { + return MP_OBJ_NULL; + } + + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + o->code_state = code_state; + o->base.type = &mp_type_frame; + o->back = NULL; + o->code = code; + o->lasti = code_state->ip - prelude->opcodes; + o->lineno = mp_prof_bytecode_lineno(rc, o->lasti); + o->trace_opcodes = false; + o->callback = MP_OBJ_NULL; + + return MP_OBJ_FROM_PTR(o); +} + + +/******************************************************************************/ +// Trace logic + +typedef struct { + struct _mp_obj_frame_t *frame; + mp_obj_t event; + mp_obj_t arg; +} prof_callback_args_t; + +static mp_obj_t mp_prof_callback_invoke(mp_obj_t callback, prof_callback_args_t *args) { + assert(mp_obj_is_callable(callback)); + + mp_prof_is_executing = true; + + mp_obj_t a[3] = {MP_OBJ_FROM_PTR(args->frame), args->event, args->arg}; + mp_obj_t top = mp_call_function_n_kw(callback, 3, 0, a); + + mp_prof_is_executing = false; + + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + mp_handle_pending(true); + } + return top; +} + +mp_obj_t mp_prof_settrace(mp_obj_t callback) { + if (mp_obj_is_callable(callback)) { + prof_trace_cb = callback; + } else { + prof_trace_cb = MP_OBJ_NULL; + } + return mp_const_none; +} + +mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state) { + assert(!mp_prof_is_executing); + + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(mp_obj_new_frame(code_state)); + if (frame == NULL) { + // Couldn't allocate a frame object + return MP_OBJ_NULL; + } + + if (code_state->prev_state && code_state->frame == NULL) { + // We are entering not-yet-traced frame + // which means it's a CALL event (not a GENERATOR) + // so set the function definition line. + const mp_raw_code_t *rc = code_state->fun_bc->rc; + frame->lineno = rc->line_of_definition; + if (!rc->line_of_definition) { + frame->lineno = mp_prof_bytecode_lineno(rc, 0); + } + } + code_state->frame = frame; + + if (!prof_trace_cb) { + return MP_OBJ_NULL; + } + + mp_obj_t top; + prof_callback_args_t _args, *args = &_args; + args->frame = code_state->frame; + + // SETTRACE event CALL + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_call); + args->arg = mp_const_none; + top = mp_prof_callback_invoke(prof_trace_cb, args); + + code_state->frame->callback = mp_obj_is_callable(top) ? top : MP_OBJ_NULL; + + // Invalidate the last executed line number so the LINE trace can trigger after this CALL. + frame->lineno = 0; + + return top; +} + +mp_obj_t mp_prof_frame_update(const mp_code_state_t *code_state) { + mp_obj_frame_t *frame = code_state->frame; + if (frame == NULL) { + // Frame was not allocated (eg because there was no memory available) + return MP_OBJ_NULL; + } + + mp_obj_frame_t *o = frame; + mp_obj_code_t *code = o->code; + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + + assert(o->code_state == code_state); + + o->lasti = code_state->ip - prelude->opcodes; + o->lineno = mp_prof_bytecode_lineno(rc, o->lasti); + + return MP_OBJ_FROM_PTR(o); +} + +mp_obj_t mp_prof_instr_tick(mp_code_state_t *code_state, bool is_exception) { + // Detect execution recursion + assert(!mp_prof_is_executing); + assert(code_state->frame); + assert(mp_obj_get_type(code_state->frame) == &mp_type_frame); + + // Detect data recursion + assert(code_state != code_state->prev_state); + + mp_obj_t top = mp_const_none; + mp_obj_t callback = code_state->frame->callback; + + prof_callback_args_t _args, *args = &_args; + args->frame = code_state->frame; + args->event = mp_const_none; + args->arg = mp_const_none; + + // Call event's are handled inside mp_prof_frame_enter + + // SETTRACE event EXCEPTION + if (is_exception) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_exception); + top = mp_prof_callback_invoke(callback, args); + return top; + } + + // SETTRACE event LINE + const mp_raw_code_t *rc = code_state->fun_bc->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + size_t prev_line_no = args->frame->lineno; + size_t current_line_no = mp_prof_bytecode_lineno(rc, code_state->ip - prelude->opcodes); + if (prev_line_no != current_line_no) { + args->frame->lineno = current_line_no; + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_line); + top = mp_prof_callback_invoke(callback, args); + } + + // SETTRACE event RETURN + const byte *ip = code_state->ip; + if (*ip == MP_BC_RETURN_VALUE || *ip == MP_BC_YIELD_VALUE) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_return); + top = mp_prof_callback_invoke(callback, args); + if (code_state->prev_state && *ip == MP_BC_RETURN_VALUE) { + code_state->frame->callback = MP_OBJ_NULL; + } + } + + // SETTRACE event OPCODE + // TODO: frame.f_trace_opcodes=True + if (false) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_opcode); + } + + return top; +} + +/******************************************************************************/ +// DEBUG + +// This section is for debugging the settrace feature itself, and is not intended +// to be included in production/release builds. The code structure for this block +// was taken from py/showbc.c and should not be used as a reference. To enable +// this debug feature enable MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE in py/profile.h. +#if MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE + +#include "runtime0.h" + +#define DECODE_UINT { \ + unum = 0; \ + do { \ + unum = (unum << 7) + (*ip & 0x7f); \ + } while ((*ip++ & 0x80) != 0); \ +} +#define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0) +#define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0) + +#define DECODE_QSTR \ + qst = ip[0] | ip[1] << 8; \ + ip += 2; +#define DECODE_PTR \ + DECODE_UINT; \ + ptr = (const byte *)const_table[unum] +#define DECODE_OBJ \ + DECODE_UINT; \ + obj = (mp_obj_t)const_table[unum] + +typedef struct _mp_dis_instruction_t { + mp_uint_t qstr_opname; + mp_uint_t arg; + mp_obj_t argobj; + mp_obj_t argobjex_cache; +} mp_dis_instruction_t; + +static const byte *mp_prof_opcode_decode(const byte *ip, const mp_uint_t *const_table, mp_dis_instruction_t *instruction) { + mp_uint_t unum; + const byte *ptr; + mp_obj_t obj; + qstr qst; + + instruction->qstr_opname = MP_QSTR_; + instruction->arg = 0; + instruction->argobj = mp_const_none; + instruction->argobjex_cache = mp_const_none; + + switch (*ip++) { + case MP_BC_LOAD_CONST_FALSE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_FALSE; + break; + + case MP_BC_LOAD_CONST_NONE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_NONE; + break; + + case MP_BC_LOAD_CONST_TRUE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_TRUE; + break; + + case MP_BC_LOAD_CONST_SMALL_INT: { + mp_int_t num = 0; + if ((ip[0] & 0x40) != 0) { + // Number is negative + num--; + } + do { + num = (num << 7) | (*ip & 0x7f); + } while ((*ip++ & 0x80) != 0); + instruction->qstr_opname = MP_QSTR_LOAD_CONST_SMALL_INT; + instruction->arg = num; + break; + } + + case MP_BC_LOAD_CONST_STRING: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_CONST_STRING; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_CONST_OBJ: + DECODE_OBJ; + instruction->qstr_opname = MP_QSTR_LOAD_CONST_OBJ; + instruction->arg = unum; + instruction->argobj = obj; + break; + + case MP_BC_LOAD_NULL: + instruction->qstr_opname = MP_QSTR_LOAD_NULL; + break; + + case MP_BC_LOAD_FAST_N: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_LOAD_FAST_N; + instruction->arg = unum; + break; + + case MP_BC_LOAD_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_LOAD_DEREF; + instruction->arg = unum; + break; + + case MP_BC_LOAD_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_NAME; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_GLOBAL; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_ATTR: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_ATTR; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_METHOD: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_METHOD; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_SUPER_METHOD: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_SUPER_METHOD; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_BUILD_CLASS: + instruction->qstr_opname = MP_QSTR_LOAD_BUILD_CLASS; + break; + + case MP_BC_LOAD_SUBSCR: + instruction->qstr_opname = MP_QSTR_LOAD_SUBSCR; + break; + + case MP_BC_STORE_FAST_N: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_FAST_N; + instruction->arg = unum; + break; + + case MP_BC_STORE_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_DEREF; + instruction->arg = unum; + break; + + case MP_BC_STORE_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_NAME; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_STORE_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_GLOBAL; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_STORE_ATTR: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_ATTR; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_STORE_SUBSCR: + instruction->qstr_opname = MP_QSTR_STORE_SUBSCR; + break; + + case MP_BC_DELETE_FAST: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_DELETE_FAST; + instruction->arg = unum; + break; + + case MP_BC_DELETE_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_DELETE_DEREF; + instruction->arg = unum; + break; + + case MP_BC_DELETE_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_DELETE_NAME; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_DELETE_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_DELETE_GLOBAL; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_DUP_TOP: + instruction->qstr_opname = MP_QSTR_DUP_TOP; + break; + + case MP_BC_DUP_TOP_TWO: + instruction->qstr_opname = MP_QSTR_DUP_TOP_TWO; + break; + + case MP_BC_POP_TOP: + instruction->qstr_opname = MP_QSTR_POP_TOP; + break; + + case MP_BC_ROT_TWO: + instruction->qstr_opname = MP_QSTR_ROT_TWO; + break; + + case MP_BC_ROT_THREE: + instruction->qstr_opname = MP_QSTR_ROT_THREE; + break; + + case MP_BC_JUMP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP; + instruction->arg = unum; + break; + + case MP_BC_POP_JUMP_IF_TRUE: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_POP_JUMP_IF_TRUE; + instruction->arg = unum; + break; + + case MP_BC_POP_JUMP_IF_FALSE: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_POP_JUMP_IF_FALSE; + instruction->arg = unum; + break; + + case MP_BC_JUMP_IF_TRUE_OR_POP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP_IF_TRUE_OR_POP; + instruction->arg = unum; + break; + + case MP_BC_JUMP_IF_FALSE_OR_POP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP_IF_FALSE_OR_POP; + instruction->arg = unum; + break; + + case MP_BC_SETUP_WITH: + DECODE_ULABEL; // loop-like labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_WITH; + instruction->arg = unum; + break; + + case MP_BC_WITH_CLEANUP: + instruction->qstr_opname = MP_QSTR_WITH_CLEANUP; + break; + + case MP_BC_UNWIND_JUMP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_UNWIND_JUMP; + instruction->arg = unum; + break; + + case MP_BC_SETUP_EXCEPT: + DECODE_ULABEL; // except labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_EXCEPT; + instruction->arg = unum; + break; + + case MP_BC_SETUP_FINALLY: + DECODE_ULABEL; // except labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_FINALLY; + instruction->arg = unum; + break; + + case MP_BC_END_FINALLY: + // if TOS is an exception, reraises the exception (3 values on TOS) + // if TOS is an integer, does something else + // if TOS is None, just pops it and continues + // else error + instruction->qstr_opname = MP_QSTR_END_FINALLY; + break; + + case MP_BC_GET_ITER: + instruction->qstr_opname = MP_QSTR_GET_ITER; + break; + + case MP_BC_GET_ITER_STACK: + instruction->qstr_opname = MP_QSTR_GET_ITER_STACK; + break; + + case MP_BC_FOR_ITER: + DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward + instruction->qstr_opname = MP_QSTR_FOR_ITER; + instruction->arg = unum; + break; + + case MP_BC_BUILD_TUPLE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_TUPLE; + instruction->arg = unum; + break; + + case MP_BC_BUILD_LIST: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_LIST; + instruction->arg = unum; + break; + + case MP_BC_BUILD_MAP: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_MAP; + instruction->arg = unum; + break; + + case MP_BC_STORE_MAP: + instruction->qstr_opname = MP_QSTR_STORE_MAP; + break; + + case MP_BC_BUILD_SET: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_SET; + instruction->arg = unum; + break; + + #if MICROPY_PY_BUILTINS_SLICE + case MP_BC_BUILD_SLICE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_SLICE; + instruction->arg = unum; + break; + #endif + + case MP_BC_STORE_COMP: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_COMP; + instruction->arg = unum; + break; + + case MP_BC_UNPACK_SEQUENCE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_UNPACK_SEQUENCE; + instruction->arg = unum; + break; + + case MP_BC_UNPACK_EX: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_UNPACK_EX; + instruction->arg = unum; + break; + + case MP_BC_MAKE_FUNCTION: + DECODE_PTR; + instruction->qstr_opname = MP_QSTR_MAKE_FUNCTION; + instruction->arg = unum; + instruction->argobj = mp_obj_new_int_from_ull((uint64_t)ptr); + break; + + case MP_BC_MAKE_FUNCTION_DEFARGS: + DECODE_PTR; + instruction->qstr_opname = MP_QSTR_MAKE_FUNCTION_DEFARGS; + instruction->arg = unum; + instruction->argobj = mp_obj_new_int_from_ull((uint64_t)ptr); + break; + + case MP_BC_MAKE_CLOSURE: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + instruction->qstr_opname = MP_QSTR_MAKE_CLOSURE; + instruction->arg = unum; + instruction->argobj = mp_obj_new_int_from_ull((uint64_t)ptr); + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(n_closed_over); + break; + } + + case MP_BC_MAKE_CLOSURE_DEFARGS: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + instruction->qstr_opname = MP_QSTR_MAKE_CLOSURE_DEFARGS; + instruction->arg = unum; + instruction->argobj = mp_obj_new_int_from_ull((uint64_t)ptr); + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(n_closed_over); + break; + } + + case MP_BC_CALL_FUNCTION: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_FUNCTION; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_FUNCTION_VAR_KW: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_FUNCTION_VAR_KW; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_METHOD; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD_VAR_KW: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_METHOD_VAR_KW; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_RETURN_VALUE: + instruction->qstr_opname = MP_QSTR_RETURN_VALUE; + break; + + case MP_BC_RAISE_LAST: + instruction->qstr_opname = MP_QSTR_RAISE_LAST; + break; + + case MP_BC_RAISE_OBJ: + instruction->qstr_opname = MP_QSTR_RAISE_OBJ; + break; + + case MP_BC_RAISE_FROM: + instruction->qstr_opname = MP_QSTR_RAISE_FROM; + break; + + case MP_BC_YIELD_VALUE: + instruction->qstr_opname = MP_QSTR_YIELD_VALUE; + break; + + case MP_BC_YIELD_FROM: + instruction->qstr_opname = MP_QSTR_YIELD_FROM; + break; + + case MP_BC_IMPORT_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_IMPORT_NAME; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_IMPORT_FROM: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_IMPORT_FROM; + instruction->arg = qst; + instruction->argobj = MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_IMPORT_STAR: + instruction->qstr_opname = MP_QSTR_IMPORT_STAR; + break; + + default: + if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + 64) { + instruction->qstr_opname = MP_QSTR_LOAD_CONST_SMALL_INT; + instruction->arg = (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16; + } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + 16) { + instruction->qstr_opname = MP_QSTR_LOAD_FAST; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI; + } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { + instruction->qstr_opname = MP_QSTR_STORE_FAST; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI; + } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) { + instruction->qstr_opname = MP_QSTR_UNARY_OP; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_UNARY_OP_MULTI; + } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) { + mp_uint_t op = ip[-1] - MP_BC_BINARY_OP_MULTI; + instruction->qstr_opname = MP_QSTR_BINARY_OP; + instruction->arg = op; + } else { + mp_printf(&mp_plat_print, "code %p, opcode 0x%02x not implemented\n", ip - 1, ip[-1]); + assert(0); + return ip; + } + break; + } + + return ip; +} + +void mp_prof_print_instr(const byte *ip, mp_code_state_t *code_state) { + mp_dis_instruction_t _instruction, *instruction = &_instruction; + mp_prof_opcode_decode(ip, code_state->fun_bc->rc->const_table, instruction); + const mp_raw_code_t *rc = code_state->fun_bc->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + + mp_uint_t offset = ip - prelude->opcodes; + mp_printf(&mp_plat_print, "instr"); + + /* long path */ if (1) { + mp_printf(&mp_plat_print, + "@0x%p:%q:%q+0x%04x:%d", + ip, + prelude->qstr_source_file, + prelude->qstr_block_name, + offset, + mp_prof_bytecode_lineno(rc, offset) + ); + } + + /* bytecode */ if (0) { + mp_printf(&mp_plat_print, " %02x %02x %02x %02x", ip[0], ip[1], ip[2], ip[3]); + } + + mp_printf(&mp_plat_print, " 0x%02x %q [%d]", *ip, instruction->qstr_opname, instruction->arg); + + if (instruction->argobj != mp_const_none) { + mp_printf(&mp_plat_print, " $"); + mp_obj_print_helper(&mp_plat_print, instruction->argobj, PRINT_REPR); + } + if (instruction->argobjex_cache != mp_const_none) { + mp_printf(&mp_plat_print, " #"); + mp_obj_print_helper(&mp_plat_print, instruction->argobjex_cache, PRINT_REPR); + } + + mp_printf(&mp_plat_print, "\n"); +} + +#endif // MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE + +#endif // MICROPY_PY_SYS_SETTRACE diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/profile.h b/non_catalog_apps/mp_flipper/lib/micropython/py/profile.h new file mode 100644 index 00000000..7f3f9140 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/profile.h @@ -0,0 +1,81 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) SatoshiLabs + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_PY_PROFILING_H +#define MICROPY_INCLUDED_PY_PROFILING_H + +#include "py/emitglue.h" + +#if MICROPY_PY_SYS_SETTRACE + +#define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing) + +typedef struct _mp_obj_code_t { + // TODO this was 4 words + mp_obj_base_t base; + const mp_module_context_t *context; + const mp_raw_code_t *rc; + mp_obj_dict_t *dict_locals; + mp_obj_t lnotab; +} mp_obj_code_t; + +typedef struct _mp_obj_frame_t { + mp_obj_base_t base; + const mp_code_state_t *code_state; + struct _mp_obj_frame_t *back; + mp_obj_t callback; + mp_obj_code_t *code; + mp_uint_t lasti; + mp_uint_t lineno; + bool trace_opcodes; +} mp_obj_frame_t; + +void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude); + +mp_obj_t mp_obj_new_code(const mp_module_context_t *mc, const mp_raw_code_t *rc); +mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state); + +// This is the implementation for the sys.settrace +mp_obj_t mp_prof_settrace(mp_obj_t callback); + +mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state); +mp_obj_t mp_prof_frame_update(const mp_code_state_t *code_state); + +// For every VM instruction tick this function deduces events from the state +mp_obj_t mp_prof_instr_tick(mp_code_state_t *code_state, bool is_exception); + +// This section is for debugging the settrace feature itself, and is not intended +// to be included in production/release builds. +#define MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE 0 +#if MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE +void mp_prof_print_instr(const byte *ip, mp_code_state_t *code_state); +#define MP_PROF_INSTR_DEBUG_PRINT(current_ip) mp_prof_print_instr((current_ip), code_state) +#else +#define MP_PROF_INSTR_DEBUG_PRINT(current_ip) +#endif + +#endif // MICROPY_PY_SYS_SETTRACE +#endif // MICROPY_INCLUDED_PY_PROFILING_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/pystack.c b/non_catalog_apps/mp_flipper/lib/micropython/py/pystack.c new file mode 100644 index 00000000..ea5f0d9d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/pystack.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Damien P. George + * + * 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 "py/runtime.h" + +#if MICROPY_ENABLE_PYSTACK + +void mp_pystack_init(void *start, void *end) { + MP_STATE_THREAD(pystack_start) = start; + MP_STATE_THREAD(pystack_end) = end; + MP_STATE_THREAD(pystack_cur) = start; +} + +void *mp_pystack_alloc(size_t n_bytes) { + n_bytes = (n_bytes + (MICROPY_PYSTACK_ALIGN - 1)) & ~(MICROPY_PYSTACK_ALIGN - 1); + #if MP_PYSTACK_DEBUG + n_bytes += MICROPY_PYSTACK_ALIGN; + #endif + if (MP_STATE_THREAD(pystack_cur) + n_bytes > MP_STATE_THREAD(pystack_end)) { + // out of memory in the pystack + mp_raise_type_arg(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_pystack_space_exhausted)); + } + void *ptr = MP_STATE_THREAD(pystack_cur); + MP_STATE_THREAD(pystack_cur) += n_bytes; + #if MP_PYSTACK_DEBUG + *(size_t *)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN) = n_bytes; + #endif + return ptr; +} + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/pystack.h b/non_catalog_apps/mp_flipper/lib/micropython/py/pystack.h new file mode 100644 index 00000000..ea8fddcf --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/pystack.h @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_PYSTACK_H +#define MICROPY_INCLUDED_PY_PYSTACK_H + +#include "py/mpstate.h" + +// Enable this debugging option to check that the amount of memory freed is +// consistent with amounts that were previously allocated. +#define MP_PYSTACK_DEBUG (0) + +#if MICROPY_ENABLE_PYSTACK + +void mp_pystack_init(void *start, void *end); +void *mp_pystack_alloc(size_t n_bytes); + +// This function can free multiple continuous blocks at once: just pass the +// pointer to the block that was allocated first and it and all subsequently +// allocated blocks will be freed. +static inline void mp_pystack_free(void *ptr) { + assert((uint8_t *)ptr >= MP_STATE_THREAD(pystack_start)); + assert((uint8_t *)ptr <= MP_STATE_THREAD(pystack_cur)); + #if MP_PYSTACK_DEBUG + size_t n_bytes_to_free = MP_STATE_THREAD(pystack_cur) - (uint8_t *)ptr; + size_t n_bytes = *(size_t *)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN); + while (n_bytes < n_bytes_to_free) { + n_bytes += *(size_t *)(MP_STATE_THREAD(pystack_cur) - n_bytes - MICROPY_PYSTACK_ALIGN); + } + if (n_bytes != n_bytes_to_free) { + mp_printf(&mp_plat_print, "mp_pystack_free() failed: %u != %u\n", (uint)n_bytes_to_free, + (uint)*(size_t *)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN)); + assert(0); + } + #endif + MP_STATE_THREAD(pystack_cur) = (uint8_t *)ptr; +} + +static inline void mp_pystack_realloc(void *ptr, size_t n_bytes) { + mp_pystack_free(ptr); + mp_pystack_alloc(n_bytes); +} + +static inline size_t mp_pystack_usage(void) { + return MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start); +} + +static inline size_t mp_pystack_limit(void) { + return MP_STATE_THREAD(pystack_end) - MP_STATE_THREAD(pystack_start); +} + +#endif + +#if !MICROPY_ENABLE_PYSTACK + +#define mp_local_alloc(n_bytes) alloca(n_bytes) + +static inline void mp_local_free(void *ptr) { + (void)ptr; +} + +static inline void *mp_nonlocal_alloc(size_t n_bytes) { + return m_new(uint8_t, n_bytes); +} + +static inline void *mp_nonlocal_realloc(void *ptr, size_t old_n_bytes, size_t new_n_bytes) { + return m_renew(uint8_t, ptr, old_n_bytes, new_n_bytes); +} + +static inline void mp_nonlocal_free(void *ptr, size_t n_bytes) { + m_del(uint8_t, ptr, n_bytes); +} + +#else + +static inline void *mp_local_alloc(size_t n_bytes) { + return mp_pystack_alloc(n_bytes); +} + +static inline void mp_local_free(void *ptr) { + mp_pystack_free(ptr); +} + +static inline void *mp_nonlocal_alloc(size_t n_bytes) { + return mp_pystack_alloc(n_bytes); +} + +static inline void *mp_nonlocal_realloc(void *ptr, size_t old_n_bytes, size_t new_n_bytes) { + (void)old_n_bytes; + mp_pystack_realloc(ptr, new_n_bytes); + return ptr; +} + +static inline void mp_nonlocal_free(void *ptr, size_t n_bytes) { + (void)n_bytes; + mp_pystack_free(ptr); +} + +#endif + +#endif // MICROPY_INCLUDED_PY_PYSTACK_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/qstr.c b/non_catalog_apps/mp_flipper/lib/micropython/py/qstr.c new file mode 100644 index 00000000..fea7f44f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/qstr.c @@ -0,0 +1,510 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpstate.h" +#include "py/qstr.h" +#include "py/gc.h" +#include "py/runtime.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_printf DEBUG_printf +#else // don't print debugging info +#define DEBUG_printf(...) (void)0 +#endif + +// A qstr is an index into the qstr pool. +// The data for a qstr is \0 terminated (so they can be printed using printf) + +#if MICROPY_QSTR_BYTES_IN_HASH +#define Q_HASH_MASK ((1 << (8 * MICROPY_QSTR_BYTES_IN_HASH)) - 1) +#else +#define Q_HASH_MASK (0xffff) +#endif + +#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL +#define QSTR_ENTER() mp_thread_mutex_lock(&MP_STATE_VM(qstr_mutex), 1) +#define QSTR_EXIT() mp_thread_mutex_unlock(&MP_STATE_VM(qstr_mutex)) +#else +#define QSTR_ENTER() +#define QSTR_EXIT() +#endif + +// Initial number of entries for qstr pool, set so that the first dynamically +// allocated pool is twice this size. The value here must be <= MP_QSTRnumber_of. +#define MICROPY_ALLOC_QSTR_ENTRIES_INIT (10) + +// this must match the equivalent function in makeqstrdata.py +size_t qstr_compute_hash(const byte *data, size_t len) { + // djb2 algorithm; see http://www.cse.yorku.ca/~oz/hash.html + size_t hash = 5381; + for (const byte *top = data + len; data < top; data++) { + hash = ((hash << 5) + hash) ^ (*data); // hash * 33 ^ data + } + hash &= Q_HASH_MASK; + // Make sure that valid hash is never zero, zero means "hash not computed" + if (hash == 0) { + hash++; + } + return hash; +} + +// The first pool is the static qstr table. The contents must remain stable as +// it is part of the .mpy ABI. See the top of py/persistentcode.c and +// static_qstr_list in makeqstrdata.py. This pool is unsorted (although in a +// future .mpy version we could re-order them and make it sorted). It also +// contains additional qstrs that must have IDs <256, see unsorted_qstr_list +// in makeqstrdata.py. +#if MICROPY_QSTR_BYTES_IN_HASH +const qstr_hash_t mp_qstr_const_hashes_static[] = { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) hash, +#define QDEF1(id, hash, len, str) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif +}; +#endif + +const qstr_len_t mp_qstr_const_lengths_static[] = { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) len, +#define QDEF1(id, hash, len, str) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif +}; + +const qstr_pool_t mp_qstr_const_pool_static = { + NULL, // no previous pool + 0, // no previous pool + false, // is_sorted + MICROPY_ALLOC_QSTR_ENTRIES_INIT, + MP_QSTRnumber_of_static, // corresponds to number of strings in array just below + #if MICROPY_QSTR_BYTES_IN_HASH + (qstr_hash_t *)mp_qstr_const_hashes_static, + #endif + (qstr_len_t *)mp_qstr_const_lengths_static, + { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) str, +#define QDEF1(id, hash, len, str) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif + }, +}; + +// The next pool is the remainder of the qstrs defined in the firmware. This +// is sorted. +#if MICROPY_QSTR_BYTES_IN_HASH +const qstr_hash_t mp_qstr_const_hashes[] = { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) hash, + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif +}; +#endif + +const qstr_len_t mp_qstr_const_lengths[] = { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) len, + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif +}; + +const qstr_pool_t mp_qstr_const_pool = { + &mp_qstr_const_pool_static, + MP_QSTRnumber_of_static, + true, // is_sorted + MICROPY_ALLOC_QSTR_ENTRIES_INIT, + MP_QSTRnumber_of - MP_QSTRnumber_of_static, // corresponds to number of strings in array just below + #if MICROPY_QSTR_BYTES_IN_HASH + (qstr_hash_t *)mp_qstr_const_hashes, + #endif + (qstr_len_t *)mp_qstr_const_lengths, + { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) str, + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif + }, +}; + +// If frozen code is enabled, then there is an additional, sorted, ROM pool +// containing additional qstrs required by the frozen code. +#ifdef MICROPY_QSTR_EXTRA_POOL +extern const qstr_pool_t MICROPY_QSTR_EXTRA_POOL; +#define CONST_POOL MICROPY_QSTR_EXTRA_POOL +#else +#define CONST_POOL mp_qstr_const_pool +#endif + +void qstr_init(void) { + MP_STATE_VM(last_pool) = (qstr_pool_t *)&CONST_POOL; // we won't modify the const_pool since it has no allocated room left + MP_STATE_VM(qstr_last_chunk) = NULL; + + #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL + mp_thread_mutex_init(&MP_STATE_VM(qstr_mutex)); + #endif +} + +static const qstr_pool_t *find_qstr(qstr *q) { + // search pool for this qstr + // total_prev_len==0 in the final pool, so the loop will always terminate + const qstr_pool_t *pool = MP_STATE_VM(last_pool); + while (*q < pool->total_prev_len) { + pool = pool->prev; + } + *q -= pool->total_prev_len; + assert(*q < pool->len); + return pool; +} + +// qstr_mutex must be taken while in this function +static qstr qstr_add(mp_uint_t len, const char *q_ptr) { + #if MICROPY_QSTR_BYTES_IN_HASH + mp_uint_t hash = qstr_compute_hash((const byte *)q_ptr, len); + DEBUG_printf("QSTR: add hash=%d len=%d data=%.*s\n", hash, len, len, q_ptr); + #else + DEBUG_printf("QSTR: add len=%d data=%.*s\n", len, len, q_ptr); + #endif + + // make sure we have room in the pool for a new qstr + if (MP_STATE_VM(last_pool)->len >= MP_STATE_VM(last_pool)->alloc) { + size_t new_alloc = MP_STATE_VM(last_pool)->alloc * 2; + #ifdef MICROPY_QSTR_EXTRA_POOL + // Put a lower bound on the allocation size in case the extra qstr pool has few entries + new_alloc = MAX(MICROPY_ALLOC_QSTR_ENTRIES_INIT, new_alloc); + #endif + mp_uint_t pool_size = sizeof(qstr_pool_t) + + (sizeof(const char *) + #if MICROPY_QSTR_BYTES_IN_HASH + + sizeof(qstr_hash_t) + #endif + + sizeof(qstr_len_t)) * new_alloc; + qstr_pool_t *pool = (qstr_pool_t *)m_malloc_maybe(pool_size); + if (pool == NULL) { + // Keep qstr_last_chunk consistent with qstr_pool_t: qstr_last_chunk is not scanned + // at garbage collection since it's reachable from a qstr_pool_t. And the caller of + // this function expects q_ptr to be stored in a qstr_pool_t so it can be reached + // by the collector. If qstr_pool_t allocation failed, qstr_last_chunk needs to be + // NULL'd. Otherwise it may become a dangling pointer at the next garbage collection. + MP_STATE_VM(qstr_last_chunk) = NULL; + QSTR_EXIT(); + m_malloc_fail(new_alloc); + } + #if MICROPY_QSTR_BYTES_IN_HASH + pool->hashes = (qstr_hash_t *)(pool->qstrs + new_alloc); + pool->lengths = (qstr_len_t *)(pool->hashes + new_alloc); + #else + pool->lengths = (qstr_len_t *)(pool->qstrs + new_alloc); + #endif + pool->prev = MP_STATE_VM(last_pool); + pool->total_prev_len = MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len; + pool->alloc = new_alloc; + pool->len = 0; + MP_STATE_VM(last_pool) = pool; + DEBUG_printf("QSTR: allocate new pool of size %d\n", MP_STATE_VM(last_pool)->alloc); + } + + // add the new qstr + mp_uint_t at = MP_STATE_VM(last_pool)->len; + #if MICROPY_QSTR_BYTES_IN_HASH + MP_STATE_VM(last_pool)->hashes[at] = hash; + #endif + MP_STATE_VM(last_pool)->lengths[at] = len; + MP_STATE_VM(last_pool)->qstrs[at] = q_ptr; + MP_STATE_VM(last_pool)->len++; + + // return id for the newly-added qstr + return MP_STATE_VM(last_pool)->total_prev_len + at; +} + +qstr qstr_find_strn(const char *str, size_t str_len) { + if (str_len == 0) { + // strncmp behaviour is undefined for str==NULL. + return MP_QSTR_; + } + + #if MICROPY_QSTR_BYTES_IN_HASH + // work out hash of str + size_t str_hash = qstr_compute_hash((const byte *)str, str_len); + #endif + + // search pools for the data + for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) { + size_t low = 0; + size_t high = pool->len - 1; + + // binary search inside the pool + if (pool->is_sorted) { + while (high - low > 1) { + size_t mid = (low + high) / 2; + int cmp = strncmp(str, pool->qstrs[mid], str_len); + if (cmp <= 0) { + high = mid; + } else { + low = mid; + } + } + } + + // sequential search for the remaining strings + for (mp_uint_t at = low; at < high + 1; at++) { + if ( + #if MICROPY_QSTR_BYTES_IN_HASH + pool->hashes[at] == str_hash && + #endif + pool->lengths[at] == str_len + && memcmp(pool->qstrs[at], str, str_len) == 0) { + return pool->total_prev_len + at; + } + } + } + + // not found; return null qstr + return MP_QSTRnull; +} + +qstr qstr_from_str(const char *str) { + return qstr_from_strn(str, strlen(str)); +} + +qstr qstr_from_strn(const char *str, size_t len) { + QSTR_ENTER(); + qstr q = qstr_find_strn(str, len); + if (q == 0) { + // qstr does not exist in interned pool so need to add it + + // check that len is not too big + if (len >= (1 << (8 * MICROPY_QSTR_BYTES_IN_LEN))) { + QSTR_EXIT(); + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("name too long")); + } + + // compute number of bytes needed to intern this string + size_t n_bytes = len + 1; + + if (MP_STATE_VM(qstr_last_chunk) != NULL && MP_STATE_VM(qstr_last_used) + n_bytes > MP_STATE_VM(qstr_last_alloc)) { + // not enough room at end of previously interned string so try to grow + char *new_p = m_renew_maybe(char, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_alloc) + n_bytes, false); + if (new_p == NULL) { + // could not grow existing memory; shrink it to fit previous + (void)m_renew_maybe(char, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_used), false); + MP_STATE_VM(qstr_last_chunk) = NULL; + } else { + // could grow existing memory + MP_STATE_VM(qstr_last_alloc) += n_bytes; + } + } + + if (MP_STATE_VM(qstr_last_chunk) == NULL) { + // no existing memory for the interned string so allocate a new chunk + size_t al = n_bytes; + if (al < MICROPY_ALLOC_QSTR_CHUNK_INIT) { + al = MICROPY_ALLOC_QSTR_CHUNK_INIT; + } + MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, al); + if (MP_STATE_VM(qstr_last_chunk) == NULL) { + // failed to allocate a large chunk so try with exact size + MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, n_bytes); + if (MP_STATE_VM(qstr_last_chunk) == NULL) { + QSTR_EXIT(); + m_malloc_fail(n_bytes); + } + al = n_bytes; + } + MP_STATE_VM(qstr_last_alloc) = al; + MP_STATE_VM(qstr_last_used) = 0; + } + + // allocate memory from the chunk for this new interned string's data + char *q_ptr = MP_STATE_VM(qstr_last_chunk) + MP_STATE_VM(qstr_last_used); + MP_STATE_VM(qstr_last_used) += n_bytes; + + // store the interned strings' data + memcpy(q_ptr, str, len); + q_ptr[len] = '\0'; + q = qstr_add(len, q_ptr); + } + QSTR_EXIT(); + return q; +} + +mp_uint_t qstr_hash(qstr q) { + const qstr_pool_t *pool = find_qstr(&q); + #if MICROPY_QSTR_BYTES_IN_HASH + return pool->hashes[q]; + #else + return qstr_compute_hash((byte *)pool->qstrs[q], pool->lengths[q]); + #endif +} + +size_t qstr_len(qstr q) { + const qstr_pool_t *pool = find_qstr(&q); + return pool->lengths[q]; +} + +const char *qstr_str(qstr q) { + const qstr_pool_t *pool = find_qstr(&q); + return pool->qstrs[q]; +} + +const byte *qstr_data(qstr q, size_t *len) { + const qstr_pool_t *pool = find_qstr(&q); + *len = pool->lengths[q]; + return (byte *)pool->qstrs[q]; +} + +void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, size_t *n_total_bytes) { + QSTR_ENTER(); + *n_pool = 0; + *n_qstr = 0; + *n_str_data_bytes = 0; + *n_total_bytes = 0; + for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { + *n_pool += 1; + *n_qstr += pool->len; + for (qstr_len_t *l = pool->lengths, *l_top = pool->lengths + pool->len; l < l_top; l++) { + *n_str_data_bytes += *l + 1; + } + #if MICROPY_ENABLE_GC + *n_total_bytes += gc_nbytes(pool); // this counts actual bytes used in heap + #else + *n_total_bytes += sizeof(qstr_pool_t) + + (sizeof(const char *) + #if MICROPY_QSTR_BYTES_IN_HASH + + sizeof(qstr_hash_t) + #endif + + sizeof(qstr_len_t)) * pool->alloc; + #endif + } + *n_total_bytes += *n_str_data_bytes; + QSTR_EXIT(); +} + +#if MICROPY_PY_MICROPYTHON_MEM_INFO +void qstr_dump_data(void) { + QSTR_ENTER(); + for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { + for (const char *const *q = pool->qstrs, *const *q_top = pool->qstrs + pool->len; q < q_top; q++) { + mp_printf(&mp_plat_print, "Q(%s)\n", *q); + } + } + QSTR_EXIT(); +} +#endif + +#if MICROPY_ROM_TEXT_COMPRESSION + +#ifdef NO_QSTR + +// If NO_QSTR is set, it means we're doing QSTR extraction. +// So we won't yet have "genhdr/compressed.data.h" + +#else + +// Emit the compressed_string_data string. +#define MP_COMPRESSED_DATA(x) static const char *compressed_string_data = x; +#define MP_MATCH_COMPRESSED(a, b) +#include "genhdr/compressed.data.h" +#undef MP_COMPRESSED_DATA +#undef MP_MATCH_COMPRESSED + +#endif // NO_QSTR + +// This implements the "common word" compression scheme (see makecompresseddata.py) where the most +// common 128 words in error messages are replaced by their index into the list of common words. + +// The compressed string data is delimited by setting high bit in the final char of each word. +// e.g. aaaa<0x80|a>bbbbbb<0x80|b>.... +// This method finds the n'th string. +static const byte *find_uncompressed_string(uint8_t n) { + const byte *c = (byte *)compressed_string_data; + while (n > 0) { + while ((*c & 0x80) == 0) { + ++c; + } + ++c; + --n; + } + return c; +} + +// Given a compressed string in src, decompresses it into dst. +// dst must be large enough (use MP_MAX_UNCOMPRESSED_TEXT_LEN+1). +void mp_decompress_rom_string(byte *dst, const mp_rom_error_text_t src_chr) { + // Skip past the 0xff marker. + const byte *src = (byte *)src_chr + 1; + // Need to add spaces around compressed words, except for the first (i.e. transition from 1<->2). + // 0 = start, 1 = compressed, 2 = regular. + int state = 0; + while (*src) { + if ((byte) * src >= 128) { + if (state != 0) { + *dst++ = ' '; + } + state = 1; + + // High bit set, replace with common word. + const byte *word = find_uncompressed_string(*src & 0x7f); + // The word is terminated by the final char having its high bit set. + while ((*word & 0x80) == 0) { + *dst++ = *word++; + } + *dst++ = (*word & 0x7f); + } else { + // Otherwise just copy one char. + if (state == 1) { + *dst++ = ' '; + } + state = 2; + + *dst++ = *src; + } + ++src; + } + // Add null-terminator. + *dst = 0; +} + +#endif // MICROPY_ROM_TEXT_COMPRESSION diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/qstr.h b/non_catalog_apps/mp_flipper/lib/micropython/py/qstr.h new file mode 100644 index 00000000..58ce285f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/qstr.h @@ -0,0 +1,118 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_QSTR_H +#define MICROPY_INCLUDED_PY_QSTR_H + +#include "py/mpconfig.h" +#include "py/misc.h" + +// See qstrdefs.h for a list of qstr's that are available as constants. +// Reference them as MP_QSTR_xxxx. +// +// Note: it would be possible to define MP_QSTR_xxx as qstr_from_str("xxx") +// for qstrs that are referenced this way, but you don't want to have them in ROM. + +// first entry in enum will be MP_QSTRnull=0, which indicates invalid/no qstr +enum { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) id, +#define QDEF1(id, hash, len, str) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif + MP_QSTRnumber_of_static, + MP_QSTRstart_of_main = MP_QSTRnumber_of_static - 1, // unused but shifts the enum counter back one + + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) id, + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 + #endif + MP_QSTRnumber_of, // no underscore so it can't clash with any of the above +}; + +typedef size_t qstr; +typedef uint16_t qstr_short_t; + +#if MICROPY_QSTR_BYTES_IN_HASH == 0 +// No qstr_hash_t type needed. +#elif MICROPY_QSTR_BYTES_IN_HASH == 1 +typedef uint8_t qstr_hash_t; +#elif MICROPY_QSTR_BYTES_IN_HASH == 2 +typedef uint16_t qstr_hash_t; +#else +#error unimplemented qstr hash decoding +#endif + +#if MICROPY_QSTR_BYTES_IN_LEN == 1 +typedef uint8_t qstr_len_t; +#elif MICROPY_QSTR_BYTES_IN_LEN == 2 +typedef uint16_t qstr_len_t; +#else +#error unimplemented qstr length decoding +#endif + +typedef struct _qstr_pool_t { + const struct _qstr_pool_t *prev; + size_t total_prev_len : (8 * sizeof(size_t) - 1); + size_t is_sorted : 1; + size_t alloc; + size_t len; + #if MICROPY_QSTR_BYTES_IN_HASH + qstr_hash_t *hashes; + #endif + qstr_len_t *lengths; + const char *qstrs[]; +} qstr_pool_t; + +#define QSTR_TOTAL() (MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len) + +void qstr_init(void); + +size_t qstr_compute_hash(const byte *data, size_t len); + +qstr qstr_find_strn(const char *str, size_t str_len); // returns MP_QSTRnull if not found + +qstr qstr_from_str(const char *str); +qstr qstr_from_strn(const char *str, size_t len); + +mp_uint_t qstr_hash(qstr q); +const char *qstr_str(qstr q); +size_t qstr_len(qstr q); +const byte *qstr_data(qstr q, size_t *len); + +void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, size_t *n_total_bytes); +void qstr_dump_data(void); + +#if MICROPY_ROM_TEXT_COMPRESSION +void mp_decompress_rom_string(byte *dst, const mp_rom_error_text_t src); +#define MP_IS_COMPRESSED_ROM_STRING(s) (*(byte *)(s) == 0xff) +#endif + +#endif // MICROPY_INCLUDED_PY_QSTR_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/qstrdefs.h b/non_catalog_apps/mp_flipper/lib/micropython/py/qstrdefs.h new file mode 100644 index 00000000..5003636d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/qstrdefs.h @@ -0,0 +1,73 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ + +// *FORMAT-OFF* + +#include "py/mpconfig.h" + +// All the qstr definitions in this file are available as constants. +// That is, they are in ROM and you can reference them simply as MP_QSTR_xxxx. + +// qstr configuration passed to makeqstrdata.py of the form QCFG(key, value) +QCFG(BYTES_IN_LEN, MICROPY_QSTR_BYTES_IN_LEN) +QCFG(BYTES_IN_HASH, MICROPY_QSTR_BYTES_IN_HASH) + +Q() +Q(*) +Q(_) +Q(/) +#if MICROPY_PY_SYS_PS1_PS2 +Q(>>> ) +Q(... ) +#endif +#if MICROPY_PY_BUILTINS_STR_OP_MODULO +Q(%#o) +Q(%#x) +#else +Q({:#o}) +Q({:#x}) +#endif +Q({:#b}) +Q( ) +Q(\n) +Q(maximum recursion depth exceeded) +Q() +Q() +Q() +Q() +Q() +Q() +Q() +Q() +Q(utf-8) + +#if MICROPY_MODULE_FROZEN +Q(.frozen) +#endif + +#if MICROPY_ENABLE_PYSTACK +Q(pystack exhausted) +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/reader.c b/non_catalog_apps/mp_flipper/lib/micropython/py/reader.c new file mode 100644 index 00000000..151e04ca --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/reader.c @@ -0,0 +1,148 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * + * 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 "py/runtime.h" +#include "py/mperrno.h" +#include "py/mpthread.h" +#include "py/reader.h" + +typedef struct _mp_reader_mem_t { + size_t free_len; // if >0 mem is freed on close by: m_free(beg, free_len) + const byte *beg; + const byte *cur; + const byte *end; +} mp_reader_mem_t; + +static mp_uint_t mp_reader_mem_readbyte(void *data) { + mp_reader_mem_t *reader = (mp_reader_mem_t *)data; + if (reader->cur < reader->end) { + return *reader->cur++; + } else { + return MP_READER_EOF; + } +} + +static void mp_reader_mem_close(void *data) { + mp_reader_mem_t *reader = (mp_reader_mem_t *)data; + if (reader->free_len > 0) { + m_del(char, (char *)reader->beg, reader->free_len); + } + m_del_obj(mp_reader_mem_t, reader); +} + +void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len) { + mp_reader_mem_t *rm = m_new_obj(mp_reader_mem_t); + rm->free_len = free_len; + rm->beg = buf; + rm->cur = buf; + rm->end = buf + len; + reader->data = rm; + reader->readbyte = mp_reader_mem_readbyte; + reader->close = mp_reader_mem_close; +} + +#if MICROPY_READER_POSIX + +#include +#include +#include + +typedef struct _mp_reader_posix_t { + bool close_fd; + int fd; + size_t len; + size_t pos; + byte buf[20]; +} mp_reader_posix_t; + +static mp_uint_t mp_reader_posix_readbyte(void *data) { + mp_reader_posix_t *reader = (mp_reader_posix_t *)data; + if (reader->pos >= reader->len) { + if (reader->len == 0) { + return MP_READER_EOF; + } else { + MP_THREAD_GIL_EXIT(); + int n = read(reader->fd, reader->buf, sizeof(reader->buf)); + MP_THREAD_GIL_ENTER(); + if (n <= 0) { + reader->len = 0; + return MP_READER_EOF; + } + reader->len = n; + reader->pos = 0; + } + } + return reader->buf[reader->pos++]; +} + +static void mp_reader_posix_close(void *data) { + mp_reader_posix_t *reader = (mp_reader_posix_t *)data; + if (reader->close_fd) { + MP_THREAD_GIL_EXIT(); + close(reader->fd); + MP_THREAD_GIL_ENTER(); + } + m_del_obj(mp_reader_posix_t, reader); +} + +void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { + mp_reader_posix_t *rp = m_new_obj(mp_reader_posix_t); + rp->close_fd = close_fd; + rp->fd = fd; + MP_THREAD_GIL_EXIT(); + int n = read(rp->fd, rp->buf, sizeof(rp->buf)); + if (n == -1) { + if (close_fd) { + close(fd); + } + MP_THREAD_GIL_ENTER(); + mp_raise_OSError(errno); + } + MP_THREAD_GIL_ENTER(); + rp->len = n; + rp->pos = 0; + reader->data = rp; + reader->readbyte = mp_reader_posix_readbyte; + reader->close = mp_reader_posix_close; +} + +#if !MICROPY_VFS_POSIX +// If MICROPY_VFS_POSIX is defined then this function is provided by the VFS layer +void mp_reader_new_file(mp_reader_t *reader, qstr filename) { + MP_THREAD_GIL_EXIT(); + int fd = open(qstr_str(filename), O_RDONLY, 0644); + MP_THREAD_GIL_ENTER(); + if (fd < 0) { + mp_raise_OSError_with_filename(errno, qstr_str(filename)); + } + mp_reader_new_file_from_fd(reader, fd, true); +} +#endif + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/reader.h b/non_catalog_apps/mp_flipper/lib/micropython/py/reader.h new file mode 100644 index 00000000..5cb1e679 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/reader.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_READER_H +#define MICROPY_INCLUDED_PY_READER_H + +#include "py/obj.h" + +// the readbyte function must return the next byte in the input stream +// it must return MP_READER_EOF if end of stream +// it can be called again after returning MP_READER_EOF, and in that case must return MP_READER_EOF +#define MP_READER_EOF ((mp_uint_t)(-1)) + +typedef struct _mp_reader_t { + void *data; + mp_uint_t (*readbyte)(void *data); + void (*close)(void *data); +} mp_reader_t; + +void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len); +void mp_reader_new_file(mp_reader_t *reader, qstr filename); +void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); + +#endif // MICROPY_INCLUDED_PY_READER_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/repl.c b/non_catalog_apps/mp_flipper/lib/micropython/py/repl.c new file mode 100644 index 00000000..b79a2d3c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/repl.c @@ -0,0 +1,331 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * + * 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 "py/obj.h" +#include "py/objmodule.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/repl.h" + +#if MICROPY_HELPER_REPL + +#if MICROPY_PY_SYS_PS1_PS2 +const char *mp_repl_get_psx(unsigned int entry) { + if (mp_obj_is_str(MP_STATE_VM(sys_mutable)[entry])) { + return mp_obj_str_get_str(MP_STATE_VM(sys_mutable)[entry]); + } else { + return ""; + } +} +#endif + +static bool str_startswith_word(const char *str, const char *head) { + size_t i; + for (i = 0; str[i] && head[i]; i++) { + if (str[i] != head[i]) { + return false; + } + } + return head[i] == '\0' && (str[i] == '\0' || !unichar_isident(str[i])); +} + +bool mp_repl_continue_with_input(const char *input) { + // check for blank input + if (input[0] == '\0') { + return false; + } + + // check if input starts with a certain keyword + bool starts_with_compound_keyword = + input[0] == '@' + || str_startswith_word(input, "if") + || str_startswith_word(input, "while") + || str_startswith_word(input, "for") + || str_startswith_word(input, "try") + || str_startswith_word(input, "with") + || str_startswith_word(input, "def") + || str_startswith_word(input, "class") + #if MICROPY_PY_ASYNC_AWAIT + || str_startswith_word(input, "async") + #endif + ; + + // check for unmatched open bracket, quote or escape quote + #define Q_NONE (0) + #define Q_1_SINGLE (1) + #define Q_1_DOUBLE (2) + #define Q_3_SINGLE (3) + #define Q_3_DOUBLE (4) + int n_paren = 0; + int n_brack = 0; + int n_brace = 0; + int in_quote = Q_NONE; + const char *i; + for (i = input; *i; i++) { + if (*i == '\'') { + if ((in_quote == Q_NONE || in_quote == Q_3_SINGLE) && i[1] == '\'' && i[2] == '\'') { + i += 2; + in_quote = Q_3_SINGLE - in_quote; + } else if (in_quote == Q_NONE || in_quote == Q_1_SINGLE) { + in_quote = Q_1_SINGLE - in_quote; + } + } else if (*i == '"') { + if ((in_quote == Q_NONE || in_quote == Q_3_DOUBLE) && i[1] == '"' && i[2] == '"') { + i += 2; + in_quote = Q_3_DOUBLE - in_quote; + } else if (in_quote == Q_NONE || in_quote == Q_1_DOUBLE) { + in_quote = Q_1_DOUBLE - in_quote; + } + } else if (*i == '\\' && (i[1] == '\'' || i[1] == '"' || i[1] == '\\')) { + if (in_quote != Q_NONE) { + i++; + } + } else if (in_quote == Q_NONE) { + switch (*i) { + case '(': + n_paren += 1; + break; + case ')': + n_paren -= 1; + break; + case '[': + n_brack += 1; + break; + case ']': + n_brack -= 1; + break; + case '{': + n_brace += 1; + break; + case '}': + n_brace -= 1; + break; + default: + break; + } + } + } + + // continue if unmatched 3-quotes + if (in_quote == Q_3_SINGLE || in_quote == Q_3_DOUBLE) { + return true; + } + + // continue if unmatched brackets, but only if not in a 1-quote + if ((n_paren > 0 || n_brack > 0 || n_brace > 0) && in_quote == Q_NONE) { + return true; + } + + // continue if last character was backslash (for line continuation) + if (i[-1] == '\\') { + return true; + } + + // continue if compound keyword and last line was not empty + if (starts_with_compound_keyword && i[-1] != '\n') { + return true; + } + + // otherwise, don't continue + return false; +} + +static bool test_qstr(mp_obj_t obj, qstr name) { + if (obj) { + // try object member + mp_obj_t dest[2]; + mp_load_method_protected(obj, name, dest, true); + return dest[0] != MP_OBJ_NULL; + } else { + // try builtin module + return mp_map_lookup((mp_map_t *)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP) || + mp_map_lookup((mp_map_t *)&mp_builtin_extensible_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP); + } +} + +static const char *find_completions(const char *s_start, size_t s_len, + mp_obj_t obj, size_t *match_len, qstr *q_first, qstr *q_last) { + + const char *match_str = NULL; + *match_len = 0; + *q_first = *q_last = 0; + size_t nqstr = QSTR_TOTAL(); + for (qstr q = MP_QSTR_ + 1; q < nqstr; ++q) { + size_t d_len; + const char *d_str = (const char *)qstr_data(q, &d_len); + // special case; filter out words that begin with underscore + // unless there's already a partial match + if (s_len == 0 && d_str[0] == '_') { + continue; + } + if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) { + if (test_qstr(obj, q)) { + if (match_str == NULL) { + match_str = d_str; + *match_len = d_len; + } else { + // search for longest common prefix of match_str and d_str + // (assumes these strings are null-terminated) + for (size_t j = s_len; j <= *match_len && j <= d_len; ++j) { + if (match_str[j] != d_str[j]) { + *match_len = j; + break; + } + } + } + if (*q_first == 0) { + *q_first = q; + } + *q_last = q; + } + } + } + return match_str; +} + +static void print_completions(const mp_print_t *print, + const char *s_start, size_t s_len, + mp_obj_t obj, qstr q_first, qstr q_last) { + + #define WORD_SLOT_LEN (16) + #define MAX_LINE_LEN (4 * WORD_SLOT_LEN) + + int line_len = MAX_LINE_LEN; // force a newline for first word + for (qstr q = q_first; q <= q_last; ++q) { + size_t d_len; + const char *d_str = (const char *)qstr_data(q, &d_len); + if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) { + if (test_qstr(obj, q)) { + int gap = (line_len + WORD_SLOT_LEN - 1) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len; + if (gap < 2) { + gap += WORD_SLOT_LEN; + } + if (line_len + gap + d_len <= MAX_LINE_LEN) { + // TODO optimise printing of gap? + for (int j = 0; j < gap; ++j) { + mp_print_str(print, " "); + } + mp_print_str(print, d_str); + line_len += gap + d_len; + } else { + mp_printf(print, "\n%s", d_str); + line_len = d_len; + } + } + } + } + mp_print_str(print, "\n"); +} + +size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print, const char **compl_str) { + // scan backwards to find start of "a.b.c" chain + const char *org_str = str; + const char *top = str + len; + for (const char *s = top; --s >= str;) { + if (!(unichar_isalpha(*s) || unichar_isdigit(*s) || *s == '_' || *s == '.')) { + ++s; + str = s; + break; + } + } + + // begin search in outer global dict which is accessed from __main__ + mp_obj_t obj = MP_OBJ_FROM_PTR(&mp_module___main__); + mp_obj_t dest[2]; + + const char *s_start; + size_t s_len; + + for (;;) { + // get next word in string to complete + s_start = str; + while (str < top && *str != '.') { + ++str; + } + s_len = str - s_start; + + if (str == top) { + // end of string, do completion on this partial name + break; + } + + // a complete word, lookup in current object + qstr q = qstr_find_strn(s_start, s_len); + if (q == MP_QSTRnull) { + // lookup will fail + return 0; + } + mp_load_method_protected(obj, q, dest, true); + obj = dest[0]; // attribute, method, or MP_OBJ_NULL if nothing found + + if (obj == MP_OBJ_NULL) { + // lookup failed + return 0; + } + + // skip '.' to move to next word + ++str; + } + + // after "import", suggest built-in modules + static const char import_str[] = "import "; + if (len >= 7 && !memcmp(org_str, import_str, 7)) { + obj = MP_OBJ_NULL; + } + + // look for matches + size_t match_len; + qstr q_first, q_last; + const char *match_str = + find_completions(s_start, s_len, obj, &match_len, &q_first, &q_last); + + // nothing found + if (q_first == 0) { + // If there're no better alternatives, and if it's first word + // in the line, try to complete "import". + if (s_start == org_str && s_len > 0 && s_len < sizeof(import_str) - 1) { + if (memcmp(s_start, import_str, s_len) == 0) { + *compl_str = import_str + s_len; + return sizeof(import_str) - 1 - s_len; + } + } + return 0; + } + + // 1 match found, or multiple matches with a common prefix + if (q_first == q_last || match_len > s_len) { + *compl_str = match_str + s_len; + return match_len - s_len; + } + + // multiple matches found, print them out + print_completions(print, s_start, s_len, obj, q_first, q_last); + + return (size_t)(-1); // indicate many matches +} + +#endif // MICROPY_HELPER_REPL diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/repl.h b/non_catalog_apps/mp_flipper/lib/micropython/py/repl.h new file mode 100644 index 00000000..9e8f7f1d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/repl.h @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_REPL_H +#define MICROPY_INCLUDED_PY_REPL_H + +#include "py/mpconfig.h" +#include "py/misc.h" +#include "py/mpprint.h" + +#if MICROPY_HELPER_REPL + +#if MICROPY_PY_SYS_PS1_PS2 + +const char *mp_repl_get_psx(unsigned int entry); + +static inline const char *mp_repl_get_ps1(void) { + return mp_repl_get_psx(MP_SYS_MUTABLE_PS1); +} + +static inline const char *mp_repl_get_ps2(void) { + return mp_repl_get_psx(MP_SYS_MUTABLE_PS2); +} + +#else + +static inline const char *mp_repl_get_ps1(void) { + return ">>> "; +} + +static inline const char *mp_repl_get_ps2(void) { + return "... "; +} + +#endif + +bool mp_repl_continue_with_input(const char *input); +size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print, const char **compl_str); + +#endif + +#endif // MICROPY_INCLUDED_PY_REPL_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/ringbuf.c b/non_catalog_apps/mp_flipper/lib/micropython/py/ringbuf.c new file mode 100644 index 00000000..10dca620 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/ringbuf.c @@ -0,0 +1,120 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jim Mussared + * + * 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 "ringbuf.h" + +int ringbuf_get16(ringbuf_t *r) { + int v = ringbuf_peek16(r); + if (v == -1) { + return v; + } + r->iget += 2; + if (r->iget >= r->size) { + r->iget -= r->size; + } + return v; +} + +int ringbuf_peek16(ringbuf_t *r) { + if (r->iget == r->iput) { + return -1; + } + uint32_t iget_a = r->iget + 1; + if (iget_a == r->size) { + iget_a = 0; + } + if (iget_a == r->iput) { + return -1; + } + return (r->buf[r->iget] << 8) | (r->buf[iget_a]); +} + +int ringbuf_put16(ringbuf_t *r, uint16_t v) { + uint32_t iput_a = r->iput + 1; + if (iput_a == r->size) { + iput_a = 0; + } + if (iput_a == r->iget) { + return -1; + } + uint32_t iput_b = iput_a + 1; + if (iput_b == r->size) { + iput_b = 0; + } + if (iput_b == r->iget) { + return -1; + } + r->buf[r->iput] = (v >> 8) & 0xff; + r->buf[iput_a] = v & 0xff; + r->iput = iput_b; + return 0; +} + +// Returns: +// 0: Success +// -1: Not enough data available to complete read (try again later) +// -2: Requested read is larger than buffer - will never succeed +int ringbuf_get_bytes(ringbuf_t *r, uint8_t *data, size_t data_len) { + if (ringbuf_avail(r) < data_len) { + return (r->size <= data_len) ? -2 : -1; + } + uint32_t iget = r->iget; + uint32_t iget_a = (iget + data_len) % r->size; + uint8_t *datap = data; + if (iget_a < iget) { + // Copy part of the data from the space left at the end of the buffer + memcpy(datap, r->buf + iget, r->size - iget); + datap += (r->size - iget); + iget = 0; + } + memcpy(datap, r->buf + iget, iget_a - iget); + r->iget = iget_a; + return 0; +} + +// Returns: +// 0: Success +// -1: Not enough free space available to complete write (try again later) +// -2: Requested write is larger than buffer - will never succeed +int ringbuf_put_bytes(ringbuf_t *r, const uint8_t *data, size_t data_len) { + if (ringbuf_free(r) < data_len) { + return (r->size <= data_len) ? -2 : -1; + } + uint32_t iput = r->iput; + uint32_t iput_a = (iput + data_len) % r->size; + const uint8_t *datap = data; + if (iput_a < iput) { + // Copy part of the data to the end of the buffer + memcpy(r->buf + iput, datap, r->size - iput); + datap += (r->size - iput); + iput = 0; + } + memcpy(r->buf + iput, datap, iput_a - iput); + r->iput = iput_a; + return 0; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/ringbuf.h b/non_catalog_apps/mp_flipper/lib/micropython/py/ringbuf.h new file mode 100644 index 00000000..c8508c07 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/ringbuf.h @@ -0,0 +1,102 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_RINGBUF_H +#define MICROPY_INCLUDED_PY_RINGBUF_H + +#include +#include + +#ifdef _MSC_VER +#include "py/mpconfig.h" // For inline. +#endif + +typedef struct _ringbuf_t { + uint8_t *buf; + uint16_t size; + uint16_t iget; + uint16_t iput; +} ringbuf_t; + +// Static initialization: +// byte buf_array[N]; +// ringbuf_t buf = {buf_array, sizeof(buf_array)}; + +// Dynamic initialization. This needs to become findable as a root pointer! +#define ringbuf_alloc(r, sz) \ + { \ + (r)->buf = m_new(uint8_t, sz); \ + (r)->size = sz; \ + (r)->iget = (r)->iput = 0; \ + } + +static inline int ringbuf_get(ringbuf_t *r) { + if (r->iget == r->iput) { + return -1; + } + uint8_t v = r->buf[r->iget++]; + if (r->iget >= r->size) { + r->iget = 0; + } + return v; +} + +static inline int ringbuf_peek(ringbuf_t *r) { + if (r->iget == r->iput) { + return -1; + } + return r->buf[r->iget]; +} + +static inline int ringbuf_put(ringbuf_t *r, uint8_t v) { + uint32_t iput_new = r->iput + 1; + if (iput_new >= r->size) { + iput_new = 0; + } + if (iput_new == r->iget) { + return -1; + } + r->buf[r->iput] = v; + r->iput = iput_new; + return 0; +} + +static inline size_t ringbuf_free(ringbuf_t *r) { + return (r->size + r->iget - r->iput - 1) % r->size; +} + +static inline size_t ringbuf_avail(ringbuf_t *r) { + return (r->size + r->iput - r->iget) % r->size; +} + +// Note: big-endian. No-op if not enough room available for both bytes. +int ringbuf_get16(ringbuf_t *r); +int ringbuf_peek16(ringbuf_t *r); +int ringbuf_put16(ringbuf_t *r, uint16_t v); + +int ringbuf_get_bytes(ringbuf_t *r, uint8_t *data, size_t data_len); +int ringbuf_put_bytes(ringbuf_t *r, const uint8_t *data, size_t data_len); + +#endif // MICROPY_INCLUDED_PY_RINGBUF_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/runtime.c b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime.c new file mode 100644 index 00000000..1836f5d9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime.c @@ -0,0 +1,1736 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2018 Paul Sokolovsky + * + * 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 "py/parsenum.h" +#include "py/compile.h" +#include "py/objstr.h" +#include "py/objtuple.h" +#include "py/objlist.h" +#include "py/objtype.h" +#include "py/objmodule.h" +#include "py/objgenerator.h" +#include "py/smallint.h" +#include "py/stream.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/stackctrl.h" +#include "py/gc.h" + +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#define DEBUG_printf DEBUG_printf +#define DEBUG_OP_printf(...) DEBUG_printf(__VA_ARGS__) +#else // don't print debugging info +#define DEBUG_printf(...) (void)0 +#define DEBUG_OP_printf(...) (void)0 +#endif + +const mp_obj_module_t mp_module___main__ = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&MP_STATE_VM(dict_main), +}; + +MP_REGISTER_MODULE(MP_QSTR___main__, mp_module___main__); + +#define TYPE_HAS_ITERNEXT(type) (type->flags & (MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_ITER_IS_CUSTOM | MP_TYPE_FLAG_ITER_IS_STREAM)) + +void mp_init(void) { + qstr_init(); + + // no pending exceptions to start with + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; + #if MICROPY_ENABLE_SCHEDULER + // no pending callbacks to start with + MP_STATE_VM(sched_state) = MP_SCHED_IDLE; + #if MICROPY_SCHEDULER_STATIC_NODES + if (MP_STATE_VM(sched_head) != NULL) { + // pending callbacks are on the list, eg from before a soft reset + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } + #endif + MP_STATE_VM(sched_idx) = 0; + MP_STATE_VM(sched_len) = 0; + #endif + + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + mp_init_emergency_exception_buf(); + #endif + + #if MICROPY_KBD_EXCEPTION + // initialise the exception object for raising KeyboardInterrupt + MP_STATE_VM(mp_kbd_exception).base.type = &mp_type_KeyboardInterrupt; + MP_STATE_VM(mp_kbd_exception).traceback_alloc = 0; + MP_STATE_VM(mp_kbd_exception).traceback_len = 0; + MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; + MP_STATE_VM(mp_kbd_exception).args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + #endif + + #if MICROPY_ENABLE_COMPILER + // optimization disabled by default + MP_STATE_VM(mp_optimise_value) = 0; + #if MICROPY_EMIT_NATIVE + MP_STATE_VM(default_emit_opt) = MP_EMIT_OPT_NONE; + #endif + #endif + + // init global module dict + mp_obj_dict_init(&MP_STATE_VM(mp_loaded_modules_dict), MICROPY_LOADED_MODULES_DICT_SIZE); + + // initialise the __main__ module + mp_obj_dict_init(&MP_STATE_VM(dict_main), 1); + mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(dict_main)), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); + + // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New()) + mp_locals_set(&MP_STATE_VM(dict_main)); + mp_globals_set(&MP_STATE_VM(dict_main)); + + #if MICROPY_CAN_OVERRIDE_BUILTINS + // start with no extensions to builtins + MP_STATE_VM(mp_module_builtins_override_dict) = NULL; + #endif + + #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE + MP_STATE_VM(track_reloc_code_list) = MP_OBJ_NULL; + #endif + + #if MICROPY_PY_OS_DUPTERM + for (size_t i = 0; i < MICROPY_PY_OS_DUPTERM; ++i) { + MP_STATE_VM(dupterm_objs[i]) = MP_OBJ_NULL; + } + #endif + + #if MICROPY_VFS + // initialise the VFS sub-system + MP_STATE_VM(vfs_cur) = NULL; + MP_STATE_VM(vfs_mount_table) = NULL; + #endif + + #if MICROPY_PY_SYS_PATH_ARGV_DEFAULTS + #if MICROPY_PY_SYS_PATH + mp_sys_path = mp_obj_new_list(0, NULL); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script) + #if MICROPY_MODULE_FROZEN + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__dot_frozen)); + #endif + #endif + #if MICROPY_PY_SYS_ARGV + mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0); + #endif + #endif // MICROPY_PY_SYS_PATH_ARGV_DEFAULTS + + #if MICROPY_PY_SYS_ATEXIT + MP_STATE_VM(sys_exitfunc) = mp_const_none; + #endif + + #if MICROPY_PY_SYS_PS1_PS2 + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = MP_OBJ_NEW_QSTR(MP_QSTR__gt__gt__gt__space_); + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = MP_OBJ_NEW_QSTR(MP_QSTR__dot__dot__dot__space_); + #endif + + #if MICROPY_PY_SYS_SETTRACE + MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL; + MP_STATE_THREAD(prof_callback_is_executing) = false; + MP_STATE_THREAD(current_code_state) = NULL; + #endif + + #if MICROPY_PY_SYS_TRACEBACKLIMIT + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT]) = MP_OBJ_NEW_SMALL_INT(1000); + #endif + + #if MICROPY_PY_BLUETOOTH + MP_STATE_VM(bluetooth) = MP_OBJ_NULL; + #endif + + #if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE + MP_STATE_VM(usbd) = MP_OBJ_NULL; + #endif + + #if MICROPY_PY_THREAD_GIL + mp_thread_mutex_init(&MP_STATE_VM(gil_mutex)); + #endif + + // call port specific initialization if any + #ifdef MICROPY_PORT_INIT_FUNC + MICROPY_PORT_INIT_FUNC; + #endif + + MP_THREAD_GIL_ENTER(); +} + +void mp_deinit(void) { + MP_THREAD_GIL_EXIT(); + + // call port specific deinitialization if any + #ifdef MICROPY_PORT_DEINIT_FUNC + MICROPY_PORT_DEINIT_FUNC; + #endif +} + +void mp_globals_locals_set_from_nlr_jump_callback(void *ctx_in) { + nlr_jump_callback_node_globals_locals_t *ctx = ctx_in; + mp_globals_set(ctx->globals); + mp_locals_set(ctx->locals); +} + +void mp_call_function_1_from_nlr_jump_callback(void *ctx_in) { + nlr_jump_callback_node_call_function_1_t *ctx = ctx_in; + ctx->func(ctx->arg); +} + +mp_obj_t MICROPY_WRAP_MP_LOAD_NAME(mp_load_name)(qstr qst) { + // logic: search locals, globals, builtins + DEBUG_OP_printf("load name %s\n", qstr_str(qst)); + // If we're at the outer scope (locals == globals), dispatch to load_global right away + if (mp_locals_get() != mp_globals_get()) { + mp_map_elem_t *elem = mp_map_lookup(&mp_locals_get()->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); + if (elem != NULL) { + return elem->value; + } + } + return mp_load_global(qst); +} + +mp_obj_t MICROPY_WRAP_MP_LOAD_GLOBAL(mp_load_global)(qstr qst) { + // logic: search globals, builtins + DEBUG_OP_printf("load global %s\n", qstr_str(qst)); + mp_map_elem_t *elem = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); + if (elem == NULL) { + #if MICROPY_CAN_OVERRIDE_BUILTINS + if (MP_STATE_VM(mp_module_builtins_override_dict) != NULL) { + // lookup in additional dynamic table of builtins first + elem = mp_map_lookup(&MP_STATE_VM(mp_module_builtins_override_dict)->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); + if (elem != NULL) { + return elem->value; + } + } + #endif + elem = mp_map_lookup((mp_map_t *)&mp_module_builtins_globals.map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); + if (elem == NULL) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_msg(&mp_type_NameError, MP_ERROR_TEXT("name not defined")); + #else + mp_raise_msg_varg(&mp_type_NameError, MP_ERROR_TEXT("name '%q' isn't defined"), qst); + #endif + } + } + return elem->value; +} + +mp_obj_t mp_load_build_class(void) { + DEBUG_OP_printf("load_build_class\n"); + #if MICROPY_CAN_OVERRIDE_BUILTINS + if (MP_STATE_VM(mp_module_builtins_override_dict) != NULL) { + // lookup in additional dynamic table of builtins first + mp_map_elem_t *elem = mp_map_lookup(&MP_STATE_VM(mp_module_builtins_override_dict)->map, MP_OBJ_NEW_QSTR(MP_QSTR___build_class__), MP_MAP_LOOKUP); + if (elem != NULL) { + return elem->value; + } + } + #endif + return MP_OBJ_FROM_PTR(&mp_builtin___build_class___obj); +} + +void mp_store_name(qstr qst, mp_obj_t obj) { + DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qst), obj); + mp_obj_dict_store(MP_OBJ_FROM_PTR(mp_locals_get()), MP_OBJ_NEW_QSTR(qst), obj); +} + +void mp_delete_name(qstr qst) { + DEBUG_OP_printf("delete name %s\n", qstr_str(qst)); + // TODO convert KeyError to NameError if qst not found + mp_obj_dict_delete(MP_OBJ_FROM_PTR(mp_locals_get()), MP_OBJ_NEW_QSTR(qst)); +} + +void mp_store_global(qstr qst, mp_obj_t obj) { + DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qst), obj); + mp_obj_dict_store(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(qst), obj); +} + +void mp_delete_global(qstr qst) { + DEBUG_OP_printf("delete global %s\n", qstr_str(qst)); + // TODO convert KeyError to NameError if qst not found + mp_obj_dict_delete(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(qst)); +} + +mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { + DEBUG_OP_printf("unary " UINT_FMT " %q %p\n", op, mp_unary_op_method_name[op], arg); + + if (op == MP_UNARY_OP_NOT) { + // "not x" is the negative of whether "x" is true per Python semantics + return mp_obj_new_bool(mp_obj_is_true(arg) == 0); + } else if (mp_obj_is_small_int(arg)) { + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(arg); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(val != 0); + case MP_UNARY_OP_HASH: + return arg; + case MP_UNARY_OP_POSITIVE: + case MP_UNARY_OP_INT_MAYBE: + return arg; + case MP_UNARY_OP_NEGATIVE: + // check for overflow + if (val == MP_SMALL_INT_MIN) { + return mp_obj_new_int(-val); + } else { + return MP_OBJ_NEW_SMALL_INT(-val); + } + case MP_UNARY_OP_ABS: + if (val >= 0) { + return arg; + } else if (val == MP_SMALL_INT_MIN) { + // check for overflow + return mp_obj_new_int(-val); + } else { + return MP_OBJ_NEW_SMALL_INT(-val); + } + default: + assert(op == MP_UNARY_OP_INVERT); + return MP_OBJ_NEW_SMALL_INT(~val); + } + } else if (op == MP_UNARY_OP_HASH && mp_obj_is_str_or_bytes(arg)) { + // fast path for hashing str/bytes + GET_STR_HASH(arg, h); + if (h == 0) { + GET_STR_DATA_LEN(arg, data, len); + h = qstr_compute_hash(data, len); + } + return MP_OBJ_NEW_SMALL_INT(h); + } else { + const mp_obj_type_t *type = mp_obj_get_type(arg); + if (MP_OBJ_TYPE_HAS_SLOT(type, unary_op)) { + mp_obj_t result = MP_OBJ_TYPE_GET_SLOT(type, unary_op)(op, arg); + if (result != MP_OBJ_NULL) { + return result; + } + } else if (op == MP_UNARY_OP_HASH) { + // Type doesn't have unary_op so use hash of object instance. + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)arg); + } + if (op == MP_UNARY_OP_BOOL) { + // Type doesn't have unary_op (or didn't handle MP_UNARY_OP_BOOL), + // so is implicitly True as this code path is impossible to reach + // if arg==mp_const_none. + return mp_const_true; + } + if (op == MP_UNARY_OP_INT_MAYBE + #if MICROPY_PY_BUILTINS_FLOAT + || op == MP_UNARY_OP_FLOAT_MAYBE + #if MICROPY_PY_BUILTINS_COMPLEX + || op == MP_UNARY_OP_COMPLEX_MAYBE + #endif + #endif + ) { + // These operators may return MP_OBJ_NULL if they are not supported by the type. + return MP_OBJ_NULL; + } + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("unsupported type for %q: '%s'"), + mp_unary_op_method_name[op], mp_obj_get_type_str(arg)); + #endif + } +} + +mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { + DEBUG_OP_printf("binary " UINT_FMT " %q %p %p\n", op, mp_binary_op_method_name[op], lhs, rhs); + + // TODO correctly distinguish inplace operators for mutable objects + // lookup logic that CPython uses for +=: + // check for implemented += + // then check for implemented + + // then check for implemented seq.inplace_concat + // then check for implemented seq.concat + // then fail + // note that list does not implement + or +=, so that inplace_concat is reached first for += + + // deal with is + if (op == MP_BINARY_OP_IS) { + return mp_obj_new_bool(lhs == rhs); + } + + // deal with == and != for all types + if (op == MP_BINARY_OP_EQUAL || op == MP_BINARY_OP_NOT_EQUAL) { + // mp_obj_equal_not_equal supports a bunch of shortcuts + return mp_obj_equal_not_equal(op, lhs, rhs); + } + + // deal with exception_match for all types + if (op == MP_BINARY_OP_EXCEPTION_MATCH) { + // rhs must be issubclass(rhs, BaseException) + if (mp_obj_is_exception_type(rhs)) { + if (mp_obj_exception_match(lhs, rhs)) { + return mp_const_true; + } else { + return mp_const_false; + } + } else if (mp_obj_is_type(rhs, &mp_type_tuple)) { + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(rhs); + for (size_t i = 0; i < tuple->len; i++) { + rhs = tuple->items[i]; + if (!mp_obj_is_exception_type(rhs)) { + goto unsupported_op; + } + if (mp_obj_exception_match(lhs, rhs)) { + return mp_const_true; + } + } + return mp_const_false; + } + goto unsupported_op; + } + + if (mp_obj_is_small_int(lhs)) { + mp_int_t lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs); + if (mp_obj_is_small_int(rhs)) { + mp_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs); + // This is a binary operation: lhs_val op rhs_val + // We need to be careful to handle overflow; see CERT INT32-C + // Operations that can overflow: + // + result always fits in mp_int_t, then handled by SMALL_INT check + // - result always fits in mp_int_t, then handled by SMALL_INT check + // * checked explicitly + // / if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check + // % if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check + // << checked explicitly + switch (op) { + case MP_BINARY_OP_OR: + case MP_BINARY_OP_INPLACE_OR: + lhs_val |= rhs_val; + break; + case MP_BINARY_OP_XOR: + case MP_BINARY_OP_INPLACE_XOR: + lhs_val ^= rhs_val; + break; + case MP_BINARY_OP_AND: + case MP_BINARY_OP_INPLACE_AND: + lhs_val &= rhs_val; + break; + case MP_BINARY_OP_LSHIFT: + case MP_BINARY_OP_INPLACE_LSHIFT: { + if (rhs_val < 0) { + // negative shift not allowed + mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); + } else if (rhs_val >= (mp_int_t)(sizeof(lhs_val) * MP_BITS_PER_BYTE) + || lhs_val > (MP_SMALL_INT_MAX >> rhs_val) + || lhs_val < (MP_SMALL_INT_MIN >> rhs_val)) { + // left-shift will overflow, so use higher precision integer + lhs = mp_obj_new_int_from_ll(lhs_val); + goto generic_binary_op; + } else { + // use standard precision + lhs_val = (mp_uint_t)lhs_val << rhs_val; + } + break; + } + case MP_BINARY_OP_RSHIFT: + case MP_BINARY_OP_INPLACE_RSHIFT: + if (rhs_val < 0) { + // negative shift not allowed + mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); + } else { + // standard precision is enough for right-shift + if (rhs_val >= (mp_int_t)(sizeof(lhs_val) * MP_BITS_PER_BYTE)) { + // Shifting to big amounts is undefined behavior + // in C and is CPU-dependent; propagate sign bit. + rhs_val = sizeof(lhs_val) * MP_BITS_PER_BYTE - 1; + } + lhs_val >>= rhs_val; + } + break; + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_INPLACE_ADD: + lhs_val += rhs_val; + break; + case MP_BINARY_OP_SUBTRACT: + case MP_BINARY_OP_INPLACE_SUBTRACT: + lhs_val -= rhs_val; + break; + case MP_BINARY_OP_MULTIPLY: + case MP_BINARY_OP_INPLACE_MULTIPLY: { + + // If long long type exists and is larger than mp_int_t, then + // we can use the following code to perform overflow-checked multiplication. + // Otherwise (eg in x64 case) we must use mp_small_int_mul_overflow. + #if 0 + // compute result using long long precision + long long res = (long long)lhs_val * (long long)rhs_val; + if (res > MP_SMALL_INT_MAX || res < MP_SMALL_INT_MIN) { + // result overflowed SMALL_INT, so return higher precision integer + return mp_obj_new_int_from_ll(res); + } else { + // use standard precision + lhs_val = (mp_int_t)res; + } + #endif + + if (mp_small_int_mul_overflow(lhs_val, rhs_val)) { + // use higher precision + lhs = mp_obj_new_int_from_ll(lhs_val); + goto generic_binary_op; + } else { + // use standard precision + return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val); + } + } + case MP_BINARY_OP_FLOOR_DIVIDE: + case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: + if (rhs_val == 0) { + goto zero_division; + } + lhs_val = mp_small_int_floor_divide(lhs_val, rhs_val); + break; + + #if MICROPY_PY_BUILTINS_FLOAT + case MP_BINARY_OP_TRUE_DIVIDE: + case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: + if (rhs_val == 0) { + goto zero_division; + } + return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val); + #endif + + case MP_BINARY_OP_MODULO: + case MP_BINARY_OP_INPLACE_MODULO: { + if (rhs_val == 0) { + goto zero_division; + } + lhs_val = mp_small_int_modulo(lhs_val, rhs_val); + break; + } + + case MP_BINARY_OP_POWER: + case MP_BINARY_OP_INPLACE_POWER: + if (rhs_val < 0) { + #if MICROPY_PY_BUILTINS_FLOAT + return mp_obj_float_binary_op(op, (mp_float_t)lhs_val, rhs); + #else + mp_raise_ValueError(MP_ERROR_TEXT("negative power with no float support")); + #endif + } else { + mp_int_t ans = 1; + while (rhs_val > 0) { + if (rhs_val & 1) { + if (mp_small_int_mul_overflow(ans, lhs_val)) { + goto power_overflow; + } + ans *= lhs_val; + } + if (rhs_val == 1) { + break; + } + rhs_val /= 2; + if (mp_small_int_mul_overflow(lhs_val, lhs_val)) { + goto power_overflow; + } + lhs_val *= lhs_val; + } + lhs_val = ans; + } + break; + + power_overflow: + // use higher precision + lhs = mp_obj_new_int_from_ll(MP_OBJ_SMALL_INT_VALUE(lhs)); + goto generic_binary_op; + + case MP_BINARY_OP_DIVMOD: { + if (rhs_val == 0) { + goto zero_division; + } + // to reduce stack usage we don't pass a temp array of the 2 items + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + tuple->items[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(lhs_val, rhs_val)); + tuple->items[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(lhs_val, rhs_val)); + return MP_OBJ_FROM_PTR(tuple); + } + + case MP_BINARY_OP_LESS: + return mp_obj_new_bool(lhs_val < rhs_val); + case MP_BINARY_OP_MORE: + return mp_obj_new_bool(lhs_val > rhs_val); + case MP_BINARY_OP_LESS_EQUAL: + return mp_obj_new_bool(lhs_val <= rhs_val); + case MP_BINARY_OP_MORE_EQUAL: + return mp_obj_new_bool(lhs_val >= rhs_val); + + default: + goto unsupported_op; + } + // This is an inlined version of mp_obj_new_int, for speed + if (MP_SMALL_INT_FITS(lhs_val)) { + return MP_OBJ_NEW_SMALL_INT(lhs_val); + } else { + return mp_obj_new_int_from_ll(lhs_val); + } + #if MICROPY_PY_BUILTINS_FLOAT + } else if (mp_obj_is_float(rhs)) { + mp_obj_t res = mp_obj_float_binary_op(op, (mp_float_t)lhs_val, rhs); + if (res == MP_OBJ_NULL) { + goto unsupported_op; + } else { + return res; + } + #endif + #if MICROPY_PY_BUILTINS_COMPLEX + } else if (mp_obj_is_type(rhs, &mp_type_complex)) { + mp_obj_t res = mp_obj_complex_binary_op(op, (mp_float_t)lhs_val, 0, rhs); + if (res == MP_OBJ_NULL) { + goto unsupported_op; + } else { + return res; + } + #endif + } + } + + // Convert MP_BINARY_OP_IN to MP_BINARY_OP_CONTAINS with swapped args. + if (op == MP_BINARY_OP_IN) { + op = MP_BINARY_OP_CONTAINS; + mp_obj_t temp = lhs; + lhs = rhs; + rhs = temp; + } + + // generic binary_op supplied by type + const mp_obj_type_t *type; +generic_binary_op: + type = mp_obj_get_type(lhs); + if (MP_OBJ_TYPE_HAS_SLOT(type, binary_op)) { + mp_obj_t result = MP_OBJ_TYPE_GET_SLOT(type, binary_op)(op, lhs, rhs); + if (result != MP_OBJ_NULL) { + return result; + } + } + + // If this was an inplace method, fallback to the corresponding normal method. + // https://docs.python.org/3/reference/datamodel.html#object.__iadd__ : + // "If a specific method is not defined, the augmented assignment falls back + // to the normal methods." + if (op >= MP_BINARY_OP_INPLACE_OR && op <= MP_BINARY_OP_INPLACE_POWER) { + op += MP_BINARY_OP_OR - MP_BINARY_OP_INPLACE_OR; + goto generic_binary_op; + } + + #if MICROPY_PY_REVERSE_SPECIAL_METHODS + if (op >= MP_BINARY_OP_OR && op <= MP_BINARY_OP_POWER) { + mp_obj_t t = rhs; + rhs = lhs; + lhs = t; + op += MP_BINARY_OP_REVERSE_OR - MP_BINARY_OP_OR; + goto generic_binary_op; + } else if (op >= MP_BINARY_OP_REVERSE_OR) { + // Convert __rop__ back to __op__ for error message + mp_obj_t t = rhs; + rhs = lhs; + lhs = t; + op -= MP_BINARY_OP_REVERSE_OR - MP_BINARY_OP_OR; + } + #endif + + if (op == MP_BINARY_OP_CONTAINS) { + // If type didn't support containment then explicitly walk the iterator. + // mp_getiter will raise the appropriate exception if lhs is not iterable. + mp_obj_iter_buf_t iter_buf; + mp_obj_t iter = mp_getiter(lhs, &iter_buf); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + if (mp_obj_equal(next, rhs)) { + return mp_const_true; + } + } + return mp_const_false; + } + +unsupported_op: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("unsupported types for %q: '%s', '%s'"), + mp_binary_op_method_name[op], mp_obj_get_type_str(lhs), mp_obj_get_type_str(rhs)); + #endif + +zero_division: + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); +} + +mp_obj_t mp_call_function_0(mp_obj_t fun) { + return mp_call_function_n_kw(fun, 0, 0, NULL); +} + +mp_obj_t mp_call_function_1(mp_obj_t fun, mp_obj_t arg) { + return mp_call_function_n_kw(fun, 1, 0, &arg); +} + +mp_obj_t mp_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) { + mp_obj_t args[2]; + args[0] = arg1; + args[1] = arg2; + return mp_call_function_n_kw(fun, 2, 0, args); +} + +// args contains, eg: arg0 arg1 key0 value0 key1 value1 +mp_obj_t mp_call_function_n_kw(mp_obj_t fun_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // TODO improve this: fun object can specify its type and we parse here the arguments, + // passing to the function arrays of fixed and keyword arguments + + DEBUG_OP_printf("calling function %p(n_args=" UINT_FMT ", n_kw=" UINT_FMT ", args=%p)\n", fun_in, n_args, n_kw, args); + + // get the type + const mp_obj_type_t *type = mp_obj_get_type(fun_in); + + // do the call + if (MP_OBJ_TYPE_HAS_SLOT(type, call)) { + return MP_OBJ_TYPE_GET_SLOT(type, call)(fun_in, n_args, n_kw, args); + } + + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object not callable")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object isn't callable"), mp_obj_get_type_str(fun_in)); + #endif +} + +// args contains: fun self/NULL arg(0) ... arg(n_args-2) arg(n_args-1) kw_key(0) kw_val(0) ... kw_key(n_kw-1) kw_val(n_kw-1) +// if n_args==0 and n_kw==0 then there are only fun and self/NULL +mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args) { + DEBUG_OP_printf("call method (fun=%p, self=%p, n_args=" UINT_FMT ", n_kw=" UINT_FMT ", args=%p)\n", args[0], args[1], n_args, n_kw, args); + int adjust = (args[1] == MP_OBJ_NULL) ? 0 : 1; + return mp_call_function_n_kw(args[0], n_args + adjust, n_kw, args + 2 - adjust); +} + +// This function only needs to be exposed externally when in stackless mode. +#if !MICROPY_STACKLESS +static +#endif +void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args, mp_call_args_t *out_args) { + mp_obj_t fun = *args++; + mp_obj_t self = MP_OBJ_NULL; + if (have_self) { + self = *args++; // may be MP_OBJ_NULL + } + size_t n_args = n_args_n_kw & 0xff; + size_t n_kw = (n_args_n_kw >> 8) & 0xff; + mp_uint_t star_args = MP_OBJ_SMALL_INT_VALUE(args[n_args + 2 * n_kw]); + + DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, map=%u)\n", fun, self, n_args, n_kw, args, star_args); + + // We need to create the following array of objects: + // args[0 .. n_args] unpacked(pos_seq) args[n_args .. n_args + 2 * n_kw] unpacked(kw_dict) + // TODO: optimize one day to avoid constructing new arg array? Will be hard. + + // The new args array + mp_obj_t *args2; + size_t args2_alloc; + size_t args2_len = 0; + + // Try to get a hint for unpacked * args length + ssize_t list_len = 0; + + if (star_args != 0) { + for (size_t i = 0; i < n_args; i++) { + if ((star_args >> i) & 1) { + mp_obj_t len = mp_obj_len_maybe(args[i]); + if (len != MP_OBJ_NULL) { + // -1 accounts for 1 of n_args occupied by this arg + list_len += mp_obj_get_int(len) - 1; + } + } + } + } + + // Try to get a hint for the size of the kw_dict + ssize_t kw_dict_len = 0; + + for (size_t i = 0; i < n_kw; i++) { + mp_obj_t key = args[n_args + i * 2]; + mp_obj_t value = args[n_args + i * 2 + 1]; + if (key == MP_OBJ_NULL && value != MP_OBJ_NULL && mp_obj_is_type(value, &mp_type_dict)) { + // -1 accounts for 1 of n_kw occupied by this arg + kw_dict_len += mp_obj_dict_len(value) - 1; + } + } + + // Extract the pos_seq sequence to the new args array. + // Note that it can be arbitrary iterator. + if (star_args == 0) { + // no star args to unpack + + // allocate memory for the new array of args + args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len); + args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); + + // copy the self + if (self != MP_OBJ_NULL) { + args2[args2_len++] = self; + } + + // copy the fixed pos args + mp_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); + args2_len += n_args; + } else { + // at least one star arg to unpack + + // allocate memory for the new array of args + args2_alloc = 1 + n_args + list_len + 2 * (n_kw + kw_dict_len); + args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); + + // copy the self + if (self != MP_OBJ_NULL) { + args2[args2_len++] = self; + } + + for (size_t i = 0; i < n_args; i++) { + mp_obj_t arg = args[i]; + if ((star_args >> i) & 1) { + // star arg + if (mp_obj_is_type(arg, &mp_type_tuple) || mp_obj_is_type(arg, &mp_type_list)) { + // optimise the case of a tuple and list + + // get the items + size_t len; + mp_obj_t *items; + mp_obj_get_array(arg, &len, &items); + + // copy the items + assert(args2_len + len <= args2_alloc); + mp_seq_copy(args2 + args2_len, items, len, mp_obj_t); + args2_len += len; + } else { + // generic iterator + + // extract the variable position args from the iterator + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(arg, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (args2_len + (n_args - i) >= args2_alloc) { + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), + args2_alloc * 2 * sizeof(mp_obj_t)); + args2_alloc *= 2; + } + args2[args2_len++] = item; + } + } + } else { + // normal argument + assert(args2_len < args2_alloc); + args2[args2_len++] = arg; + } + } + } + + // The size of the args2 array now is the number of positional args. + size_t pos_args_len = args2_len; + + // ensure there is still enough room for kw args + if (args2_len + 2 * (n_kw + kw_dict_len) > args2_alloc) { + size_t new_alloc = args2_len + 2 * (n_kw + kw_dict_len); + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), + new_alloc * sizeof(mp_obj_t)); + args2_alloc = new_alloc; + } + + // Copy the kw args. + for (size_t i = 0; i < n_kw; i++) { + mp_obj_t kw_key = args[n_args + i * 2]; + mp_obj_t kw_value = args[n_args + i * 2 + 1]; + if (kw_key == MP_OBJ_NULL) { + // double-star args + if (mp_obj_is_type(kw_value, &mp_type_dict)) { + // dictionary + mp_map_t *map = mp_obj_dict_get_map(kw_value); + // should have enough, since kw_dict_len is in this case hinted correctly above + assert(args2_len + 2 * map->used <= args2_alloc); + for (size_t j = 0; j < map->alloc; j++) { + if (mp_map_slot_is_filled(map, j)) { + // the key must be a qstr, so intern it if it's a string + mp_obj_t key = map->table[j].key; + if (!mp_obj_is_qstr(key)) { + key = mp_obj_str_intern_checked(key); + } + args2[args2_len++] = key; + args2[args2_len++] = map->table[j].value; + } + } + } else { + // generic mapping: + // - call keys() to get an iterable of all keys in the mapping + // - call __getitem__ for each key to get the corresponding value + + // get the keys iterable + mp_obj_t dest[3]; + mp_load_method(kw_value, MP_QSTR_keys, dest); + mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest), NULL); + + mp_obj_t key; + while ((key = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + // expand size of args array if needed + if (args2_len + 1 >= args2_alloc) { + size_t new_alloc = args2_alloc * 2; + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); + args2_alloc = new_alloc; + } + + // the key must be a qstr, so intern it if it's a string + if (!mp_obj_is_qstr(key)) { + key = mp_obj_str_intern_checked(key); + } + + // get the value corresponding to the key + mp_load_method(kw_value, MP_QSTR___getitem__, dest); + dest[2] = key; + mp_obj_t value = mp_call_method_n_kw(1, 0, dest); + + // store the key/value pair in the argument array + args2[args2_len++] = key; + args2[args2_len++] = value; + } + } + } else { + // normal kwarg + assert(args2_len + 2 <= args2_alloc); + args2[args2_len++] = kw_key; + args2[args2_len++] = kw_value; + } + } + + out_args->fun = fun; + out_args->args = args2; + out_args->n_args = pos_args_len; + out_args->n_kw = (args2_len - pos_args_len) / 2; + out_args->n_alloc = args2_alloc; +} + +mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args) { + mp_call_args_t out_args; + mp_call_prepare_args_n_kw_var(have_self, n_args_n_kw, args, &out_args); + + mp_obj_t res = mp_call_function_n_kw(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args); + mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); + + return res; +} + +// unpacked items are stored in reverse order into the array pointed to by items +void mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) { + size_t seq_len; + if (mp_obj_is_type(seq_in, &mp_type_tuple) || mp_obj_is_type(seq_in, &mp_type_list)) { + mp_obj_t *seq_items; + mp_obj_get_array(seq_in, &seq_len, &seq_items); + if (seq_len < num) { + goto too_short; + } else if (seq_len > num) { + goto too_long; + } + for (size_t i = 0; i < num; i++) { + items[i] = seq_items[num - 1 - i]; + } + } else { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(seq_in, &iter_buf); + + for (seq_len = 0; seq_len < num; seq_len++) { + mp_obj_t el = mp_iternext(iterable); + if (el == MP_OBJ_STOP_ITERATION) { + goto too_short; + } + items[num - 1 - seq_len] = el; + } + if (mp_iternext(iterable) != MP_OBJ_STOP_ITERATION) { + goto too_long; + } + } + return; + +too_short: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); + #else + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("need more than %d values to unpack"), (int)seq_len); + #endif +too_long: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); + #else + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("too many values to unpack (expected %d)"), (int)num); + #endif +} + +// unpacked items are stored in reverse order into the array pointed to by items +void mp_unpack_ex(mp_obj_t seq_in, size_t num_in, mp_obj_t *items) { + size_t num_left = num_in & 0xff; + size_t num_right = (num_in >> 8) & 0xff; + DEBUG_OP_printf("unpack ex " UINT_FMT " " UINT_FMT "\n", num_left, num_right); + size_t seq_len; + if (mp_obj_is_type(seq_in, &mp_type_tuple) || mp_obj_is_type(seq_in, &mp_type_list)) { + // Make the seq variable volatile so the compiler keeps a reference to it, + // since if it's a tuple then seq_items points to the interior of the GC cell + // and mp_obj_new_list may trigger a GC which doesn't trace this and reclaims seq. + volatile mp_obj_t seq = seq_in; + mp_obj_t *seq_items; + mp_obj_get_array(seq, &seq_len, &seq_items); + if (seq_len < num_left + num_right) { + goto too_short; + } + for (size_t i = 0; i < num_right; i++) { + items[i] = seq_items[seq_len - 1 - i]; + } + items[num_right] = mp_obj_new_list(seq_len - num_left - num_right, seq_items + num_left); + for (size_t i = 0; i < num_left; i++) { + items[num_right + 1 + i] = seq_items[num_left - 1 - i]; + } + seq = MP_OBJ_NULL; + } else { + // Generic iterable; this gets a bit messy: we unpack known left length to the + // items destination array, then the rest to a dynamically created list. Once the + // iterable is exhausted, we take from this list for the right part of the items. + // TODO Improve to waste less memory in the dynamically created list. + mp_obj_t iterable = mp_getiter(seq_in, NULL); + mp_obj_t item; + for (seq_len = 0; seq_len < num_left; seq_len++) { + item = mp_iternext(iterable); + if (item == MP_OBJ_STOP_ITERATION) { + goto too_short; + } + items[num_left + num_right + 1 - 1 - seq_len] = item; + } + mp_obj_list_t *rest = MP_OBJ_TO_PTR(mp_obj_new_list(0, NULL)); + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + mp_obj_list_append(MP_OBJ_FROM_PTR(rest), item); + } + if (rest->len < num_right) { + goto too_short; + } + items[num_right] = MP_OBJ_FROM_PTR(rest); + for (size_t i = 0; i < num_right; i++) { + items[num_right - 1 - i] = rest->items[rest->len - num_right + i]; + } + mp_obj_list_set_len(MP_OBJ_FROM_PTR(rest), rest->len - num_right); + } + return; + +too_short: + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); + #else + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("need more than %d values to unpack"), (int)seq_len); + #endif +} + +mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) { + DEBUG_OP_printf("load attr %p.%s\n", base, qstr_str(attr)); + // use load_method + mp_obj_t dest[2]; + mp_load_method(base, attr, dest); + if (dest[1] == MP_OBJ_NULL) { + // load_method returned just a normal attribute + return dest[0]; + } else { + // load_method returned a method, so build a bound method object + return mp_obj_new_bound_meth(dest[0], dest[1]); + } +} + +#if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG + +// The following "checked fun" type is local to the mp_convert_member_lookup +// function, and serves to check that the first argument to a builtin function +// has the correct type. + +typedef struct _mp_obj_checked_fun_t { + mp_obj_base_t base; + const mp_obj_type_t *type; + mp_obj_t fun; +} mp_obj_checked_fun_t; + +static mp_obj_t checked_fun_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_obj_checked_fun_t *self = MP_OBJ_TO_PTR(self_in); + if (n_args > 0) { + const mp_obj_type_t *arg0_type = mp_obj_get_type(args[0]); + if (arg0_type != self->type) { + #if MICROPY_ERROR_REPORTING != MICROPY_ERROR_REPORTING_DETAILED + mp_raise_TypeError(MP_ERROR_TEXT("argument has wrong type")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("argument should be a '%q' not a '%q'"), self->type->name, arg0_type->name); + #endif + } + } + return mp_call_function_n_kw(self->fun, n_args, n_kw, args); +} + +static MP_DEFINE_CONST_OBJ_TYPE( + mp_type_checked_fun, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + call, checked_fun_call + ); + +static mp_obj_t mp_obj_new_checked_fun(const mp_obj_type_t *type, mp_obj_t fun) { + mp_obj_checked_fun_t *o = mp_obj_malloc(mp_obj_checked_fun_t, &mp_type_checked_fun); + o->type = type; + o->fun = fun; + return MP_OBJ_FROM_PTR(o); +} + +#endif // MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG + +// Given a member that was extracted from an instance, convert it correctly +// and put the result in the dest[] array for a possible method call. +// Conversion means dealing with static/class methods, callables, and values. +// see http://docs.python.org/3/howto/descriptor.html +// and also https://mail.python.org/pipermail/python-dev/2015-March/138950.html +void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest) { + if (mp_obj_is_obj(member)) { + const mp_obj_type_t *m_type = ((mp_obj_base_t *)MP_OBJ_TO_PTR(member))->type; + if (m_type->flags & MP_TYPE_FLAG_BINDS_SELF) { + // `member` is a function that binds self as its first argument. + if (m_type->flags & MP_TYPE_FLAG_BUILTIN_FUN) { + // `member` is a built-in function, which has special behaviour. + if (mp_obj_is_instance_type(type)) { + // Built-in functions on user types always behave like a staticmethod. + dest[0] = member; + } + #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG + else if (self == MP_OBJ_NULL && type != &mp_type_object) { + // `member` is a built-in method without a first argument, so wrap + // it in a type checker that will check self when it's supplied. + // Note that object will do its own checking so shouldn't be wrapped. + dest[0] = mp_obj_new_checked_fun(type, member); + } + #endif + else { + // Return a (built-in) bound method, with self being this object. + dest[0] = member; + dest[1] = self; + } + } else { + // Return a bound method, with self being this object. + dest[0] = member; + dest[1] = self; + } + } else if (m_type == &mp_type_staticmethod) { + // `member` is a staticmethod, return the function that it wraps. + dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; + } else if (m_type == &mp_type_classmethod) { + // `member` is a classmethod, return a bound method with self being the type of + // this object. This type should be the type of the original instance, not the + // base type (which is what is passed in the `type` argument to this function). + if (self != MP_OBJ_NULL) { + type = mp_obj_get_type(self); + } + dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; + dest[1] = MP_OBJ_FROM_PTR(type); + } else { + // `member` is a value, so just return that value. + dest[0] = member; + } + } else { + // `member` is a value, so just return that value. + dest[0] = member; + } +} + +// no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL +// normal attribute found, returns: dest[0] == , dest[1] == MP_OBJ_NULL +// method attribute found, returns: dest[0] == , dest[1] == +void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) { + // clear output to indicate no attribute/method found yet + dest[0] = MP_OBJ_NULL; + dest[1] = MP_OBJ_NULL; + + // Note: the specific case of obj being an instance type is fast-path'ed in the VM + // for the MP_BC_LOAD_ATTR opcode. Instance types handle type->attr and look up directly + // in their member's map. + + // get the type + const mp_obj_type_t *type = mp_obj_get_type(obj); + + // look for built-in names + #if MICROPY_CPYTHON_COMPAT + if (attr == MP_QSTR___class__) { + // a.__class__ is equivalent to type(a) + dest[0] = MP_OBJ_FROM_PTR(type); + return; + } + #endif + + if (attr == MP_QSTR___next__ && TYPE_HAS_ITERNEXT(type)) { + dest[0] = MP_OBJ_FROM_PTR(&mp_builtin_next_obj); + dest[1] = obj; + return; + } + if (MP_OBJ_TYPE_HAS_SLOT(type, attr)) { + // this type can do its own load, so call it + MP_OBJ_TYPE_GET_SLOT(type, attr)(obj, attr, dest); + // If type->attr has set dest[1] = MP_OBJ_SENTINEL, we should proceed + // with lookups below (i.e. in locals_dict). If not, return right away. + if (dest[1] != MP_OBJ_SENTINEL) { + return; + } + // Clear the fail flag set by type->attr so it's like it never ran. + dest[1] = MP_OBJ_NULL; + } + if (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { + // generic method lookup + // this is a lookup in the object (ie not class or type) + assert(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->base.type == &mp_type_dict); // MicroPython restriction, for now + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + if (elem != NULL) { + mp_convert_member_lookup(obj, type, elem->value, dest); + } + return; + } +} + +void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { + DEBUG_OP_printf("load method %p.%s\n", base, qstr_str(attr)); + + mp_load_method_maybe(base, attr, dest); + + if (dest[0] == MP_OBJ_NULL) { + // no attribute/method called attr + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("no such attribute")); + #else + // following CPython, we give a more detailed error message for type objects + if (mp_obj_is_type(base, &mp_type_type)) { + mp_raise_msg_varg(&mp_type_AttributeError, + MP_ERROR_TEXT("type object '%q' has no attribute '%q'"), + ((mp_obj_type_t *)MP_OBJ_TO_PTR(base))->name, attr); + } else { + mp_raise_msg_varg(&mp_type_AttributeError, + MP_ERROR_TEXT("'%s' object has no attribute '%q'"), + mp_obj_get_type_str(base), attr); + } + #endif + } +} + +// Acts like mp_load_method_maybe but catches AttributeError, and all other exceptions if requested +void mp_load_method_protected(mp_obj_t obj, qstr attr, mp_obj_t *dest, bool catch_all_exc) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_load_method_maybe(obj, attr, dest); + nlr_pop(); + } else { + if (!catch_all_exc + && !mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), + MP_OBJ_FROM_PTR(&mp_type_AttributeError))) { + // Re-raise the exception + nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val)); + } + } +} + +void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) { + DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value); + const mp_obj_type_t *type = mp_obj_get_type(base); + if (MP_OBJ_TYPE_HAS_SLOT(type, attr)) { + mp_obj_t dest[2] = {MP_OBJ_SENTINEL, value}; + MP_OBJ_TYPE_GET_SLOT(type, attr)(base, attr, dest); + if (dest[0] == MP_OBJ_NULL) { + // success + return; + } + } + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("no such attribute")); + #else + mp_raise_msg_varg(&mp_type_AttributeError, + MP_ERROR_TEXT("'%s' object has no attribute '%q'"), + mp_obj_get_type_str(base), attr); + #endif +} + +mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { + assert(o_in); + const mp_obj_type_t *type = mp_obj_get_type(o_in); + + // Most types that use iternext just use the identity getiter. We handle this case explicitly + // so we don't unnecessarily allocate any RAM for the iter_buf, which won't be used. + if ((type->flags & MP_TYPE_FLAG_ITER_IS_ITERNEXT) == MP_TYPE_FLAG_ITER_IS_ITERNEXT || (type->flags & MP_TYPE_FLAG_ITER_IS_STREAM) == MP_TYPE_FLAG_ITER_IS_STREAM) { + return o_in; + } + + if (MP_OBJ_TYPE_HAS_SLOT(type, iter)) { + // check for native getiter (corresponds to __iter__) + if (iter_buf == NULL && MP_OBJ_TYPE_GET_SLOT(type, iter) != mp_obj_instance_getiter) { + // if caller did not provide a buffer then allocate one on the heap + // mp_obj_instance_getiter is special, it will allocate only if needed + iter_buf = m_new_obj(mp_obj_iter_buf_t); + } + mp_getiter_fun_t getiter; + if (type->flags & MP_TYPE_FLAG_ITER_IS_CUSTOM) { + getiter = ((mp_getiter_iternext_custom_t *)MP_OBJ_TYPE_GET_SLOT(type, iter))->getiter; + } else { + getiter = (mp_getiter_fun_t)MP_OBJ_TYPE_GET_SLOT(type, iter); + } + mp_obj_t iter = getiter(o_in, iter_buf); + if (iter != MP_OBJ_NULL) { + return iter; + } + } + + // check for __getitem__ + mp_obj_t dest[2]; + mp_load_method_maybe(o_in, MP_QSTR___getitem__, dest); + if (dest[0] != MP_OBJ_NULL) { + // __getitem__ exists, create and return an iterator + if (iter_buf == NULL) { + // if caller did not provide a buffer then allocate one on the heap + iter_buf = m_new_obj(mp_obj_iter_buf_t); + } + return mp_obj_new_getitem_iter(dest, iter_buf); + } + + // object not iterable + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object not iterable")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object isn't iterable"), mp_obj_get_type_str(o_in)); + #endif + +} + +static mp_fun_1_t type_get_iternext(const mp_obj_type_t *type) { + if ((type->flags & MP_TYPE_FLAG_ITER_IS_STREAM) == MP_TYPE_FLAG_ITER_IS_STREAM) { + return mp_stream_unbuffered_iter; + } else if (type->flags & MP_TYPE_FLAG_ITER_IS_ITERNEXT) { + return (mp_fun_1_t)MP_OBJ_TYPE_GET_SLOT(type, iter); + } else if (type->flags & MP_TYPE_FLAG_ITER_IS_CUSTOM) { + return ((mp_getiter_iternext_custom_t *)MP_OBJ_TYPE_GET_SLOT(type, iter))->iternext; + } else { + return NULL; + } +} + +// may return MP_OBJ_STOP_ITERATION as an optimisation instead of raise StopIteration() +// may also raise StopIteration() +mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) { + const mp_obj_type_t *type = mp_obj_get_type(o_in); + if (TYPE_HAS_ITERNEXT(type)) { + MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; + return type_get_iternext(type)(o_in); + } else { + // check for __next__ method + mp_obj_t dest[2]; + mp_load_method_maybe(o_in, MP_QSTR___next__, dest); + if (dest[0] != MP_OBJ_NULL) { + // __next__ exists, call it and return its result + return mp_call_method_n_kw(0, 0, dest); + } else { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object not an iterator")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object isn't an iterator"), mp_obj_get_type_str(o_in)); + #endif + } + } +} + +// will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration() (or any subclass thereof) +// may raise other exceptions +mp_obj_t mp_iternext(mp_obj_t o_in) { + MP_STACK_CHECK(); // enumerate, filter, map and zip can recursively call mp_iternext + const mp_obj_type_t *type = mp_obj_get_type(o_in); + if (TYPE_HAS_ITERNEXT(type)) { + MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; + return type_get_iternext(type)(o_in); + } else { + // check for __next__ method + mp_obj_t dest[2]; + mp_load_method_maybe(o_in, MP_QSTR___next__, dest); + if (dest[0] != MP_OBJ_NULL) { + // __next__ exists, call it and return its result + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t ret = mp_call_method_n_kw(0, 0, dest); + nlr_pop(); + return ret; + } else { + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { + return mp_make_stop_iteration(mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val))); + } else { + nlr_jump(nlr.ret_val); + } + } + } else { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + mp_raise_TypeError(MP_ERROR_TEXT("object not an iterator")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("'%s' object isn't an iterator"), mp_obj_get_type_str(o_in)); + #endif + } + } +} + +mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) { + assert((send_value != MP_OBJ_NULL) ^ (throw_value != MP_OBJ_NULL)); + const mp_obj_type_t *type = mp_obj_get_type(self_in); + + if (type == &mp_type_gen_instance) { + return mp_obj_gen_resume(self_in, send_value, throw_value, ret_val); + } + + if (TYPE_HAS_ITERNEXT(type) && send_value == mp_const_none) { + MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; + mp_obj_t ret = type_get_iternext(type)(self_in); + *ret_val = ret; + if (ret != MP_OBJ_STOP_ITERATION) { + return MP_VM_RETURN_YIELD; + } else { + // The generator is finished. + // This is an optimised "raise StopIteration(*ret_val)". + *ret_val = MP_STATE_THREAD(stop_iteration_arg); + if (*ret_val == MP_OBJ_NULL) { + *ret_val = mp_const_none; + } + return MP_VM_RETURN_NORMAL; + } + } + + mp_obj_t dest[3]; // Reserve slot for send() arg + + // Python instance iterator protocol + if (send_value == mp_const_none) { + mp_load_method_maybe(self_in, MP_QSTR___next__, dest); + if (dest[0] != MP_OBJ_NULL) { + *ret_val = mp_call_method_n_kw(0, 0, dest); + return MP_VM_RETURN_YIELD; + } + } + + // Either python instance generator protocol, or native object + // generator protocol. + if (send_value != MP_OBJ_NULL) { + mp_load_method(self_in, MP_QSTR_send, dest); + dest[2] = send_value; + *ret_val = mp_call_method_n_kw(1, 0, dest); + return MP_VM_RETURN_YIELD; + } + + assert(throw_value != MP_OBJ_NULL); + { + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(throw_value)), MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { + mp_load_method_maybe(self_in, MP_QSTR_close, dest); + if (dest[0] != MP_OBJ_NULL) { + // TODO: Exceptions raised in close() are not propagated, + // printed to sys.stderr + *ret_val = mp_call_method_n_kw(0, 0, dest); + // We assume one can't "yield" from close() + return MP_VM_RETURN_NORMAL; + } + } else { + mp_load_method_maybe(self_in, MP_QSTR_throw, dest); + if (dest[0] != MP_OBJ_NULL) { + dest[2] = throw_value; + *ret_val = mp_call_method_n_kw(1, 0, dest); + // If .throw() method returned, we assume it's value to yield + // - any exception would be thrown with nlr_raise(). + return MP_VM_RETURN_YIELD; + } + } + // If there's nowhere to throw exception into, then we assume that object + // is just incapable to handle it, so any exception thrown into it + // will be propagated up. This behavior is approved by test_pep380.py + // test_delegation_of_close_to_non_generator(), + // test_delegating_throw_to_non_generator() + if (mp_obj_exception_match(throw_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { + // PEP479: if StopIteration is raised inside a generator it is replaced with RuntimeError + *ret_val = mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("generator raised StopIteration")); + } else { + *ret_val = mp_make_raise_obj(throw_value); + } + return MP_VM_RETURN_EXCEPTION; + } +} + +mp_obj_t mp_make_raise_obj(mp_obj_t o) { + DEBUG_printf("raise %p\n", o); + if (mp_obj_is_exception_type(o)) { + // o is an exception type (it is derived from BaseException (or is BaseException)) + // create and return a new exception instance by calling o + // TODO could have an option to disable traceback, then builtin exceptions (eg TypeError) + // could have const instances in ROM which we return here instead + o = mp_call_function_n_kw(o, 0, 0, NULL); + } + + if (mp_obj_is_exception_instance(o)) { + // o is an instance of an exception, so use it as the exception + return o; + } else { + // o cannot be used as an exception, so return a type error (which will be raised by the caller) + return mp_obj_new_exception_msg(&mp_type_TypeError, MP_ERROR_TEXT("exceptions must derive from BaseException")); + } +} + +mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) { + DEBUG_printf("import name '%s' level=%d\n", qstr_str(name), MP_OBJ_SMALL_INT_VALUE(level)); + + // build args array + mp_obj_t args[5]; + args[0] = MP_OBJ_NEW_QSTR(name); + args[1] = mp_const_none; // TODO should be globals + args[2] = mp_const_none; // TODO should be locals + args[3] = fromlist; + args[4] = level; + + #if MICROPY_CAN_OVERRIDE_BUILTINS + // Lookup __import__ and call that if it exists + mp_obj_dict_t *bo_dict = MP_STATE_VM(mp_module_builtins_override_dict); + if (bo_dict != NULL) { + mp_map_elem_t *import = mp_map_lookup(&bo_dict->map, MP_OBJ_NEW_QSTR(MP_QSTR___import__), MP_MAP_LOOKUP); + if (import != NULL) { + return mp_call_function_n_kw(import->value, 5, 0, args); + } + } + #endif + + return mp_builtin___import__(5, args); +} + +mp_obj_t mp_import_from(mp_obj_t module, qstr name) { + DEBUG_printf("import from %p %s\n", module, qstr_str(name)); + + mp_obj_t dest[2]; + + mp_load_method_maybe(module, name, dest); + + if (dest[1] != MP_OBJ_NULL) { + // Hopefully we can't import bound method from an object + import_error: + mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("can't import name %q"), name); + } + + if (dest[0] != MP_OBJ_NULL) { + return dest[0]; + } + + #if MICROPY_ENABLE_EXTERNAL_IMPORT + + // See if it's a package, then can try FS import + mp_load_method_maybe(module, MP_QSTR___path__, dest); + if (dest[0] == MP_OBJ_NULL) { + goto import_error; + } + + mp_load_method_maybe(module, MP_QSTR___name__, dest); + size_t pkg_name_len; + const char *pkg_name = mp_obj_str_get_data(dest[0], &pkg_name_len); + + const uint dot_name_len = pkg_name_len + 1 + qstr_len(name); + char *dot_name = mp_local_alloc(dot_name_len); + memcpy(dot_name, pkg_name, pkg_name_len); + dot_name[pkg_name_len] = '.'; + memcpy(dot_name + pkg_name_len + 1, qstr_str(name), qstr_len(name)); + qstr dot_name_q = qstr_from_strn(dot_name, dot_name_len); + mp_local_free(dot_name); + + // For fromlist, pass sentinel "non empty" value to force returning of leaf module + return mp_import_name(dot_name_q, mp_const_true, MP_OBJ_NEW_SMALL_INT(0)); + + #else + + // Package import not supported with external imports disabled + goto import_error; + + #endif +} + +void mp_import_all(mp_obj_t module) { + DEBUG_printf("import all %p\n", module); + + // TODO: Support __all__ + mp_map_t *map = &mp_obj_module_get_globals(module)->map; + for (size_t i = 0; i < map->alloc; i++) { + if (mp_map_slot_is_filled(map, i)) { + // Entry in module global scope may be generated programmatically + // (and thus be not a qstr for longer names). Avoid turning it in + // qstr if it has '_' and was used exactly to save memory. + const char *name = mp_obj_str_get_str(map->table[i].key); + if (*name != '_') { + qstr qname = mp_obj_str_get_qstr(map->table[i].key); + mp_store_name(qname, map->table[i].value); + } + } + } +} + +#if MICROPY_ENABLE_COMPILER + +mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { + // save context + nlr_jump_callback_node_globals_locals_t ctx; + ctx.globals = mp_globals_get(); + ctx.locals = mp_locals_get(); + + // set new context + mp_globals_set(globals); + mp_locals_set(locals); + + // set exception handler to restore context if an exception is raised + nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback); + + qstr source_name = lex->source_name; + mp_parse_tree_t parse_tree = mp_parse(lex, parse_input_kind); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT); + + mp_obj_t ret; + if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { + // for compile only, return value is the module function + ret = module_fun; + } else { + // execute module function and get return value + ret = mp_call_function_0(module_fun); + } + + // deregister exception handler and restore context + nlr_pop_jump_callback(true); + + // return value + return ret; +} + +#endif // MICROPY_ENABLE_COMPILER + +NORETURN void m_malloc_fail(size_t num_bytes) { + DEBUG_printf("memory allocation failed, allocating %u bytes\n", (uint)num_bytes); + #if MICROPY_ENABLE_GC + if (gc_is_locked()) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("memory allocation failed, heap is locked")); + } + #endif + mp_raise_msg_varg(&mp_type_MemoryError, + MP_ERROR_TEXT("memory allocation failed, allocating %u bytes"), (uint)num_bytes); +} + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE + +NORETURN void mp_raise_type(const mp_obj_type_t *exc_type) { + nlr_raise(mp_obj_new_exception(exc_type)); +} + +NORETURN void mp_raise_ValueError_no_msg(void) { + mp_raise_type(&mp_type_ValueError); +} + +NORETURN void mp_raise_TypeError_no_msg(void) { + mp_raise_type(&mp_type_TypeError); +} + +NORETURN void mp_raise_NotImplementedError_no_msg(void) { + mp_raise_type(&mp_type_NotImplementedError); +} + +#else + +NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { + if (msg == NULL) { + nlr_raise(mp_obj_new_exception(exc_type)); + } else { + nlr_raise(mp_obj_new_exception_msg(exc_type, msg)); + } +} + +NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { + va_list args; + va_start(args, fmt); + mp_obj_t exc = mp_obj_new_exception_msg_vlist(exc_type, fmt, args); + va_end(args); + nlr_raise(exc); +} + +NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg) { + mp_raise_msg(&mp_type_ValueError, msg); +} + +NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg) { + mp_raise_msg(&mp_type_TypeError, msg); +} + +NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { + mp_raise_msg(&mp_type_NotImplementedError, msg); +} + +#endif + +NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) { + nlr_raise(mp_obj_new_exception_arg1(exc_type, arg)); +} + +NORETURN void mp_raise_StopIteration(mp_obj_t arg) { + if (arg == MP_OBJ_NULL) { + mp_raise_type(&mp_type_StopIteration); + } else { + mp_raise_type_arg(&mp_type_StopIteration, arg); + } +} + +NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + (void)arg; + mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert %s to int"), mp_obj_get_type_str(arg)); + #endif +} + +NORETURN void mp_raise_OSError(int errno_) { + mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_)); +} + +NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename) { + vstr_t vstr; + vstr_init(&vstr, 32); + vstr_printf(&vstr, "can't open %s", filename); + mp_obj_t o_str = mp_obj_new_str_from_vstr(&vstr); + mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(errno_), MP_OBJ_FROM_PTR(o_str)}; + nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); +} + +#if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK +NORETURN void mp_raise_recursion_depth(void) { + mp_raise_type_arg(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded)); +} +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/runtime.h b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime.h new file mode 100644 index 00000000..5465c06d --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime.h @@ -0,0 +1,305 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_RUNTIME_H +#define MICROPY_INCLUDED_PY_RUNTIME_H + +#include "py/mpstate.h" +#include "py/pystack.h" +#include "py/stackctrl.h" + +// For use with mp_call_function_1_from_nlr_jump_callback. +#define MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, f, a) \ + nlr_jump_callback_node_call_function_1_t ctx = { \ + .func = (void (*)(void *))(f), \ + .arg = (a), \ + } + +typedef enum { + MP_VM_RETURN_NORMAL, + MP_VM_RETURN_YIELD, + MP_VM_RETURN_EXCEPTION, +} mp_vm_return_kind_t; + +typedef enum { + MP_ARG_BOOL = 0x001, + MP_ARG_INT = 0x002, + MP_ARG_OBJ = 0x003, + MP_ARG_KIND_MASK = 0x0ff, + MP_ARG_REQUIRED = 0x100, + MP_ARG_KW_ONLY = 0x200, +} mp_arg_flag_t; + +typedef union _mp_arg_val_t { + bool u_bool; + mp_int_t u_int; + mp_obj_t u_obj; + mp_rom_obj_t u_rom_obj; +} mp_arg_val_t; + +typedef struct _mp_arg_t { + uint16_t qst; + uint16_t flags; + mp_arg_val_t defval; +} mp_arg_t; + +struct _mp_sched_node_t; + +typedef void (*mp_sched_callback_t)(struct _mp_sched_node_t *); + +typedef struct _mp_sched_node_t { + mp_sched_callback_t callback; + struct _mp_sched_node_t *next; +} mp_sched_node_t; + +// For use with mp_globals_locals_set_from_nlr_jump_callback. +typedef struct _nlr_jump_callback_node_globals_locals_t { + nlr_jump_callback_node_t callback; + mp_obj_dict_t *globals; + mp_obj_dict_t *locals; +} nlr_jump_callback_node_globals_locals_t; + +// For use with mp_call_function_1_from_nlr_jump_callback. +typedef struct _nlr_jump_callback_node_call_function_1_t { + nlr_jump_callback_node_t callback; + void (*func)(void *); + void *arg; +} nlr_jump_callback_node_call_function_1_t; + +// Tables mapping operator enums to qstrs, defined in objtype.c +extern const byte mp_unary_op_method_name[]; +extern const byte mp_binary_op_method_name[]; + +void mp_init(void); +void mp_deinit(void); + +void mp_sched_exception(mp_obj_t exc); +void mp_sched_keyboard_interrupt(void); +#if MICROPY_ENABLE_VM_ABORT +void mp_sched_vm_abort(void); +#endif +void mp_handle_pending(bool raise_exc); + +#if MICROPY_ENABLE_SCHEDULER +void mp_sched_lock(void); +void mp_sched_unlock(void); +#define mp_sched_num_pending() (MP_STATE_VM(sched_len)) +bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg); +bool mp_sched_schedule_node(mp_sched_node_t *node, mp_sched_callback_t callback); +#endif + +// Handles any pending MicroPython events without waiting for an interrupt or event. +void mp_event_handle_nowait(void); + +// Handles any pending MicroPython events and then suspends execution until the +// next interrupt or event. +// +// Note: on "tickless" ports this can suspend execution for a long time, +// don't call unless you know an interrupt is coming to continue execution. +// On "ticked" ports it may return early due to the tick interrupt. +void mp_event_wait_indefinite(void); + +// Handle any pending MicroPython events and then suspends execution until the +// next interrupt or event, or until timeout_ms milliseconds have elapsed. +// +// On "ticked" ports it may return early due to the tick interrupt. +void mp_event_wait_ms(mp_uint_t timeout_ms); + +// extra printing method specifically for mp_obj_t's which are integral type +int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec); + +void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig); +static inline void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) { + mp_arg_check_num_sig(n_args, n_kw, MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, takes_kw)); +} +void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); +void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); +NORETURN void mp_arg_error_terse_mismatch(void); +NORETURN void mp_arg_error_unimpl_kw(void); + +static inline mp_obj_dict_t *mp_locals_get(void) { + return MP_STATE_THREAD(dict_locals); +} +static inline void mp_locals_set(mp_obj_dict_t *d) { + MP_STATE_THREAD(dict_locals) = d; +} +static inline mp_obj_dict_t *mp_globals_get(void) { + return MP_STATE_THREAD(dict_globals); +} +static inline void mp_globals_set(mp_obj_dict_t *d) { + MP_STATE_THREAD(dict_globals) = d; +} + +void mp_globals_locals_set_from_nlr_jump_callback(void *ctx_in); +void mp_call_function_1_from_nlr_jump_callback(void *ctx_in); + +#if MICROPY_PY_THREAD +static inline void mp_thread_init_state(mp_state_thread_t *ts, size_t stack_size, mp_obj_dict_t *locals, mp_obj_dict_t *globals) { + mp_thread_set_state(ts); + + mp_stack_set_top(ts + 1); // need to include ts in root-pointer scan + mp_stack_set_limit(stack_size); + + // GC starts off unlocked + ts->gc_lock_depth = 0; + + // There are no pending jump callbacks or exceptions yet + ts->nlr_jump_callback_top = NULL; + ts->mp_pending_exception = MP_OBJ_NULL; + + // If locals/globals are not given, inherit from main thread + if (locals == NULL) { + locals = mp_state_ctx.thread.dict_locals; + } + if (globals == NULL) { + globals = mp_state_ctx.thread.dict_globals; + } + mp_locals_set(locals); + mp_globals_set(globals); +} +#endif + +mp_obj_t mp_load_name(qstr qst); +mp_obj_t mp_load_global(qstr qst); +mp_obj_t mp_load_build_class(void); +void mp_store_name(qstr qst, mp_obj_t obj); +void mp_store_global(qstr qst, mp_obj_t obj); +void mp_delete_name(qstr qst); +void mp_delete_global(qstr qst); + +mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg); +mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs); + +mp_obj_t mp_call_function_0(mp_obj_t fun); +mp_obj_t mp_call_function_1(mp_obj_t fun, mp_obj_t arg); +mp_obj_t mp_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2); +mp_obj_t mp_call_function_n_kw(mp_obj_t fun, size_t n_args, size_t n_kw, const mp_obj_t *args); +mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args); +mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args); +mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args); +// Call function and catch/dump exception - for Python callbacks from C code +// (return MP_OBJ_NULL in case of exception). +mp_obj_t mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg); +mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2); + +typedef struct _mp_call_args_t { + mp_obj_t fun; + size_t n_args, n_kw, n_alloc; + mp_obj_t *args; +} mp_call_args_t; + +#if MICROPY_STACKLESS +// Takes arguments which are the most general mix of Python arg types, and +// prepares argument array suitable for passing to ->call() method of a +// function object (and mp_call_function_n_kw()). +// (Only needed in stackless mode.) +void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args, mp_call_args_t *out_args); +#endif + +void mp_unpack_sequence(mp_obj_t seq, size_t num, mp_obj_t *items); +void mp_unpack_ex(mp_obj_t seq, size_t num, mp_obj_t *items); +mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value); +mp_obj_t mp_load_attr(mp_obj_t base, qstr attr); +void mp_convert_member_lookup(mp_obj_t obj, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest); +void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest); +void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest); +void mp_load_method_protected(mp_obj_t obj, qstr attr, mp_obj_t *dest, bool catch_all_exc); +void mp_load_super_method(qstr attr, mp_obj_t *dest); +void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val); + +mp_obj_t mp_getiter(mp_obj_t o, mp_obj_iter_buf_t *iter_buf); +mp_obj_t mp_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_STOP_ITERATION instead of raising StopIteration() +mp_obj_t mp_iternext(mp_obj_t o); // will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration(...) +mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val); + +static inline mp_obj_t mp_make_stop_iteration(mp_obj_t o) { + MP_STATE_THREAD(stop_iteration_arg) = o; + return MP_OBJ_STOP_ITERATION; +} + +mp_obj_t mp_make_raise_obj(mp_obj_t o); + +mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level); +mp_obj_t mp_import_from(mp_obj_t module, qstr name); +void mp_import_all(mp_obj_t module); + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE +NORETURN void mp_raise_type(const mp_obj_type_t *exc_type); +NORETURN void mp_raise_ValueError_no_msg(void); +NORETURN void mp_raise_TypeError_no_msg(void); +NORETURN void mp_raise_NotImplementedError_no_msg(void); +#define mp_raise_msg(exc_type, msg) mp_raise_type(exc_type) +#define mp_raise_msg_varg(exc_type, ...) mp_raise_type(exc_type) +#define mp_raise_ValueError(msg) mp_raise_ValueError_no_msg() +#define mp_raise_TypeError(msg) mp_raise_TypeError_no_msg() +#define mp_raise_NotImplementedError(msg) mp_raise_NotImplementedError_no_msg() +#else +#define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) +NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); +NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...); +NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); +NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); +NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); +#endif + +NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); +NORETURN void mp_raise_StopIteration(mp_obj_t arg); +NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg); +NORETURN void mp_raise_OSError(int errno_); +NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename); +NORETURN void mp_raise_recursion_depth(void); + +#if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG +#undef mp_check_self +#define mp_check_self(pred) +#else +// A port may define to raise TypeError for example +#ifndef mp_check_self +#define mp_check_self(pred) assert(pred) +#endif +#endif + +// helper functions for native/viper code +int mp_native_type_from_qstr(qstr qst); +mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type); +mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type); + +#if MICROPY_PY_SYS_PATH +#define mp_sys_path (MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PATH])) +#endif + +#if MICROPY_PY_SYS_ARGV +#define mp_sys_argv (MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_sys_argv_obj))) +#endif + +#if MICROPY_WARNINGS +#ifndef mp_warning +void mp_warning(const char *category, const char *msg, ...); +#endif +#else +#define mp_warning(...) +#endif + +#endif // MICROPY_INCLUDED_PY_RUNTIME_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/runtime0.h b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime0.h new file mode 100644 index 00000000..9c6f0e07 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime0.h @@ -0,0 +1,167 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_RUNTIME0_H +#define MICROPY_INCLUDED_PY_RUNTIME0_H + +// These constants are used by: +// - mp_raw_code_t::is_generator (only MP_SCOPE_FLAG_GENERATOR) +// - scope_t::scope_flags (16 bits) +// - MP_BC_PRELUDE_SIG_ENCODE macro, masked by MP_SCOPE_FLAG_ALL_SIG (4 bits) +// - tools/mpy_ld.py, when generating mpy files (maximum 7 bits) +#define MP_SCOPE_FLAG_ALL_SIG (0x0f) +#define MP_SCOPE_FLAG_GENERATOR (0x01) +#define MP_SCOPE_FLAG_VARKEYWORDS (0x02) +#define MP_SCOPE_FLAG_VARARGS (0x04) +#define MP_SCOPE_FLAG_DEFKWARGS (0x08) +#define MP_SCOPE_FLAG_REFGLOBALS (0x10) // used only if native emitter enabled +#define MP_SCOPE_FLAG_HASCONSTS (0x20) // used only if native emitter enabled +#define MP_SCOPE_FLAG_VIPERRET_POS (6) // 3 bits used for viper return type, to pass from compiler to native emitter +#define MP_SCOPE_FLAG_VIPERRELOC (0x10) // used only when loading viper from .mpy +#define MP_SCOPE_FLAG_VIPERRODATA (0x20) // used only when loading viper from .mpy +#define MP_SCOPE_FLAG_VIPERBSS (0x40) // used only when loading viper from .mpy + +// types for native (viper) function signature +#define MP_NATIVE_TYPE_OBJ (0x00) +#define MP_NATIVE_TYPE_BOOL (0x01) +#define MP_NATIVE_TYPE_INT (0x02) +#define MP_NATIVE_TYPE_UINT (0x03) +#define MP_NATIVE_TYPE_PTR (0x04) +#define MP_NATIVE_TYPE_PTR8 (0x05) +#define MP_NATIVE_TYPE_PTR16 (0x06) +#define MP_NATIVE_TYPE_PTR32 (0x07) + +// Not use for viper, but for dynamic native modules +#define MP_NATIVE_TYPE_QSTR (0x08) + +// Bytecode and runtime boundaries for unary ops +#define MP_UNARY_OP_NUM_BYTECODE (MP_UNARY_OP_NOT + 1) +#define MP_UNARY_OP_NUM_RUNTIME (MP_UNARY_OP_SIZEOF + 1) + +// Bytecode and runtime boundaries for binary ops +#define MP_BINARY_OP_NUM_BYTECODE (MP_BINARY_OP_POWER + 1) +#if MICROPY_PY_REVERSE_SPECIAL_METHODS +#define MP_BINARY_OP_NUM_RUNTIME (MP_BINARY_OP_REVERSE_POWER + 1) +#else +#define MP_BINARY_OP_NUM_RUNTIME (MP_BINARY_OP_CONTAINS + 1) +#endif + +typedef enum { + // These ops may appear in the bytecode. Changing this group + // in any way requires changing the bytecode version. + MP_UNARY_OP_POSITIVE, + MP_UNARY_OP_NEGATIVE, + MP_UNARY_OP_INVERT, + MP_UNARY_OP_NOT, + + // Following ops cannot appear in the bytecode + MP_UNARY_OP_BOOL, // __bool__ + MP_UNARY_OP_LEN, // __len__ + MP_UNARY_OP_HASH, // __hash__; must return a small int + MP_UNARY_OP_ABS, // __abs__ + MP_UNARY_OP_INT_MAYBE, // __int__; must return MP_OBJ_NULL, or an object satisfying mp_obj_is_int() + MP_UNARY_OP_FLOAT_MAYBE, // __float__ + MP_UNARY_OP_COMPLEX_MAYBE, // __complex__ + MP_UNARY_OP_SIZEOF, // for sys.getsizeof() +} mp_unary_op_t; + +typedef enum { + // The following 9+13+13 ops are used in bytecode and changing + // them requires changing the bytecode version. + + // 9 relational operations, should return a bool; order of first 6 matches corresponding mp_token_kind_t + MP_BINARY_OP_LESS, + MP_BINARY_OP_MORE, + MP_BINARY_OP_EQUAL, + MP_BINARY_OP_LESS_EQUAL, + MP_BINARY_OP_MORE_EQUAL, + MP_BINARY_OP_NOT_EQUAL, + MP_BINARY_OP_IN, + MP_BINARY_OP_IS, + MP_BINARY_OP_EXCEPTION_MATCH, + + // 13 inplace arithmetic operations; order matches corresponding mp_token_kind_t + MP_BINARY_OP_INPLACE_OR, + MP_BINARY_OP_INPLACE_XOR, + MP_BINARY_OP_INPLACE_AND, + MP_BINARY_OP_INPLACE_LSHIFT, + MP_BINARY_OP_INPLACE_RSHIFT, + MP_BINARY_OP_INPLACE_ADD, + MP_BINARY_OP_INPLACE_SUBTRACT, + MP_BINARY_OP_INPLACE_MULTIPLY, + MP_BINARY_OP_INPLACE_MAT_MULTIPLY, + MP_BINARY_OP_INPLACE_FLOOR_DIVIDE, + MP_BINARY_OP_INPLACE_TRUE_DIVIDE, + MP_BINARY_OP_INPLACE_MODULO, + MP_BINARY_OP_INPLACE_POWER, + + // 13 normal arithmetic operations; order matches corresponding mp_token_kind_t + MP_BINARY_OP_OR, + MP_BINARY_OP_XOR, + MP_BINARY_OP_AND, + MP_BINARY_OP_LSHIFT, + MP_BINARY_OP_RSHIFT, + MP_BINARY_OP_ADD, + MP_BINARY_OP_SUBTRACT, + MP_BINARY_OP_MULTIPLY, + MP_BINARY_OP_MAT_MULTIPLY, + MP_BINARY_OP_FLOOR_DIVIDE, + MP_BINARY_OP_TRUE_DIVIDE, + MP_BINARY_OP_MODULO, + MP_BINARY_OP_POWER, + + // Operations below this line don't appear in bytecode, they + // just identify special methods. + + // This is not emitted by the compiler but is supported by the runtime. + // It must follow immediately after MP_BINARY_OP_POWER. + MP_BINARY_OP_DIVMOD, + + // The runtime will convert MP_BINARY_OP_IN to this operator with swapped args. + // A type should implement this containment operator instead of MP_BINARY_OP_IN. + MP_BINARY_OP_CONTAINS, + + // 13 MP_BINARY_OP_REVERSE_* operations must be in the same order as MP_BINARY_OP_*, + // and be the last ones supported by the runtime. + MP_BINARY_OP_REVERSE_OR, + MP_BINARY_OP_REVERSE_XOR, + MP_BINARY_OP_REVERSE_AND, + MP_BINARY_OP_REVERSE_LSHIFT, + MP_BINARY_OP_REVERSE_RSHIFT, + MP_BINARY_OP_REVERSE_ADD, + MP_BINARY_OP_REVERSE_SUBTRACT, + MP_BINARY_OP_REVERSE_MULTIPLY, + MP_BINARY_OP_REVERSE_MAT_MULTIPLY, + MP_BINARY_OP_REVERSE_FLOOR_DIVIDE, + MP_BINARY_OP_REVERSE_TRUE_DIVIDE, + MP_BINARY_OP_REVERSE_MODULO, + MP_BINARY_OP_REVERSE_POWER, + + // These 2 are not supported by the runtime and must be synthesised by the emitter + MP_BINARY_OP_NOT_IN, + MP_BINARY_OP_IS_NOT, +} mp_binary_op_t; + +#endif // MICROPY_INCLUDED_PY_RUNTIME0_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/runtime_utils.c b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime_utils.c new file mode 100644 index 00000000..b92c6bd7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/runtime_utils.c @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Josef Gajdusek + * Copyright (c) 2015 Paul Sokolovsky + * + * 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 "py/runtime.h" + +mp_obj_t mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t ret = mp_call_function_1(fun, arg); + nlr_pop(); + return ret; + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + return MP_OBJ_NULL; + } +} + +mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t ret = mp_call_function_2(fun, arg1, arg2); + nlr_pop(); + return ret; + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + return MP_OBJ_NULL; + } +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/scheduler.c b/non_catalog_apps/mp_flipper/lib/micropython/py/scheduler.c new file mode 100644 index 00000000..3eae8b4f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/scheduler.c @@ -0,0 +1,279 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Damien P. George + * + * 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 "py/mphal.h" +#include "py/runtime.h" + +// Schedules an exception on the main thread (for exceptions "thrown" by async +// sources such as interrupts and UNIX signal handlers). +void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { + MP_STATE_MAIN_THREAD(mp_pending_exception) = exc; + + #if MICROPY_ENABLE_SCHEDULER && !MICROPY_PY_THREAD + // Optimisation for the case where we have scheduler but no threading. + // Allows the VM to do a single check to exclude both pending exception + // and queued tasks. + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } + #endif +} + +#if MICROPY_KBD_EXCEPTION +// This function may be called asynchronously at any time so only do the bare minimum. +void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) { + MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; + mp_sched_exception(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); +} +#endif + +#if MICROPY_ENABLE_VM_ABORT +void MICROPY_WRAP_MP_SCHED_VM_ABORT(mp_sched_vm_abort)(void) { + MP_STATE_VM(vm_abort) = true; +} +#endif + +#if MICROPY_ENABLE_SCHEDULER + +#define IDX_MASK(i) ((i) & (MICROPY_SCHEDULER_DEPTH - 1)) + +// This is a macro so it is guaranteed to be inlined in functions like +// mp_sched_schedule that may be located in a special memory region. +#define mp_sched_full() (mp_sched_num_pending() == MICROPY_SCHEDULER_DEPTH) + +static inline bool mp_sched_empty(void) { + MP_STATIC_ASSERT(MICROPY_SCHEDULER_DEPTH <= 255); // MICROPY_SCHEDULER_DEPTH must fit in 8 bits + MP_STATIC_ASSERT((IDX_MASK(MICROPY_SCHEDULER_DEPTH) == 0)); // MICROPY_SCHEDULER_DEPTH must be a power of 2 + + return mp_sched_num_pending() == 0; +} + +static inline void mp_sched_run_pending(void) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + if (MP_STATE_VM(sched_state) != MP_SCHED_PENDING) { + // Something else (e.g. hard IRQ) locked the scheduler while we + // acquired the lock. + MICROPY_END_ATOMIC_SECTION(atomic_state); + return; + } + + // Equivalent to mp_sched_lock(), but we're already in the atomic + // section and know that we're pending. + MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; + + #if MICROPY_SCHEDULER_STATIC_NODES + // Run all pending C callbacks. + while (MP_STATE_VM(sched_head) != NULL) { + mp_sched_node_t *node = MP_STATE_VM(sched_head); + MP_STATE_VM(sched_head) = node->next; + if (MP_STATE_VM(sched_head) == NULL) { + MP_STATE_VM(sched_tail) = NULL; + } + mp_sched_callback_t callback = node->callback; + node->callback = NULL; + MICROPY_END_ATOMIC_SECTION(atomic_state); + callback(node); + atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + } + #endif + + // Run at most one pending Python callback. + if (!mp_sched_empty()) { + mp_sched_item_t item = MP_STATE_VM(sched_queue)[MP_STATE_VM(sched_idx)]; + MP_STATE_VM(sched_idx) = IDX_MASK(MP_STATE_VM(sched_idx) + 1); + --MP_STATE_VM(sched_len); + MICROPY_END_ATOMIC_SECTION(atomic_state); + mp_call_function_1_protected(item.func, item.arg); + } else { + MICROPY_END_ATOMIC_SECTION(atomic_state); + } + + // Restore MP_STATE_VM(sched_state) to idle (or pending if there are still + // tasks in the queue). + mp_sched_unlock(); +} + +// Locking the scheduler prevents tasks from executing (does not prevent new +// tasks from being added). We lock the scheduler while executing scheduled +// tasks and also in hard interrupts or GC finalisers. +void mp_sched_lock(void) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + if (MP_STATE_VM(sched_state) < 0) { + // Already locked, increment lock (recursive lock). + --MP_STATE_VM(sched_state); + } else { + // Pending or idle. + MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; + } + MICROPY_END_ATOMIC_SECTION(atomic_state); +} + +void mp_sched_unlock(void) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + assert(MP_STATE_VM(sched_state) < 0); + if (++MP_STATE_VM(sched_state) == 0) { + // Scheduler became unlocked. Check if there are still tasks in the + // queue and set sched_state accordingly. + if ( + #if !MICROPY_PY_THREAD + // See optimisation in mp_sched_exception. + MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL || + #endif + #if MICROPY_SCHEDULER_STATIC_NODES + MP_STATE_VM(sched_head) != NULL || + #endif + mp_sched_num_pending()) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } else { + MP_STATE_VM(sched_state) = MP_SCHED_IDLE; + } + } + MICROPY_END_ATOMIC_SECTION(atomic_state); +} + +bool MICROPY_WRAP_MP_SCHED_SCHEDULE(mp_sched_schedule)(mp_obj_t function, mp_obj_t arg) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + bool ret; + if (!mp_sched_full()) { + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } + uint8_t iput = IDX_MASK(MP_STATE_VM(sched_idx) + MP_STATE_VM(sched_len)++); + MP_STATE_VM(sched_queue)[iput].func = function; + MP_STATE_VM(sched_queue)[iput].arg = arg; + MICROPY_SCHED_HOOK_SCHEDULED; + ret = true; + } else { + // schedule queue is full + ret = false; + } + MICROPY_END_ATOMIC_SECTION(atomic_state); + return ret; +} + +#if MICROPY_SCHEDULER_STATIC_NODES +bool mp_sched_schedule_node(mp_sched_node_t *node, mp_sched_callback_t callback) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + bool ret; + if (node->callback == NULL) { + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } + node->callback = callback; + node->next = NULL; + if (MP_STATE_VM(sched_tail) == NULL) { + MP_STATE_VM(sched_head) = node; + } else { + MP_STATE_VM(sched_tail)->next = node; + } + MP_STATE_VM(sched_tail) = node; + MICROPY_SCHED_HOOK_SCHEDULED; + ret = true; + } else { + // already scheduled + ret = false; + } + MICROPY_END_ATOMIC_SECTION(atomic_state); + return ret; +} +#endif + +MP_REGISTER_ROOT_POINTER(mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]); + +#endif // MICROPY_ENABLE_SCHEDULER + +// Called periodically from the VM or from "waiting" code (e.g. sleep) to +// process background tasks and pending exceptions (e.g. KeyboardInterrupt). +void mp_handle_pending(bool raise_exc) { + // Handle pending VM abort. + #if MICROPY_ENABLE_VM_ABORT + if (MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { + MP_STATE_VM(vm_abort) = false; + if (raise_exc && nlr_get_abort() != NULL) { + nlr_jump_abort(); + } + } + #endif + + // Handle any pending exception. + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); + if (obj != MP_OBJ_NULL) { + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; + if (raise_exc) { + MICROPY_END_ATOMIC_SECTION(atomic_state); + nlr_raise(obj); + } + } + MICROPY_END_ATOMIC_SECTION(atomic_state); + } + + // Handle any pending callbacks. + #if MICROPY_ENABLE_SCHEDULER + if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { + mp_sched_run_pending(); + } + #endif +} + +// Handles any pending MicroPython events without waiting for an interrupt or event. +void mp_event_handle_nowait(void) { + #if defined(MICROPY_EVENT_POLL_HOOK_FAST) && !MICROPY_PREVIEW_VERSION_2 + // For ports still using the old macros. + MICROPY_EVENT_POLL_HOOK_FAST + #else + // Process any port layer (non-blocking) events. + MICROPY_INTERNAL_EVENT_HOOK; + mp_handle_pending(true); + #endif +} + +// Handles any pending MicroPython events and then suspends execution until the +// next interrupt or event. +void mp_event_wait_indefinite(void) { + #if defined(MICROPY_EVENT_POLL_HOOK) && !MICROPY_PREVIEW_VERSION_2 + // For ports still using the old macros. + MICROPY_EVENT_POLL_HOOK + #else + mp_event_handle_nowait(); + MICROPY_INTERNAL_WFE(-1); + #endif +} + +// Handle any pending MicroPython events and then suspends execution until the +// next interrupt or event, or until timeout_ms milliseconds have elapsed. +void mp_event_wait_ms(mp_uint_t timeout_ms) { + #if defined(MICROPY_EVENT_POLL_HOOK) && !MICROPY_PREVIEW_VERSION_2 + // For ports still using the old macros. + MICROPY_EVENT_POLL_HOOK + #else + mp_event_handle_nowait(); + MICROPY_INTERNAL_WFE(timeout_ms); + #endif +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/scope.c b/non_catalog_apps/mp_flipper/lib/micropython/py/scope.c new file mode 100644 index 00000000..4893e7cc --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/scope.c @@ -0,0 +1,154 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/scope.h" + +#if MICROPY_ENABLE_COMPILER + +// These low numbered qstrs should fit in 8 bits. See assertions below. +// The (unescaped) names appear in `unsorted_str_list` in the QSTR +// generator script py/makeqstrdata.py to ensure they are assigned low numbers. +static const uint8_t scope_simple_name_table[] = { + [SCOPE_MODULE] = MP_QSTR__lt_module_gt_, + [SCOPE_LAMBDA] = MP_QSTR__lt_lambda_gt_, + [SCOPE_LIST_COMP] = MP_QSTR__lt_listcomp_gt_, + [SCOPE_DICT_COMP] = MP_QSTR__lt_dictcomp_gt_, + [SCOPE_SET_COMP] = MP_QSTR__lt_setcomp_gt_, + [SCOPE_GEN_EXPR] = MP_QSTR__lt_genexpr_gt_, +}; + +scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, mp_uint_t emit_options) { + // Make sure those qstrs indeed fit in an uint8_t. + MP_STATIC_ASSERT(MP_QSTR__lt_module_gt_ <= UINT8_MAX); + MP_STATIC_ASSERT(MP_QSTR__lt_lambda_gt_ <= UINT8_MAX); + MP_STATIC_ASSERT(MP_QSTR__lt_listcomp_gt_ <= UINT8_MAX); + MP_STATIC_ASSERT(MP_QSTR__lt_dictcomp_gt_ <= UINT8_MAX); + MP_STATIC_ASSERT(MP_QSTR__lt_setcomp_gt_ <= UINT8_MAX); + MP_STATIC_ASSERT(MP_QSTR__lt_genexpr_gt_ <= UINT8_MAX); + + scope_t *scope = m_new0(scope_t, 1); + scope->kind = kind; + scope->pn = pn; + if (kind == SCOPE_FUNCTION || kind == SCOPE_CLASS) { + assert(MP_PARSE_NODE_IS_STRUCT(pn)); + scope->simple_name = MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t *)pn)->nodes[0]); + } else { + scope->simple_name = scope_simple_name_table[kind]; + } + scope->raw_code = mp_emit_glue_new_raw_code(); + scope->emit_options = emit_options; + scope->id_info_alloc = MICROPY_ALLOC_SCOPE_ID_INIT; + scope->id_info = m_new(id_info_t, scope->id_info_alloc); + + return scope; +} + +void scope_free(scope_t *scope) { + m_del(id_info_t, scope->id_info, scope->id_info_alloc); + m_del(scope_t, scope, 1); +} + +id_info_t *scope_find_or_add_id(scope_t *scope, qstr qst, id_info_kind_t kind) { + id_info_t *id_info = scope_find(scope, qst); + if (id_info != NULL) { + return id_info; + } + + // make sure we have enough memory + if (scope->id_info_len >= scope->id_info_alloc) { + scope->id_info = m_renew(id_info_t, scope->id_info, scope->id_info_alloc, scope->id_info_alloc + MICROPY_ALLOC_SCOPE_ID_INC); + scope->id_info_alloc += MICROPY_ALLOC_SCOPE_ID_INC; + } + + // add new id to end of array of all ids; this seems to match CPython + // important thing is that function arguments are first, but that is + // handled by the compiler because it adds arguments before compiling the body + id_info = &scope->id_info[scope->id_info_len++]; + + id_info->kind = kind; + id_info->flags = 0; + id_info->local_num = 0; + id_info->qst = qst; + return id_info; +} + +id_info_t *scope_find(scope_t *scope, qstr qst) { + for (mp_uint_t i = 0; i < scope->id_info_len; i++) { + if (scope->id_info[i].qst == qst) { + return &scope->id_info[i]; + } + } + return NULL; +} + +id_info_t *scope_find_global(scope_t *scope, qstr qst) { + while (scope->parent != NULL) { + scope = scope->parent; + } + return scope_find(scope, qst); +} + +static void scope_close_over_in_parents(scope_t *scope, qstr qst) { + assert(scope->parent != NULL); // we should have at least 1 parent + for (scope_t *s = scope->parent;; s = s->parent) { + assert(s->parent != NULL); // we should not get to the outer scope + id_info_t *id = scope_find_or_add_id(s, qst, ID_INFO_KIND_UNDECIDED); + if (id->kind == ID_INFO_KIND_UNDECIDED) { + // variable not previously declared in this scope, so declare it as free and keep searching parents + id->kind = ID_INFO_KIND_FREE; + } else { + // variable is declared in this scope, so finish + if (id->kind == ID_INFO_KIND_LOCAL) { + // variable local to this scope, close it over + id->kind = ID_INFO_KIND_CELL; + } else { + // ID_INFO_KIND_FREE: variable already closed over in a parent scope + // ID_INFO_KIND_CELL: variable already closed over in this scope + assert(id->kind == ID_INFO_KIND_FREE || id->kind == ID_INFO_KIND_CELL); + } + return; + } + } +} + +void scope_check_to_close_over(scope_t *scope, id_info_t *id) { + if (scope->parent != NULL) { + for (scope_t *s = scope->parent; s->parent != NULL; s = s->parent) { + id_info_t *id2 = scope_find(s, id->qst); + if (id2 != NULL) { + if (id2->kind == ID_INFO_KIND_LOCAL || id2->kind == ID_INFO_KIND_CELL || id2->kind == ID_INFO_KIND_FREE) { + id->kind = ID_INFO_KIND_FREE; + scope_close_over_in_parents(scope, id->qst); + } + break; + } + } + } +} + +#endif // MICROPY_ENABLE_COMPILER diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/scope.h b/non_catalog_apps/mp_flipper/lib/micropython/py/scope.h new file mode 100644 index 00000000..927c4a7b --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/scope.h @@ -0,0 +1,103 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_SCOPE_H +#define MICROPY_INCLUDED_PY_SCOPE_H + +#include "py/parse.h" +#include "py/emitglue.h" + +typedef enum { + ID_INFO_KIND_UNDECIDED, + ID_INFO_KIND_GLOBAL_IMPLICIT, + ID_INFO_KIND_GLOBAL_IMPLICIT_ASSIGNED, + ID_INFO_KIND_GLOBAL_EXPLICIT, + ID_INFO_KIND_LOCAL, // in a function f, written and only referenced by f + ID_INFO_KIND_CELL, // in a function f, read/written by children of f + ID_INFO_KIND_FREE, // in a function f, belongs to the parent of f +} id_info_kind_t; + +enum { + ID_FLAG_IS_PARAM = 0x01, + ID_FLAG_IS_STAR_PARAM = 0x02, + ID_FLAG_IS_DBL_STAR_PARAM = 0x04, + ID_FLAG_VIPER_TYPE_POS = 4, +}; + +typedef struct _id_info_t { + uint8_t kind; + uint8_t flags; + // when it's an ID_INFO_KIND_LOCAL this is the unique number of the local + // when it's an ID_INFO_KIND_CELL/FREE this is the unique number of the closed over variable + uint16_t local_num; + qstr qst; +} id_info_t; + +#define SCOPE_IS_FUNC_LIKE(s) ((s) >= SCOPE_LAMBDA) +#define SCOPE_IS_COMP_LIKE(s) (SCOPE_LIST_COMP <= (s) && (s) <= SCOPE_GEN_EXPR) + +// scope is a "block" in Python parlance +typedef enum { + SCOPE_MODULE, + SCOPE_CLASS, + SCOPE_LAMBDA, + SCOPE_LIST_COMP, + SCOPE_DICT_COMP, + SCOPE_SET_COMP, + SCOPE_GEN_EXPR, + SCOPE_FUNCTION, +} scope_kind_t; + +typedef struct _scope_t { + scope_kind_t kind; + struct _scope_t *parent; + struct _scope_t *next; + mp_parse_node_t pn; + mp_raw_code_t *raw_code; + #if MICROPY_DEBUG_PRINTERS + size_t raw_code_data_len; // for mp_bytecode_print + #endif + uint16_t simple_name; // a qstr + uint16_t scope_flags; // see runtime0.h + uint16_t emit_options; // see emitglue.h + uint16_t num_pos_args; + uint16_t num_kwonly_args; + uint16_t num_def_pos_args; + uint16_t num_locals; + uint16_t stack_size; // maximum size of the locals stack + uint16_t exc_stack_size; // maximum size of the exception stack + uint16_t id_info_alloc; + uint16_t id_info_len; + id_info_t *id_info; +} scope_t; + +scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, mp_uint_t emit_options); +void scope_free(scope_t *scope); +id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, id_info_kind_t kind); +id_info_t *scope_find(scope_t *scope, qstr qstr); +id_info_t *scope_find_global(scope_t *scope, qstr qstr); +void scope_check_to_close_over(scope_t *scope, id_info_t *id); + +#endif // MICROPY_INCLUDED_PY_SCOPE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/sequence.c b/non_catalog_apps/mp_flipper/lib/micropython/py/sequence.c new file mode 100644 index 00000000..58386074 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/sequence.c @@ -0,0 +1,218 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/runtime.h" + +// Helpers for sequence types + +#define SWAP(type, var1, var2) { type t = var2; var2 = var1; var1 = t; } + +// Implements backend of sequence * integer operation. Assumes elements are +// memory-adjacent in sequence. +void mp_seq_multiply(const void *items, size_t item_sz, size_t len, size_t times, void *dest) { + for (size_t i = 0; i < times; i++) { + size_t copy_sz = item_sz * len; + memcpy(dest, items, copy_sz); + dest = (char *)dest + copy_sz; + } +} + +#if MICROPY_PY_BUILTINS_SLICE + +bool mp_seq_get_fast_slice_indexes(mp_uint_t len, mp_obj_t slice, mp_bound_slice_t *indexes) { + mp_obj_slice_indices(slice, len, indexes); + + // If the index is negative then stop points to the last item, not after it + if (indexes->step < 0) { + indexes->stop++; + } + + // CPython returns empty sequence in such case, or point for assignment is at start + if (indexes->step > 0 && indexes->start > indexes->stop) { + indexes->stop = indexes->start; + } else if (indexes->step < 0 && indexes->start < indexes->stop) { + indexes->stop = indexes->start + 1; + } + + return indexes->step == 1; +} + +#endif + +mp_obj_t mp_seq_extract_slice(size_t len, const mp_obj_t *seq, mp_bound_slice_t *indexes) { + (void)len; // TODO can we remove len from the arg list? + + mp_int_t start = indexes->start, stop = indexes->stop; + mp_int_t step = indexes->step; + + mp_obj_t res = mp_obj_new_list(0, NULL); + + if (step < 0) { + while (start >= stop) { + mp_obj_list_append(res, seq[start]); + start += step; + } + } else { + while (start < stop) { + mp_obj_list_append(res, seq[start]); + start += step; + } + } + return res; +} + +// Special-case comparison function for sequences of bytes +// Don't pass MP_BINARY_OP_NOT_EQUAL here +bool mp_seq_cmp_bytes(mp_uint_t op, const byte *data1, size_t len1, const byte *data2, size_t len2) { + if (op == MP_BINARY_OP_EQUAL && len1 != len2) { + return false; + } + + // Let's deal only with > & >= + if (op == MP_BINARY_OP_LESS || op == MP_BINARY_OP_LESS_EQUAL) { + SWAP(const byte *, data1, data2); + SWAP(size_t, len1, len2); + if (op == MP_BINARY_OP_LESS) { + op = MP_BINARY_OP_MORE; + } else { + op = MP_BINARY_OP_MORE_EQUAL; + } + } + size_t min_len = len1 < len2 ? len1 : len2; + int res = memcmp(data1, data2, min_len); + if (op == MP_BINARY_OP_EQUAL) { + // If we are checking for equality, here's the answer + return res == 0; + } + if (res < 0) { + return false; + } + if (res > 0) { + return true; + } + + // If we had tie in the last element... + // ... and we have lists of different lengths... + if (len1 != len2) { + if (len1 < len2) { + // ... then longer list length wins (we deal only with >) + return false; + } + } else if (op == MP_BINARY_OP_MORE) { + // Otherwise, if we have strict relation, equality means failure + return false; + } + return true; +} + +// Special-case comparison function for sequences of mp_obj_t +// Don't pass MP_BINARY_OP_NOT_EQUAL here +bool mp_seq_cmp_objs(mp_uint_t op, const mp_obj_t *items1, size_t len1, const mp_obj_t *items2, size_t len2) { + if (op == MP_BINARY_OP_EQUAL && len1 != len2) { + return false; + } + + // Let's deal only with > & >= + if (op == MP_BINARY_OP_LESS || op == MP_BINARY_OP_LESS_EQUAL) { + SWAP(const mp_obj_t *, items1, items2); + SWAP(size_t, len1, len2); + if (op == MP_BINARY_OP_LESS) { + op = MP_BINARY_OP_MORE; + } else { + op = MP_BINARY_OP_MORE_EQUAL; + } + } + + size_t len = len1 < len2 ? len1 : len2; + for (size_t i = 0; i < len; i++) { + // If current elements equal, can't decide anything - go on + if (mp_obj_equal(items1[i], items2[i])) { + continue; + } + + // Otherwise, if they are not equal, we can have final decision based on them + if (op == MP_BINARY_OP_EQUAL) { + // In particular, if we are checking for equality, here're the answer + return false; + } + + // Otherwise, application of relation op gives the answer + return mp_binary_op(op, items1[i], items2[i]) == mp_const_true; + } + + // If we had tie in the last element... + // ... and we have lists of different lengths... + if (len1 != len2) { + if (len1 < len2) { + // ... then longer list length wins (we deal only with >) + return false; + } + } else if (op == MP_BINARY_OP_MORE) { + // Otherwise, if we have strict relation, sequence equality means failure + return false; + } + + return true; +} + +// Special-case of index() which searches for mp_obj_t +mp_obj_t mp_seq_index_obj(const mp_obj_t *items, size_t len, size_t n_args, const mp_obj_t *args) { + const mp_obj_type_t *type = mp_obj_get_type(args[0]); + mp_obj_t value = args[1]; + size_t start = 0; + size_t stop = len; + + if (n_args >= 3) { + start = mp_get_index(type, len, args[2], true); + if (n_args >= 4) { + stop = mp_get_index(type, len, args[3], true); + } + } + + for (size_t i = start; i < stop; i++) { + if (mp_obj_equal(items[i], value)) { + // Common sense says this cannot overflow small int + return MP_OBJ_NEW_SMALL_INT(i); + } + } + + mp_raise_ValueError(MP_ERROR_TEXT("object not in sequence")); +} + +mp_obj_t mp_seq_count_obj(const mp_obj_t *items, size_t len, mp_obj_t value) { + size_t count = 0; + for (size_t i = 0; i < len; i++) { + if (mp_obj_equal(items[i], value)) { + count++; + } + } + + // Common sense says this cannot overflow small int + return MP_OBJ_NEW_SMALL_INT(count); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/showbc.c b/non_catalog_apps/mp_flipper/lib/micropython/py/showbc.c new file mode 100644 index 00000000..6913d18c --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/showbc.c @@ -0,0 +1,562 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/bc0.h" +#include "py/emitglue.h" + +#if MICROPY_DEBUG_PRINTERS + +#define DECODE_UINT { \ + unum = 0; \ + do { \ + unum = (unum << 7) + (*ip & 0x7f); \ + } while ((*ip++ & 0x80) != 0); \ +} + +#define DECODE_ULABEL \ + do { \ + if (ip[0] & 0x80) { \ + unum = ((ip[0] & 0x7f) | (ip[1] << 7)); \ + ip += 2; \ + } else { \ + unum = ip[0]; \ + ip += 1; \ + } \ + } while (0) + +#define DECODE_SLABEL \ + do { \ + if (ip[0] & 0x80) { \ + unum = ((ip[0] & 0x7f) | (ip[1] << 7)) - 0x4000; \ + ip += 2; \ + } else { \ + unum = ip[0] - 0x40; \ + ip += 1; \ + } \ + } while (0) + +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + +#define DECODE_QSTR \ + DECODE_UINT; \ + qst = qstr_table[unum] + +#else + +#define DECODE_QSTR \ + DECODE_UINT; \ + qst = unum; + +#endif + +#define DECODE_PTR \ + DECODE_UINT; \ + unum = (mp_uint_t)(uintptr_t)child_table[unum] + +#define DECODE_OBJ \ + DECODE_UINT; \ + unum = (mp_uint_t)obj_table[unum] + +void mp_bytecode_print(const mp_print_t *print, const mp_raw_code_t *rc, size_t fun_data_len, const mp_module_constants_t *cm) { + const byte *ip_start = rc->fun_data; + const byte *ip = rc->fun_data; + + // Decode prelude + MP_BC_PRELUDE_SIG_DECODE(ip); + MP_BC_PRELUDE_SIZE_DECODE(ip); + const byte *code_info = ip; + + qstr block_name = mp_decode_uint(&code_info); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + block_name = cm->qstr_table[block_name]; + qstr source_file = cm->qstr_table[0]; + #else + qstr source_file = cm->source_file; + #endif + mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p %u bytes)\n", + qstr_str(source_file), qstr_str(block_name), rc, ip_start, (unsigned)fun_data_len); + + // raw bytecode dump + size_t prelude_size = ip - ip_start + n_info + n_cell; + mp_printf(print, "Raw bytecode (code_info_size=%u, bytecode_size=%u):\n", + (unsigned)prelude_size, (unsigned)(fun_data_len - prelude_size)); + for (size_t i = 0; i < fun_data_len; i++) { + if (i > 0 && i % 16 == 0) { + mp_printf(print, "\n"); + } + mp_printf(print, " %02x", ip_start[i]); + } + mp_printf(print, "\n"); + + // bytecode prelude: arg names (as qstr objects) + mp_printf(print, "arg names:"); + for (mp_uint_t i = 0; i < n_pos_args + n_kwonly_args; i++) { + qstr qst = mp_decode_uint(&code_info); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + qst = cm->qstr_table[qst]; + #endif + mp_printf(print, " %s", qstr_str(qst)); + } + mp_printf(print, "\n"); + + mp_printf(print, "(N_STATE %u)\n", (unsigned)n_state); + mp_printf(print, "(N_EXC_STACK %u)\n", (unsigned)n_exc_stack); + + // skip over code_info + ip += n_info; + const byte *line_info_top = ip; + + // bytecode prelude: initialise closed over variables + for (size_t i = 0; i < n_cell; ++i) { + uint local_num = *ip++; + mp_printf(print, "(INIT_CELL %u)\n", local_num); + } + + // print out line number info + { + mp_int_t bc = 0; + mp_uint_t source_line = 1; + mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); + for (const byte *ci = code_info; ci < line_info_top;) { + if ((ci[0] & 0x80) == 0) { + // 0b0LLBBBBB encoding + bc += ci[0] & 0x1f; + source_line += ci[0] >> 5; + ci += 1; + } else { + // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + bc += ci[0] & 0xf; + source_line += ((ci[0] << 4) & 0x700) | ci[1]; + ci += 2; + } + mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); + } + } + mp_bytecode_print2(print, ip, fun_data_len - prelude_size, rc->children, cm); +} + +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, const byte *ip, mp_raw_code_t *const *child_table, const mp_module_constants_t *cm) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + const qstr_short_t *qstr_table = cm->qstr_table; + #endif + const mp_obj_t *obj_table = cm->obj_table; + mp_uint_t unum; + qstr qst; + + switch (*ip++) { + case MP_BC_LOAD_CONST_FALSE: + mp_printf(print, "LOAD_CONST_FALSE"); + break; + + case MP_BC_LOAD_CONST_NONE: + mp_printf(print, "LOAD_CONST_NONE"); + break; + + case MP_BC_LOAD_CONST_TRUE: + mp_printf(print, "LOAD_CONST_TRUE"); + break; + + case MP_BC_LOAD_CONST_SMALL_INT: { + mp_int_t num = 0; + if ((ip[0] & 0x40) != 0) { + // Number is negative + num--; + } + do { + num = ((mp_uint_t)num << 7) | (*ip & 0x7f); + } while ((*ip++ & 0x80) != 0); + mp_printf(print, "LOAD_CONST_SMALL_INT " INT_FMT, num); + break; + } + + case MP_BC_LOAD_CONST_STRING: + DECODE_QSTR; + mp_printf(print, "LOAD_CONST_STRING '%s'", qstr_str(qst)); + break; + + case MP_BC_LOAD_CONST_OBJ: + DECODE_OBJ; + mp_printf(print, "LOAD_CONST_OBJ %p=", MP_OBJ_TO_PTR(unum)); + mp_obj_print_helper(print, (mp_obj_t)unum, PRINT_REPR); + break; + + case MP_BC_LOAD_NULL: + mp_printf(print, "LOAD_NULL"); + break; + + case MP_BC_LOAD_FAST_N: + DECODE_UINT; + mp_printf(print, "LOAD_FAST_N " UINT_FMT, unum); + break; + + case MP_BC_LOAD_DEREF: + DECODE_UINT; + mp_printf(print, "LOAD_DEREF " UINT_FMT, unum); + break; + + case MP_BC_LOAD_NAME: + DECODE_QSTR; + mp_printf(print, "LOAD_NAME %s", qstr_str(qst)); + break; + + case MP_BC_LOAD_GLOBAL: + DECODE_QSTR; + mp_printf(print, "LOAD_GLOBAL %s", qstr_str(qst)); + break; + + case MP_BC_LOAD_ATTR: + DECODE_QSTR; + mp_printf(print, "LOAD_ATTR %s", qstr_str(qst)); + break; + + case MP_BC_LOAD_METHOD: + DECODE_QSTR; + mp_printf(print, "LOAD_METHOD %s", qstr_str(qst)); + break; + + case MP_BC_LOAD_SUPER_METHOD: + DECODE_QSTR; + mp_printf(print, "LOAD_SUPER_METHOD %s", qstr_str(qst)); + break; + + case MP_BC_LOAD_BUILD_CLASS: + mp_printf(print, "LOAD_BUILD_CLASS"); + break; + + case MP_BC_LOAD_SUBSCR: + mp_printf(print, "LOAD_SUBSCR"); + break; + + case MP_BC_STORE_FAST_N: + DECODE_UINT; + mp_printf(print, "STORE_FAST_N " UINT_FMT, unum); + break; + + case MP_BC_STORE_DEREF: + DECODE_UINT; + mp_printf(print, "STORE_DEREF " UINT_FMT, unum); + break; + + case MP_BC_STORE_NAME: + DECODE_QSTR; + mp_printf(print, "STORE_NAME %s", qstr_str(qst)); + break; + + case MP_BC_STORE_GLOBAL: + DECODE_QSTR; + mp_printf(print, "STORE_GLOBAL %s", qstr_str(qst)); + break; + + case MP_BC_STORE_ATTR: + DECODE_QSTR; + mp_printf(print, "STORE_ATTR %s", qstr_str(qst)); + break; + + case MP_BC_STORE_SUBSCR: + mp_printf(print, "STORE_SUBSCR"); + break; + + case MP_BC_DELETE_FAST: + DECODE_UINT; + mp_printf(print, "DELETE_FAST " UINT_FMT, unum); + break; + + case MP_BC_DELETE_DEREF: + DECODE_UINT; + mp_printf(print, "DELETE_DEREF " UINT_FMT, unum); + break; + + case MP_BC_DELETE_NAME: + DECODE_QSTR; + mp_printf(print, "DELETE_NAME %s", qstr_str(qst)); + break; + + case MP_BC_DELETE_GLOBAL: + DECODE_QSTR; + mp_printf(print, "DELETE_GLOBAL %s", qstr_str(qst)); + break; + + case MP_BC_DUP_TOP: + mp_printf(print, "DUP_TOP"); + break; + + case MP_BC_DUP_TOP_TWO: + mp_printf(print, "DUP_TOP_TWO"); + break; + + case MP_BC_POP_TOP: + mp_printf(print, "POP_TOP"); + break; + + case MP_BC_ROT_TWO: + mp_printf(print, "ROT_TWO"); + break; + + case MP_BC_ROT_THREE: + mp_printf(print, "ROT_THREE"); + break; + + case MP_BC_JUMP: + DECODE_SLABEL; + mp_printf(print, "JUMP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_POP_JUMP_IF_TRUE: + DECODE_SLABEL; + mp_printf(print, "POP_JUMP_IF_TRUE " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_POP_JUMP_IF_FALSE: + DECODE_SLABEL; + mp_printf(print, "POP_JUMP_IF_FALSE " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_JUMP_IF_TRUE_OR_POP: + DECODE_ULABEL; + mp_printf(print, "JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_JUMP_IF_FALSE_OR_POP: + DECODE_ULABEL; + mp_printf(print, "JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_SETUP_WITH: + DECODE_ULABEL; // loop-like labels are always forward + mp_printf(print, "SETUP_WITH " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_WITH_CLEANUP: + mp_printf(print, "WITH_CLEANUP"); + break; + + case MP_BC_UNWIND_JUMP: + DECODE_SLABEL; + mp_printf(print, "UNWIND_JUMP " UINT_FMT " %d", (mp_uint_t)(ip + unum - ip_start), *ip); + ip += 1; + break; + + case MP_BC_SETUP_EXCEPT: + DECODE_ULABEL; // except labels are always forward + mp_printf(print, "SETUP_EXCEPT " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_SETUP_FINALLY: + DECODE_ULABEL; // except labels are always forward + mp_printf(print, "SETUP_FINALLY " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_END_FINALLY: + // if TOS is an exception, reraises the exception (3 values on TOS) + // if TOS is an integer, does something else + // if TOS is None, just pops it and continues + // else error + mp_printf(print, "END_FINALLY"); + break; + + case MP_BC_GET_ITER: + mp_printf(print, "GET_ITER"); + break; + + case MP_BC_GET_ITER_STACK: + mp_printf(print, "GET_ITER_STACK"); + break; + + case MP_BC_FOR_ITER: + DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward + mp_printf(print, "FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_POP_EXCEPT_JUMP: + DECODE_ULABEL; // these labels are always forward + mp_printf(print, "POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); + break; + + case MP_BC_BUILD_TUPLE: + DECODE_UINT; + mp_printf(print, "BUILD_TUPLE " UINT_FMT, unum); + break; + + case MP_BC_BUILD_LIST: + DECODE_UINT; + mp_printf(print, "BUILD_LIST " UINT_FMT, unum); + break; + + case MP_BC_BUILD_MAP: + DECODE_UINT; + mp_printf(print, "BUILD_MAP " UINT_FMT, unum); + break; + + case MP_BC_STORE_MAP: + mp_printf(print, "STORE_MAP"); + break; + + case MP_BC_BUILD_SET: + DECODE_UINT; + mp_printf(print, "BUILD_SET " UINT_FMT, unum); + break; + + #if MICROPY_PY_BUILTINS_SLICE + case MP_BC_BUILD_SLICE: + DECODE_UINT; + mp_printf(print, "BUILD_SLICE " UINT_FMT, unum); + break; + #endif + + case MP_BC_STORE_COMP: + DECODE_UINT; + mp_printf(print, "STORE_COMP " UINT_FMT, unum); + break; + + case MP_BC_UNPACK_SEQUENCE: + DECODE_UINT; + mp_printf(print, "UNPACK_SEQUENCE " UINT_FMT, unum); + break; + + case MP_BC_UNPACK_EX: + DECODE_UINT; + mp_printf(print, "UNPACK_EX " UINT_FMT, unum); + break; + + case MP_BC_MAKE_FUNCTION: + DECODE_PTR; + mp_printf(print, "MAKE_FUNCTION %p", (void *)(uintptr_t)unum); + break; + + case MP_BC_MAKE_FUNCTION_DEFARGS: + DECODE_PTR; + mp_printf(print, "MAKE_FUNCTION_DEFARGS %p", (void *)(uintptr_t)unum); + break; + + case MP_BC_MAKE_CLOSURE: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + mp_printf(print, "MAKE_CLOSURE %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); + break; + } + + case MP_BC_MAKE_CLOSURE_DEFARGS: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + mp_printf(print, "MAKE_CLOSURE_DEFARGS %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); + break; + } + + case MP_BC_CALL_FUNCTION: + DECODE_UINT; + mp_printf(print, "CALL_FUNCTION n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + break; + + case MP_BC_CALL_FUNCTION_VAR_KW: + DECODE_UINT; + mp_printf(print, "CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD: + DECODE_UINT; + mp_printf(print, "CALL_METHOD n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD_VAR_KW: + DECODE_UINT; + mp_printf(print, "CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + break; + + case MP_BC_RETURN_VALUE: + mp_printf(print, "RETURN_VALUE"); + break; + + case MP_BC_RAISE_LAST: + mp_printf(print, "RAISE_LAST"); + break; + + case MP_BC_RAISE_OBJ: + mp_printf(print, "RAISE_OBJ"); + break; + + case MP_BC_RAISE_FROM: + mp_printf(print, "RAISE_FROM"); + break; + + case MP_BC_YIELD_VALUE: + mp_printf(print, "YIELD_VALUE"); + break; + + case MP_BC_YIELD_FROM: + mp_printf(print, "YIELD_FROM"); + break; + + case MP_BC_IMPORT_NAME: + DECODE_QSTR; + mp_printf(print, "IMPORT_NAME '%s'", qstr_str(qst)); + break; + + case MP_BC_IMPORT_FROM: + DECODE_QSTR; + mp_printf(print, "IMPORT_FROM '%s'", qstr_str(qst)); + break; + + case MP_BC_IMPORT_STAR: + mp_printf(print, "IMPORT_STAR"); + break; + + default: + if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + 64) { + mp_printf(print, "LOAD_CONST_SMALL_INT " INT_FMT, (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16); + } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + 16) { + mp_printf(print, "LOAD_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI); + } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { + mp_printf(print, "STORE_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI); + } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) { + mp_uint_t op = ip[-1] - MP_BC_UNARY_OP_MULTI; + mp_printf(print, "UNARY_OP " UINT_FMT " %s", op, qstr_str(mp_unary_op_method_name[op])); + } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) { + mp_uint_t op = ip[-1] - MP_BC_BINARY_OP_MULTI; + mp_printf(print, "BINARY_OP " UINT_FMT " %s", op, qstr_str(mp_binary_op_method_name[op])); + } else { + mp_printf(print, "code %p, byte code 0x%02x not implemented\n", ip - 1, ip[-1]); + assert(0); + return ip; + } + break; + } + + return ip; +} + +void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, mp_raw_code_t *const *child_table, const mp_module_constants_t *cm) { + const byte *ip_start = ip; + while (ip < ip_start + len) { + mp_printf(print, "%02u ", (uint)(ip - ip_start)); + ip = mp_bytecode_print_str(print, ip_start, ip, child_table, cm); + mp_printf(print, "\n"); + } +} + +#endif // MICROPY_DEBUG_PRINTERS diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/smallint.c b/non_catalog_apps/mp_flipper/lib/micropython/py/smallint.c new file mode 100644 index 00000000..aa542ca7 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/smallint.c @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/smallint.h" + +bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y) { + // Check for multiply overflow; see CERT INT32-C + if (x > 0) { // x is positive + if (y > 0) { // x and y are positive + if (x > (MP_SMALL_INT_MAX / y)) { + return true; + } + } else { // x positive, y nonpositive + if (y < (MP_SMALL_INT_MIN / x)) { + return true; + } + } // x positive, y nonpositive + } else { // x is nonpositive + if (y > 0) { // x is nonpositive, y is positive + if (x < (MP_SMALL_INT_MIN / y)) { + return true; + } + } else { // x and y are nonpositive + if (x != 0 && y < (MP_SMALL_INT_MAX / x)) { + return true; + } + } // End if x and y are nonpositive + } // End if x is nonpositive + return false; +} + +mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor) { + // Python specs require that mod has same sign as second operand + dividend %= divisor; + if ((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) { + dividend += divisor; + } + return dividend; +} + +mp_int_t mp_small_int_floor_divide(mp_int_t num, mp_int_t denom) { + if (num >= 0) { + if (denom < 0) { + num += -denom - 1; + } + } else { + if (denom >= 0) { + num += -denom + 1; + } + } + return num / denom; +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/smallint.h b/non_catalog_apps/mp_flipper/lib/micropython/py/smallint.h new file mode 100644 index 00000000..584e0018 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/smallint.h @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_SMALLINT_H +#define MICROPY_INCLUDED_PY_SMALLINT_H + +#include "py/mpconfig.h" +#include "py/misc.h" + +// Functions for small integer arithmetic + +#ifndef MP_SMALL_INT_MIN + +// In SMALL_INT, next-to-highest bits is used as sign, so both must match for value in range +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + +#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)MP_OBJ_WORD_MSBIT_HIGH) >> 1)) +#define MP_SMALL_INT_FITS(n) ((((n) ^ ((mp_uint_t)(n) << 1)) & MP_OBJ_WORD_MSBIT_HIGH) == 0) +// Mask to truncate mp_int_t to positive value +#define MP_SMALL_INT_POSITIVE_MASK ~(MP_OBJ_WORD_MSBIT_HIGH | (MP_OBJ_WORD_MSBIT_HIGH >> 1)) + +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B + +#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)MP_OBJ_WORD_MSBIT_HIGH) >> 2)) +#define MP_SMALL_INT_FITS(n) ((((n) & MP_SMALL_INT_MIN) == 0) || (((n) & MP_SMALL_INT_MIN) == MP_SMALL_INT_MIN)) +// Mask to truncate mp_int_t to positive value +#define MP_SMALL_INT_POSITIVE_MASK ~(MP_OBJ_WORD_MSBIT_HIGH | (MP_OBJ_WORD_MSBIT_HIGH >> 1) | (MP_OBJ_WORD_MSBIT_HIGH >> 2)) + +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + +#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)0xffff800000000000) >> 1)) +#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & 0xffff800000000000) == 0) +// Mask to truncate mp_int_t to positive value +#define MP_SMALL_INT_POSITIVE_MASK ~(0xffff800000000000 | (0xffff800000000000 >> 1)) + +#endif + +#endif + +#define MP_SMALL_INT_MAX ((mp_int_t)(~(MP_SMALL_INT_MIN))) + +// https://stackoverflow.com/a/4589384/1976323 +// Number of bits in inttype_MAX, or in any (1<= 13 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdangling-pointer" + #endif + volatile int stack_dummy; + MP_STATE_THREAD(stack_top) = (char *)&stack_dummy; + #if __GNUC__ >= 13 + #pragma GCC diagnostic pop + #endif +} + +void mp_stack_set_top(void *top) { + MP_STATE_THREAD(stack_top) = top; +} + +mp_uint_t mp_stack_usage(void) { + // Assumes descending stack + volatile int stack_dummy; + return MP_STATE_THREAD(stack_top) - (char *)&stack_dummy; +} + +#if MICROPY_STACK_CHECK + +void mp_stack_set_limit(mp_uint_t limit) { + MP_STATE_THREAD(stack_limit) = limit; +} + +void mp_stack_check(void) { + if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { + mp_raise_recursion_depth(); + } +} + +#endif // MICROPY_STACK_CHECK diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/stackctrl.h b/non_catalog_apps/mp_flipper/lib/micropython/py/stackctrl.h new file mode 100644 index 00000000..c21288b2 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/stackctrl.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Paul Sokolovsky + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_STACKCTRL_H +#define MICROPY_INCLUDED_PY_STACKCTRL_H + +#include "py/mpconfig.h" + +void mp_stack_ctrl_init(void); +void mp_stack_set_top(void *top); +mp_uint_t mp_stack_usage(void); + +#if MICROPY_STACK_CHECK + +void mp_stack_set_limit(mp_uint_t limit); +void mp_stack_check(void); +#define MP_STACK_CHECK() mp_stack_check() + +#else + +#define mp_stack_set_limit(limit) (void)(limit) +#define MP_STACK_CHECK() + +#endif + +#endif // MICROPY_INCLUDED_PY_STACKCTRL_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/stream.c b/non_catalog_apps/mp_flipper/lib/micropython/py/stream.c new file mode 100644 index 00000000..d7a8881e --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/stream.c @@ -0,0 +1,575 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * Copyright (c) 2014-2016 Paul Sokolovsky + * + * 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 "py/objstr.h" +#include "py/stream.h" +#include "py/runtime.h" + +// This file defines generic Python stream read/write methods which +// dispatch to the underlying stream interface of an object. + +// TODO: should be in mpconfig.h +#define DEFAULT_BUFFER_SIZE 256 + +static mp_obj_t stream_readall(mp_obj_t self_in); + +// Returns error condition in *errcode, if non-zero, return value is number of bytes written +// before error condition occurred. If *errcode == 0, returns total bytes written (which will +// be equal to input size). +mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode, byte flags) { + byte *buf = buf_; + typedef mp_uint_t (*io_func_t)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode); + io_func_t io_func; + const mp_stream_p_t *stream_p = mp_get_stream(stream); + if (flags & MP_STREAM_RW_WRITE) { + io_func = (io_func_t)stream_p->write; + } else { + io_func = stream_p->read; + } + + *errcode = 0; + mp_uint_t done = 0; + while (size > 0) { + mp_uint_t out_sz = io_func(stream, buf, size, errcode); + // For read, out_sz == 0 means EOF. For write, it's unspecified + // what it means, but we don't make any progress, so returning + // is still the best option. + if (out_sz == 0) { + return done; + } + if (out_sz == MP_STREAM_ERROR) { + // If we read something before getting EAGAIN, don't leak it + if (mp_is_nonblocking_error(*errcode) && done != 0) { + *errcode = 0; + } + return done; + } + if (flags & MP_STREAM_RW_ONCE) { + return out_sz; + } + + buf += out_sz; + size -= out_sz; + done += out_sz; + } + return done; +} + +mp_off_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) { + struct mp_stream_seek_t seek_s; + seek_s.offset = offset; + seek_s.whence = whence; + const mp_stream_p_t *stream_p = mp_get_stream(stream); + mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, errcode); + if (res == MP_STREAM_ERROR) { + return (mp_off_t)-1; + } + return seek_s.offset; +} + +const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { + const mp_obj_type_t *type = mp_obj_get_type(self_in); + if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) { + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(type, protocol); + if (!((flags & MP_STREAM_OP_READ) && stream_p->read == NULL) + && !((flags & MP_STREAM_OP_WRITE) && stream_p->write == NULL) + && !((flags & MP_STREAM_OP_IOCTL) && stream_p->ioctl == NULL)) { + return stream_p; + } + } + // CPython: io.UnsupportedOperation, OSError subclass + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported")); +} + +static mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) { + // What to do if sz < -1? Python docs don't specify this case. + // CPython does a readall, but here we silently let negatives through, + // and they will cause a MemoryError. + mp_int_t sz; + if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) { + return stream_readall(args[0]); + } + + const mp_stream_p_t *stream_p = mp_get_stream(args[0]); + + #if MICROPY_PY_BUILTINS_STR_UNICODE + if (stream_p->is_text) { + // We need to read sz number of unicode characters. Because we don't have any + // buffering, and because the stream API can only read bytes, we must read here + // in units of bytes and must never over read. If we want sz chars, then reading + // sz bytes will never over-read, so we follow this approach, in a loop to keep + // reading until we have exactly enough chars. This will be 1 read for text + // with ASCII-only chars, and about 2 reads for text with a couple of non-ASCII + // chars. For text with lots of non-ASCII chars, it'll be pretty inefficient + // in time and memory. + + vstr_t vstr; + vstr_init(&vstr, sz); + mp_uint_t more_bytes = sz; + mp_uint_t last_buf_offset = 0; + while (more_bytes > 0) { + char *p = vstr_add_len(&vstr, more_bytes); + int error; + mp_uint_t out_sz = mp_stream_read_exactly(args[0], p, more_bytes, &error); + if (error != 0) { + vstr_cut_tail_bytes(&vstr, more_bytes); + if (mp_is_nonblocking_error(error)) { + // With non-blocking streams, we read as much as we can. + // If we read nothing, return None, just like read(). + // Otherwise, return data read so far. + // TODO what if we have read only half a non-ASCII char? + if (vstr.len == 0) { + vstr_clear(&vstr); + return mp_const_none; + } + break; + } + mp_raise_OSError(error); + } + + if (out_sz < more_bytes) { + // Finish reading. + // TODO what if we have read only half a non-ASCII char? + vstr_cut_tail_bytes(&vstr, more_bytes - out_sz); + if (out_sz == 0) { + break; + } + } + + // count chars from bytes just read + for (mp_uint_t off = last_buf_offset;;) { + byte b = vstr.buf[off]; + int n; + if (!UTF8_IS_NONASCII(b)) { + // 1-byte ASCII char + n = 1; + } else if ((b & 0xe0) == 0xc0) { + // 2-byte char + n = 2; + } else if ((b & 0xf0) == 0xe0) { + // 3-byte char + n = 3; + } else if ((b & 0xf8) == 0xf0) { + // 4-byte char + n = 4; + } else { + // TODO + n = 5; + } + if (off + n <= vstr.len) { + // got a whole char in n bytes + off += n; + sz -= 1; + last_buf_offset = off; + if (off >= vstr.len) { + more_bytes = sz; + break; + } + } else { + // didn't get a whole char, so work out how many extra bytes are needed for + // this partial char, plus bytes for additional chars that we want + more_bytes = (off + n - vstr.len) + (sz - 1); + break; + } + } + } + + return mp_obj_new_str_from_vstr(&vstr); + } + #endif + + vstr_t vstr; + vstr_init_len(&vstr, sz); + int error; + mp_uint_t out_sz = mp_stream_rw(args[0], vstr.buf, sz, &error, flags); + if (error != 0) { + vstr_clear(&vstr); + if (mp_is_nonblocking_error(error)) { + // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read + // "If the object is in non-blocking mode and no bytes are available, + // None is returned." + // This is actually very weird, as naive truth check will treat + // this as EOF. + return mp_const_none; + } + mp_raise_OSError(error); + } else { + vstr.len = out_sz; + if (stream_p->is_text) { + return mp_obj_new_str_from_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } + } +} + +static mp_obj_t stream_read(size_t n_args, const mp_obj_t *args) { + return stream_read_generic(n_args, args, MP_STREAM_RW_READ); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj, 1, 2, stream_read); + +static mp_obj_t stream_read1(size_t n_args, const mp_obj_t *args) { + return stream_read_generic(n_args, args, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read1_obj, 1, 2, stream_read1); + +mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte flags) { + int error; + mp_uint_t out_sz = mp_stream_rw(self_in, (void *)buf, len, &error, flags); + if (error != 0) { + if (mp_is_nonblocking_error(error)) { + // http://docs.python.org/3/library/io.html#io.RawIOBase.write + // "None is returned if the raw stream is set not to block and + // no single byte could be readily written to it." + return mp_const_none; + } + mp_raise_OSError(error); + } else { + return MP_OBJ_NEW_SMALL_INT(out_sz); + } +} + +// This is used to adapt a stream object to an mp_print_t interface +void mp_stream_write_adaptor(void *self, const char *buf, size_t len) { + mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len, MP_STREAM_RW_WRITE); +} + +static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + size_t max_len = (size_t)-1; + size_t off = 0; + if (n_args == 3) { + max_len = mp_obj_get_int_truncated(args[2]); + } else if (n_args == 4) { + off = mp_obj_get_int_truncated(args[2]); + max_len = mp_obj_get_int_truncated(args[3]); + if (off > bufinfo.len) { + off = bufinfo.len; + } + } + bufinfo.len -= off; + return mp_stream_write(args[0], (byte *)bufinfo.buf + off, MIN(bufinfo.len, max_len), MP_STREAM_RW_WRITE); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj, 2, 4, stream_write_method); + +static mp_obj_t stream_write1_method(mp_obj_t self_in, mp_obj_t arg) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); + return mp_stream_write(self_in, bufinfo.buf, bufinfo.len, MP_STREAM_RW_WRITE | MP_STREAM_RW_ONCE); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_write1_obj, stream_write1_method); + +static mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + + // CPython extension: if 2nd arg is provided, that's max len to read, + // instead of full buffer. Similar to + // https://docs.python.org/3/library/socket.html#socket.socket.recv_into + mp_uint_t len = bufinfo.len; + if (n_args > 2) { + len = mp_obj_get_int(args[2]); + if (len > bufinfo.len) { + len = bufinfo.len; + } + } + + int error; + mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); + if (error != 0) { + if (mp_is_nonblocking_error(error)) { + return mp_const_none; + } + mp_raise_OSError(error); + } else { + return MP_OBJ_NEW_SMALL_INT(out_sz); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj, 2, 3, stream_readinto); + +static mp_obj_t stream_readall(mp_obj_t self_in) { + const mp_stream_p_t *stream_p = mp_get_stream(self_in); + + mp_uint_t total_size = 0; + vstr_t vstr; + vstr_init(&vstr, DEFAULT_BUFFER_SIZE); + char *p = vstr.buf; + mp_uint_t current_read = DEFAULT_BUFFER_SIZE; + while (true) { + int error; + mp_uint_t out_sz = stream_p->read(self_in, p, current_read, &error); + if (out_sz == MP_STREAM_ERROR) { + if (mp_is_nonblocking_error(error)) { + // With non-blocking streams, we read as much as we can. + // If we read nothing, return None, just like read(). + // Otherwise, return data read so far. + if (total_size == 0) { + return mp_const_none; + } + break; + } + mp_raise_OSError(error); + } + if (out_sz == 0) { + break; + } + total_size += out_sz; + if (out_sz < current_read) { + current_read -= out_sz; + p += out_sz; + } else { + p = vstr_extend(&vstr, DEFAULT_BUFFER_SIZE); + current_read = DEFAULT_BUFFER_SIZE; + } + } + + vstr.len = total_size; + if (stream_p->is_text) { + return mp_obj_new_str_from_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } +} + +// Unbuffered, inefficient implementation of readline() for raw I/O files. +static mp_obj_t stream_unbuffered_readline(size_t n_args, const mp_obj_t *args) { + const mp_stream_p_t *stream_p = mp_get_stream(args[0]); + + mp_int_t max_size = -1; + if (n_args > 1) { + max_size = MP_OBJ_SMALL_INT_VALUE(args[1]); + } + + vstr_t vstr; + if (max_size != -1) { + vstr_init(&vstr, max_size); + } else { + vstr_init(&vstr, 16); + } + + while (max_size == -1 || max_size-- != 0) { + char *p = vstr_add_len(&vstr, 1); + int error; + mp_uint_t out_sz = stream_p->read(args[0], p, 1, &error); + if (out_sz == MP_STREAM_ERROR) { + if (mp_is_nonblocking_error(error)) { + if (vstr.len == 1) { + // We just incremented it, but otherwise we read nothing + // and immediately got EAGAIN. This case is not well + // specified in + // https://docs.python.org/3/library/io.html#io.IOBase.readline + // unlike similar case for read(). But we follow the latter's + // behavior - return None. + vstr_clear(&vstr); + return mp_const_none; + } else { + goto done; + } + } + mp_raise_OSError(error); + } + if (out_sz == 0) { + done: + // Back out previously added byte + // Consider, what's better - read a char and get OutOfMemory (so read + // char is lost), or allocate first as we do. + vstr_cut_tail_bytes(&vstr, 1); + break; + } + if (*p == '\n') { + break; + } + } + + if (stream_p->is_text) { + return mp_obj_new_str_from_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj, 1, 2, stream_unbuffered_readline); + +// TODO take an optional extra argument (what does it do exactly?) +static mp_obj_t stream_unbuffered_readlines(mp_obj_t self) { + mp_obj_t lines = mp_obj_new_list(0, NULL); + for (;;) { + mp_obj_t line = stream_unbuffered_readline(1, &self); + if (!mp_obj_is_true(line)) { + break; + } + mp_obj_list_append(lines, line); + } + return lines; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj, stream_unbuffered_readlines); + +mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) { + mp_obj_t l_in = stream_unbuffered_readline(1, &self); + if (mp_obj_is_true(l_in)) { + return l_in; + } + return MP_OBJ_STOP_ITERATION; +} + +mp_obj_t mp_stream_close(mp_obj_t stream) { + const mp_stream_p_t *stream_p = mp_get_stream(stream); + int error; + mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); + if (res == MP_STREAM_ERROR) { + mp_raise_OSError(error); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_close_obj, mp_stream_close); + +static mp_obj_t mp_stream___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + return mp_stream_close(args[0]); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream___exit___obj, 4, 4, mp_stream___exit__); + +static mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) { + // TODO: Could be uint64 + mp_off_t offset = mp_obj_get_int(args[1]); + int whence = SEEK_SET; + if (n_args == 3) { + whence = mp_obj_get_int(args[2]); + } + + // In POSIX, it's error to seek before end of stream, we enforce it here. + if (whence == SEEK_SET && offset < 0) { + mp_raise_OSError(MP_EINVAL); + } + + int error; + mp_off_t res = mp_stream_seek(args[0], offset, whence, &error); + if (res == (mp_off_t)-1) { + mp_raise_OSError(error); + } + + // TODO: Could be uint64 + return mp_obj_new_int_from_uint(res); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj, 2, 3, stream_seek); + +static mp_obj_t stream_tell(mp_obj_t self) { + mp_obj_t offset = MP_OBJ_NEW_SMALL_INT(0); + mp_obj_t whence = MP_OBJ_NEW_SMALL_INT(SEEK_CUR); + const mp_obj_t args[3] = {self, offset, whence}; + return stream_seek(3, args); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_tell_obj, stream_tell); + +static mp_obj_t stream_flush(mp_obj_t self) { + const mp_stream_p_t *stream_p = mp_get_stream(self); + int error; + mp_uint_t res = stream_p->ioctl(self, MP_STREAM_FLUSH, 0, &error); + if (res == MP_STREAM_ERROR) { + mp_raise_OSError(error); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_flush_obj, stream_flush); + +static mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + uintptr_t val = 0; + if (n_args > 2) { + if (mp_get_buffer(args[2], &bufinfo, MP_BUFFER_WRITE)) { + val = (uintptr_t)bufinfo.buf; + } else { + val = mp_obj_get_int_truncated(args[2]); + } + } + + const mp_stream_p_t *stream_p = mp_get_stream(args[0]); + int error; + mp_uint_t res = stream_p->ioctl(args[0], mp_obj_get_int(args[1]), val, &error); + if (res == MP_STREAM_ERROR) { + mp_raise_OSError(error); + } + + return mp_obj_new_int(res); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj, 2, 3, stream_ioctl); + +#if MICROPY_STREAMS_POSIX_API +/* + * POSIX-like functions + * + * These functions have POSIX-compatible signature (except for "void *stream" + * first argument instead of "int fd"). They are useful to port existing + * POSIX-compatible software to work with MicroPython streams. + */ + +#include + +ssize_t mp_stream_posix_write(void *stream, const void *buf, size_t len) { + mp_obj_base_t *o = stream; + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); + mp_uint_t out_sz = stream_p->write(MP_OBJ_FROM_PTR(stream), buf, len, &errno); + if (out_sz == MP_STREAM_ERROR) { + return -1; + } else { + return out_sz; + } +} + +ssize_t mp_stream_posix_read(void *stream, void *buf, size_t len) { + mp_obj_base_t *o = stream; + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); + mp_uint_t out_sz = stream_p->read(MP_OBJ_FROM_PTR(stream), buf, len, &errno); + if (out_sz == MP_STREAM_ERROR) { + return -1; + } else { + return out_sz; + } +} + +off_t mp_stream_posix_lseek(void *stream, off_t offset, int whence) { + mp_off_t res = mp_stream_seek(MP_OBJ_FROM_PTR(stream), offset, whence, &errno); + if (res == (mp_off_t)-1) { + return -1; + } + return res; +} + +int mp_stream_posix_fsync(void *stream) { + mp_obj_base_t *o = stream; + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); + mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_FLUSH, 0, &errno); + if (res == MP_STREAM_ERROR) { + return -1; + } + return res; +} + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/stream.h b/non_catalog_apps/mp_flipper/lib/micropython/py/stream.h new file mode 100644 index 00000000..7c4d38af --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/stream.h @@ -0,0 +1,138 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2016 Paul Sokolovsky + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_STREAM_H +#define MICROPY_INCLUDED_PY_STREAM_H + +#include "py/obj.h" +#include "py/mperrno.h" + +#define MP_STREAM_ERROR ((mp_uint_t)-1) + +// Stream ioctl request codes +#define MP_STREAM_FLUSH (1) +#define MP_STREAM_SEEK (2) +#define MP_STREAM_POLL (3) +#define MP_STREAM_CLOSE (4) +#define MP_STREAM_TIMEOUT (5) // Get/set timeout (single op) +#define MP_STREAM_GET_OPTS (6) // Get stream options +#define MP_STREAM_SET_OPTS (7) // Set stream options +#define MP_STREAM_GET_DATA_OPTS (8) // Get data/message options +#define MP_STREAM_SET_DATA_OPTS (9) // Set data/message options +#define MP_STREAM_GET_FILENO (10) // Get fileno of underlying file +#define MP_STREAM_GET_BUFFER_SIZE (11) // Get preferred buffer size for file + +// These poll ioctl values are compatible with Linux +#define MP_STREAM_POLL_RD (0x0001) +#define MP_STREAM_POLL_WR (0x0004) +#define MP_STREAM_POLL_ERR (0x0008) +#define MP_STREAM_POLL_HUP (0x0010) +#define MP_STREAM_POLL_NVAL (0x0020) + +// Argument structure for MP_STREAM_SEEK +struct mp_stream_seek_t { + // If whence == MP_SEEK_SET, offset should be treated as unsigned. + // This allows dealing with full-width stream sizes (16, 32, 64, + // etc. bits). For other seek types, should be treated as signed. + mp_off_t offset; + int whence; +}; + +// seek ioctl "whence" values +#define MP_SEEK_SET (0) +#define MP_SEEK_CUR (1) +#define MP_SEEK_END (2) + +// Stream protocol +typedef struct _mp_stream_p_t { + // On error, functions should return MP_STREAM_ERROR and fill in *errcode (values + // are implementation-dependent, but will be exposed to user, e.g. via exception). + mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode); + mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode); + mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); + mp_uint_t is_text : 1; // default is bytes, set this for text stream +} mp_stream_p_t; + +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read1_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_stream_write1_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_close_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream___exit___obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_tell_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_flush_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj); + +// these are for mp_get_stream_raise and can be or'd together +#define MP_STREAM_OP_READ (1) +#define MP_STREAM_OP_WRITE (2) +#define MP_STREAM_OP_IOCTL (4) + +// Object is assumed to have a non-NULL stream protocol with valid r/w/ioctl methods +static inline const mp_stream_p_t *mp_get_stream(mp_const_obj_t self) { + return (const mp_stream_p_t *)MP_OBJ_TYPE_GET_SLOT(((const mp_obj_base_t *)MP_OBJ_TO_PTR(self))->type, protocol); +} + +const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags); +mp_obj_t mp_stream_close(mp_obj_t stream); + +// Iterator which uses mp_stream_unbuffered_readline_obj +mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self); + +mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte flags); + +// C-level helper functions +#define MP_STREAM_RW_READ 0 +#define MP_STREAM_RW_WRITE 2 +#define MP_STREAM_RW_ONCE 1 +mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf, mp_uint_t size, int *errcode, byte flags); +#define mp_stream_write_exactly(stream, buf, size, err) mp_stream_rw(stream, (byte *)buf, size, err, MP_STREAM_RW_WRITE) +#define mp_stream_read_exactly(stream, buf, size, err) mp_stream_rw(stream, buf, size, err, MP_STREAM_RW_READ) +mp_off_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode); + +void mp_stream_write_adaptor(void *self, const char *buf, size_t len); + +#if MICROPY_STREAMS_POSIX_API +#include +// Functions with POSIX-compatible signatures +// "stream" is assumed to be a pointer to a concrete object with the stream protocol +ssize_t mp_stream_posix_write(void *stream, const void *buf, size_t len); +ssize_t mp_stream_posix_read(void *stream, void *buf, size_t len); +off_t mp_stream_posix_lseek(void *stream, off_t offset, int whence); +int mp_stream_posix_fsync(void *stream); +#endif + +#if MICROPY_STREAMS_NON_BLOCK +#define mp_is_nonblocking_error(errno) ((errno) == MP_EAGAIN || (errno) == MP_EWOULDBLOCK) +#else +#define mp_is_nonblocking_error(errno) (0) +#endif + +#endif // MICROPY_INCLUDED_PY_STREAM_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/unicode.c b/non_catalog_apps/mp_flipper/lib/micropython/py/unicode.c new file mode 100644 index 00000000..81a37880 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/unicode.c @@ -0,0 +1,211 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/unicode.h" + +// attribute flags +#define FL_PRINT (0x01) +#define FL_SPACE (0x02) +#define FL_DIGIT (0x04) +#define FL_ALPHA (0x08) +#define FL_UPPER (0x10) +#define FL_LOWER (0x20) +#define FL_XDIGIT (0x40) + +// shorthand character attributes +#define AT_PR (FL_PRINT) +#define AT_SP (FL_SPACE | FL_PRINT) +#define AT_DI (FL_DIGIT | FL_PRINT | FL_XDIGIT) +#define AT_AL (FL_ALPHA | FL_PRINT) +#define AT_UP (FL_UPPER | FL_ALPHA | FL_PRINT) +#define AT_LO (FL_LOWER | FL_ALPHA | FL_PRINT) +#define AT_UX (FL_UPPER | FL_ALPHA | FL_PRINT | FL_XDIGIT) +#define AT_LX (FL_LOWER | FL_ALPHA | FL_PRINT | FL_XDIGIT) + +// table of attributes for ascii characters +static const uint8_t attr[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, AT_SP, AT_SP, AT_SP, AT_SP, AT_SP, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + AT_SP, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, + AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, + AT_DI, AT_DI, AT_DI, AT_DI, AT_DI, AT_DI, AT_DI, AT_DI, + AT_DI, AT_DI, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, + AT_PR, AT_UX, AT_UX, AT_UX, AT_UX, AT_UX, AT_UX, AT_UP, + AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, + AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, + AT_UP, AT_UP, AT_UP, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, + AT_PR, AT_LX, AT_LX, AT_LX, AT_LX, AT_LX, AT_LX, AT_LO, + AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, + AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, + AT_LO, AT_LO, AT_LO, AT_PR, AT_PR, AT_PR, AT_PR, 0 +}; + +#if MICROPY_PY_BUILTINS_STR_UNICODE + +unichar utf8_get_char(const byte *s) { + unichar ord = *s++; + if (!UTF8_IS_NONASCII(ord)) { + return ord; + } + ord &= 0x7F; + for (unichar mask = 0x40; ord & mask; mask >>= 1) { + ord &= ~mask; + } + while (UTF8_IS_CONT(*s)) { + ord = (ord << 6) | (*s++ & 0x3F); + } + return ord; +} + +const byte *utf8_next_char(const byte *s) { + ++s; + while (UTF8_IS_CONT(*s)) { + ++s; + } + return s; +} + +mp_uint_t utf8_ptr_to_index(const byte *s, const byte *ptr) { + mp_uint_t i = 0; + while (ptr > s) { + if (!UTF8_IS_CONT(*--ptr)) { + i++; + } + } + + return i; +} + +size_t utf8_charlen(const byte *str, size_t len) { + size_t charlen = 0; + for (const byte *top = str + len; str < top; ++str) { + if (!UTF8_IS_CONT(*str)) { + ++charlen; + } + } + return charlen; +} + +#endif + +// Be aware: These unichar_is* functions are actually ASCII-only! +bool unichar_isspace(unichar c) { + return c < 128 && (attr[c] & FL_SPACE) != 0; +} + +bool unichar_isalpha(unichar c) { + return c < 128 && (attr[c] & FL_ALPHA) != 0; +} + +/* unused +bool unichar_isprint(unichar c) { + return c < 128 && (attr[c] & FL_PRINT) != 0; +} +*/ + +bool unichar_isdigit(unichar c) { + return c < 128 && (attr[c] & FL_DIGIT) != 0; +} + +bool unichar_isxdigit(unichar c) { + return c < 128 && (attr[c] & FL_XDIGIT) != 0; +} + +bool unichar_isident(unichar c) { + return c < 128 && ((attr[c] & (FL_ALPHA | FL_DIGIT)) != 0 || c == '_'); +} + +bool unichar_isalnum(unichar c) { + return c < 128 && ((attr[c] & (FL_ALPHA | FL_DIGIT)) != 0); +} + +bool unichar_isupper(unichar c) { + return c < 128 && (attr[c] & FL_UPPER) != 0; +} + +bool unichar_islower(unichar c) { + return c < 128 && (attr[c] & FL_LOWER) != 0; +} + +unichar unichar_tolower(unichar c) { + if (unichar_isupper(c)) { + return c + 0x20; + } + return c; +} + +unichar unichar_toupper(unichar c) { + if (unichar_islower(c)) { + return c - 0x20; + } + return c; +} + +mp_uint_t unichar_xdigit_value(unichar c) { + // c is assumed to be hex digit + mp_uint_t n = c - '0'; + if (n > 9) { + n &= ~('a' - 'A'); + n -= ('A' - ('9' + 1)); + } + return n; +} + +#if MICROPY_PY_BUILTINS_STR_UNICODE + +bool utf8_check(const byte *p, size_t len) { + uint8_t need = 0; + const byte *end = p + len; + for (; p < end; p++) { + byte c = *p; + if (need) { + if (UTF8_IS_CONT(c)) { + need--; + } else { + // mismatch + return 0; + } + } else { + if (c >= 0xc0) { + if (c >= 0xf8) { + // mismatch + return 0; + } + need = (0xe5 >> ((c >> 3) & 0x6)) & 3; + } else if (c >= 0x80) { + // mismatch + return 0; + } + } + } + return need == 0; // no pending fragments allowed +} + +#endif diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/unicode.h b/non_catalog_apps/mp_flipper/lib/micropython/py/unicode.h new file mode 100644 index 00000000..c1fb5178 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/unicode.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_PY_UNICODE_H +#define MICROPY_INCLUDED_PY_UNICODE_H + +#include "py/mpconfig.h" +#include "py/misc.h" + +mp_uint_t utf8_ptr_to_index(const byte *s, const byte *ptr); +bool utf8_check(const byte *p, size_t len); + +#endif // MICROPY_INCLUDED_PY_UNICODE_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/vm.c b/non_catalog_apps/mp_flipper/lib/micropython/py/vm.c new file mode 100644 index 00000000..f87e52c9 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/vm.c @@ -0,0 +1,1489 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * Copyright (c) 2014-2015 Paul Sokolovsky + * + * 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 "py/emitglue.h" +#include "py/objtype.h" +#include "py/objfun.h" +#include "py/runtime.h" +#include "py/bc0.h" +#include "py/profile.h" + +// *FORMAT-OFF* + +#if 0 +#if MICROPY_PY_THREAD +#define TRACE_PREFIX mp_printf(&mp_plat_print, "ts=%p sp=%d ", mp_thread_get_state(), (int)(sp - &code_state->state[0] + 1)) +#else +#define TRACE_PREFIX mp_printf(&mp_plat_print, "sp=%d ", (int)(sp - &code_state->state[0] + 1)) +#endif +#define TRACE(ip) TRACE_PREFIX; mp_bytecode_print2(&mp_plat_print, ip, 1, code_state->fun_bc->child_table, &code_state->fun_bc->context->constants); +#else +#define TRACE(ip) +#endif + +// Value stack grows up (this makes it incompatible with native C stack, but +// makes sure that arguments to functions are in natural order arg1..argN +// (Python semantics mandates left-to-right evaluation order, including for +// function arguments). Stack pointer is pre-incremented and points at the +// top element. +// Exception stack also grows up, top element is also pointed at. + +#define DECODE_UINT \ + mp_uint_t unum = 0; \ + do { \ + unum = (unum << 7) + (*ip & 0x7f); \ + } while ((*ip++ & 0x80) != 0) + +#define DECODE_ULABEL \ + size_t ulab; \ + do { \ + if (ip[0] & 0x80) { \ + ulab = ((ip[0] & 0x7f) | (ip[1] << 7)); \ + ip += 2; \ + } else { \ + ulab = ip[0]; \ + ip += 1; \ + } \ + } while (0) + +#define DECODE_SLABEL \ + size_t slab; \ + do { \ + if (ip[0] & 0x80) { \ + slab = ((ip[0] & 0x7f) | (ip[1] << 7)) - 0x4000; \ + ip += 2; \ + } else { \ + slab = ip[0] - 0x40; \ + ip += 1; \ + } \ + } while (0) + +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + +#define DECODE_QSTR \ + DECODE_UINT; \ + qstr qst = qstr_table[unum] + +#else + +#define DECODE_QSTR \ + DECODE_UINT; \ + qstr qst = unum; + +#endif + +#define DECODE_PTR \ + DECODE_UINT; \ + void *ptr = (void *)(uintptr_t)code_state->fun_bc->child_table[unum] + +#define DECODE_OBJ \ + DECODE_UINT; \ + mp_obj_t obj = (mp_obj_t)code_state->fun_bc->context->constants.obj_table[unum] + +#define PUSH(val) *++sp = (val) +#define POP() (*sp--) +#define TOP() (*sp) +#define SET_TOP(val) *sp = (val) + +#if MICROPY_PY_SYS_EXC_INFO +#define CLEAR_SYS_EXC_INFO() MP_STATE_VM(cur_exception) = NULL; +#else +#define CLEAR_SYS_EXC_INFO() +#endif + +#define PUSH_EXC_BLOCK(with_or_finally) do { \ + DECODE_ULABEL; /* except labels are always forward */ \ + ++exc_sp; \ + exc_sp->handler = ip + ulab; \ + exc_sp->val_sp = MP_TAGPTR_MAKE(sp, ((with_or_finally) << 1)); \ + exc_sp->prev_exc = NULL; \ +} while (0) + +#define POP_EXC_BLOCK() \ + exc_sp--; /* pop back to previous exception handler */ \ + CLEAR_SYS_EXC_INFO() /* just clear sys.exc_info(), not compliant, but it shouldn't be used in 1st place */ + +#define CANCEL_ACTIVE_FINALLY(sp) do { \ + if (mp_obj_is_small_int(sp[-1])) { \ + /* Stack: (..., prev_dest_ip, prev_cause, dest_ip) */ \ + /* Cancel the unwind through the previous finally, replace with current one */ \ + sp[-2] = sp[0]; \ + sp -= 2; \ + } else { \ + assert(sp[-1] == mp_const_none || mp_obj_is_exception_instance(sp[-1])); \ + /* Stack: (..., None/exception, dest_ip) */ \ + /* Silence the finally's exception value (may be None or an exception) */ \ + sp[-1] = sp[0]; \ + --sp; \ + } \ +} while (0) + +#if MICROPY_PY_SYS_SETTRACE + +#define FRAME_SETUP() do { \ + assert(code_state != code_state->prev_state); \ + MP_STATE_THREAD(current_code_state) = code_state; \ + assert(code_state != code_state->prev_state); \ +} while(0) + +#define FRAME_ENTER() do { \ + assert(code_state != code_state->prev_state); \ + code_state->prev_state = MP_STATE_THREAD(current_code_state); \ + assert(code_state != code_state->prev_state); \ + if (!mp_prof_is_executing) { \ + mp_prof_frame_enter(code_state); \ + } \ +} while(0) + +#define FRAME_LEAVE() do { \ + assert(code_state != code_state->prev_state); \ + MP_STATE_THREAD(current_code_state) = code_state->prev_state; \ + assert(code_state != code_state->prev_state); \ +} while(0) + +#define FRAME_UPDATE() do { \ + assert(MP_STATE_THREAD(current_code_state) == code_state); \ + if (!mp_prof_is_executing) { \ + code_state->frame = MP_OBJ_TO_PTR(mp_prof_frame_update(code_state)); \ + } \ +} while(0) + +#define TRACE_TICK(current_ip, current_sp, is_exception) do { \ + assert(code_state != code_state->prev_state); \ + assert(MP_STATE_THREAD(current_code_state) == code_state); \ + if (!mp_prof_is_executing && code_state->frame && MP_STATE_THREAD(prof_trace_callback)) { \ + MP_PROF_INSTR_DEBUG_PRINT(code_state->ip); \ + } \ + if (!mp_prof_is_executing && code_state->frame && code_state->frame->callback) { \ + mp_prof_instr_tick(code_state, is_exception); \ + } \ +} while(0) + +#else // MICROPY_PY_SYS_SETTRACE +#define FRAME_SETUP() +#define FRAME_ENTER() +#define FRAME_LEAVE() +#define FRAME_UPDATE() +#define TRACE_TICK(current_ip, current_sp, is_exception) +#endif // MICROPY_PY_SYS_SETTRACE + +// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc) +// sp points to bottom of stack which grows up +// returns: +// MP_VM_RETURN_NORMAL, sp valid, return value in *sp +// MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp +// MP_VM_RETURN_EXCEPTION, exception in state[0] +mp_vm_return_kind_t MICROPY_WRAP_MP_EXECUTE_BYTECODE(mp_execute_bytecode)(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) { + +#define SELECTIVE_EXC_IP (0) +// When disabled, code_state->ip is updated unconditionally during op +// dispatch, and this is subsequently used in the exception handler +// (either NLR jump or direct RAISE). This is good for code size because it +// happens in a single place but is more work than necessary, as many opcodes +// cannot raise. Enabling SELECTIVE_EXC_IP means that code_state->ip +// is "selectively" updated only during handling of opcodes that might raise. +// This costs about 360 bytes on PYBV11 for a 1-3% performance gain (e.g. 3% +// in bm_fft.py). On rp2040, there is zero code size diff for a 0-1% gain. +// (Both with computed goto enabled). +#if SELECTIVE_EXC_IP +// Note: Because ip has already been advanced by one byte in the dispatch, the +// value of ip here is one byte past the last opcode. +#define MARK_EXC_IP_SELECTIVE() { code_state->ip = ip; } +// No need to update in dispatch. +#define MARK_EXC_IP_GLOBAL() +#else +#define MARK_EXC_IP_SELECTIVE() +// Immediately before dispatch, save the current ip, which will be the opcode +// about to be dispatched. +#define MARK_EXC_IP_GLOBAL() { code_state->ip = ip; } +#endif + +#if MICROPY_OPT_COMPUTED_GOTO + #include "py/vmentrytable.h" + #define DISPATCH() do { \ + TRACE(ip); \ + MARK_EXC_IP_GLOBAL(); \ + TRACE_TICK(ip, sp, false); \ + goto *entry_table[*ip++]; \ + } while (0) + #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check + #define ENTRY(op) entry_##op + #define ENTRY_DEFAULT entry_default +#else + #define DISPATCH() goto dispatch_loop + #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check + #define ENTRY(op) case op + #define ENTRY_DEFAULT default +#endif + + // nlr_raise needs to be implemented as a goto, so that the C compiler's flow analyser + // sees that it's possible for us to jump from the dispatch loop to the exception + // handler. Without this, the code may have a different stack layout in the dispatch + // loop and the exception handler, leading to very obscure bugs. + #define RAISE(o) do { nlr_pop(); nlr.ret_val = MP_OBJ_TO_PTR(o); goto exception_handler; } while (0) + +#if MICROPY_STACKLESS +run_code_state: ; +#endif +FRAME_ENTER(); + +#if MICROPY_STACKLESS +run_code_state_from_return: ; +#endif +FRAME_SETUP(); + + // Pointers which are constant for particular invocation of mp_execute_bytecode() + mp_obj_t * /*const*/ fastn; + mp_exc_stack_t * /*const*/ exc_stack; + { + size_t n_state = code_state->n_state; + fastn = &code_state->state[n_state - 1]; + exc_stack = (mp_exc_stack_t*)(code_state->state + n_state); + } + + // variables that are visible to the exception handler (declared volatile) + mp_exc_stack_t *volatile exc_sp = MP_CODE_STATE_EXC_SP_IDX_TO_PTR(exc_stack, code_state->exc_sp_idx); // stack grows up, exc_sp points to top of stack + + #if MICROPY_PY_THREAD_GIL && MICROPY_PY_THREAD_GIL_VM_DIVISOR + // This needs to be volatile and outside the VM loop so it persists across handling + // of any exceptions. Otherwise it's possible that the VM never gives up the GIL. + volatile int gil_divisor = MICROPY_PY_THREAD_GIL_VM_DIVISOR; + #endif + + // outer exception handling loop + for (;;) { + nlr_buf_t nlr; +outer_dispatch_loop: + if (nlr_push(&nlr) == 0) { + // local variables that are not visible to the exception handler + const byte *ip = code_state->ip; + mp_obj_t *sp = code_state->sp; + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + const qstr_short_t *qstr_table = code_state->fun_bc->context->constants.qstr_table; + #endif + mp_obj_t obj_shared; + MICROPY_VM_HOOK_INIT + + // If we have exception to inject, now that we finish setting up + // execution context, raise it. This works as if MP_BC_RAISE_OBJ + // bytecode was executed. + // Injecting exc into yield from generator is a special case, + // handled by MP_BC_YIELD_FROM itself + if (inject_exc != MP_OBJ_NULL && *ip != MP_BC_YIELD_FROM) { + mp_obj_t exc = inject_exc; + inject_exc = MP_OBJ_NULL; + exc = mp_make_raise_obj(exc); + RAISE(exc); + } + + // loop to execute byte code + for (;;) { +dispatch_loop: + #if MICROPY_OPT_COMPUTED_GOTO + DISPATCH(); + #else + TRACE(ip); + MARK_EXC_IP_GLOBAL(); + TRACE_TICK(ip, sp, false); + switch (*ip++) { + #endif + + ENTRY(MP_BC_LOAD_CONST_FALSE): + PUSH(mp_const_false); + DISPATCH(); + + ENTRY(MP_BC_LOAD_CONST_NONE): + PUSH(mp_const_none); + DISPATCH(); + + ENTRY(MP_BC_LOAD_CONST_TRUE): + PUSH(mp_const_true); + DISPATCH(); + + ENTRY(MP_BC_LOAD_CONST_SMALL_INT): { + mp_uint_t num = 0; + if ((ip[0] & 0x40) != 0) { + // Number is negative + num--; + } + do { + num = (num << 7) | (*ip & 0x7f); + } while ((*ip++ & 0x80) != 0); + PUSH(MP_OBJ_NEW_SMALL_INT(num)); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_CONST_STRING): { + DECODE_QSTR; + PUSH(MP_OBJ_NEW_QSTR(qst)); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_CONST_OBJ): { + DECODE_OBJ; + PUSH(obj); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_NULL): + PUSH(MP_OBJ_NULL); + DISPATCH(); + + ENTRY(MP_BC_LOAD_FAST_N): { + DECODE_UINT; + obj_shared = fastn[-unum]; + load_check: + if (obj_shared == MP_OBJ_NULL) { + local_name_error: { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NameError, MP_ERROR_TEXT("local variable referenced before assignment")); + RAISE(obj); + } + } + PUSH(obj_shared); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_DEREF): { + DECODE_UINT; + obj_shared = mp_obj_cell_get(fastn[-unum]); + goto load_check; + } + + ENTRY(MP_BC_LOAD_NAME): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + PUSH(mp_load_name(qst)); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_GLOBAL): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + PUSH(mp_load_global(qst)); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_ATTR): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_obj_t top = TOP(); + mp_obj_t obj; + #if MICROPY_OPT_LOAD_ATTR_FAST_PATH + // For the specific case of an instance type, it implements .attr + // and forwards to its members map. Attribute lookups on instance + // types are extremely common, so avoid all the other checks and + // calls that normally happen first. + mp_map_elem_t *elem = NULL; + if (mp_obj_is_instance_type(mp_obj_get_type(top))) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(top); + elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); + } + if (elem) { + obj = elem->value; + } else + #endif + { + obj = mp_load_attr(top, qst); + } + SET_TOP(obj); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_METHOD): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_load_method(*sp, qst, sp); + sp += 1; + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_SUPER_METHOD): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + sp -= 1; + mp_load_super_method(qst, sp - 1); + DISPATCH(); + } + + ENTRY(MP_BC_LOAD_BUILD_CLASS): + MARK_EXC_IP_SELECTIVE(); + PUSH(mp_load_build_class()); + DISPATCH(); + + ENTRY(MP_BC_LOAD_SUBSCR): { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t index = POP(); + SET_TOP(mp_obj_subscr(TOP(), index, MP_OBJ_SENTINEL)); + DISPATCH(); + } + + ENTRY(MP_BC_STORE_FAST_N): { + DECODE_UINT; + fastn[-unum] = POP(); + DISPATCH(); + } + + ENTRY(MP_BC_STORE_DEREF): { + DECODE_UINT; + mp_obj_cell_set(fastn[-unum], POP()); + DISPATCH(); + } + + ENTRY(MP_BC_STORE_NAME): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_store_name(qst, POP()); + DISPATCH(); + } + + ENTRY(MP_BC_STORE_GLOBAL): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_store_global(qst, POP()); + DISPATCH(); + } + + ENTRY(MP_BC_STORE_ATTR): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_store_attr(sp[0], qst, sp[-1]); + sp -= 2; + DISPATCH(); + } + + ENTRY(MP_BC_STORE_SUBSCR): + MARK_EXC_IP_SELECTIVE(); + mp_obj_subscr(sp[-1], sp[0], sp[-2]); + sp -= 3; + DISPATCH(); + + ENTRY(MP_BC_DELETE_FAST): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + if (fastn[-unum] == MP_OBJ_NULL) { + goto local_name_error; + } + fastn[-unum] = MP_OBJ_NULL; + DISPATCH(); + } + + ENTRY(MP_BC_DELETE_DEREF): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + if (mp_obj_cell_get(fastn[-unum]) == MP_OBJ_NULL) { + goto local_name_error; + } + mp_obj_cell_set(fastn[-unum], MP_OBJ_NULL); + DISPATCH(); + } + + ENTRY(MP_BC_DELETE_NAME): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_delete_name(qst); + DISPATCH(); + } + + ENTRY(MP_BC_DELETE_GLOBAL): { + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_delete_global(qst); + DISPATCH(); + } + + ENTRY(MP_BC_DUP_TOP): { + mp_obj_t top = TOP(); + PUSH(top); + DISPATCH(); + } + + ENTRY(MP_BC_DUP_TOP_TWO): + sp += 2; + sp[0] = sp[-2]; + sp[-1] = sp[-3]; + DISPATCH(); + + ENTRY(MP_BC_POP_TOP): + sp -= 1; + DISPATCH(); + + ENTRY(MP_BC_ROT_TWO): { + mp_obj_t top = sp[0]; + sp[0] = sp[-1]; + sp[-1] = top; + DISPATCH(); + } + + ENTRY(MP_BC_ROT_THREE): { + mp_obj_t top = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = top; + DISPATCH(); + } + + ENTRY(MP_BC_JUMP): { + DECODE_SLABEL; + ip += slab; + DISPATCH_WITH_PEND_EXC_CHECK(); + } + + ENTRY(MP_BC_POP_JUMP_IF_TRUE): { + DECODE_SLABEL; + if (mp_obj_is_true(POP())) { + ip += slab; + } + DISPATCH_WITH_PEND_EXC_CHECK(); + } + + ENTRY(MP_BC_POP_JUMP_IF_FALSE): { + DECODE_SLABEL; + if (!mp_obj_is_true(POP())) { + ip += slab; + } + DISPATCH_WITH_PEND_EXC_CHECK(); + } + + ENTRY(MP_BC_JUMP_IF_TRUE_OR_POP): { + DECODE_ULABEL; + if (mp_obj_is_true(TOP())) { + ip += ulab; + } else { + sp--; + } + DISPATCH_WITH_PEND_EXC_CHECK(); + } + + ENTRY(MP_BC_JUMP_IF_FALSE_OR_POP): { + DECODE_ULABEL; + if (mp_obj_is_true(TOP())) { + sp--; + } else { + ip += ulab; + } + DISPATCH_WITH_PEND_EXC_CHECK(); + } + + ENTRY(MP_BC_SETUP_WITH): { + MARK_EXC_IP_SELECTIVE(); + // stack: (..., ctx_mgr) + mp_obj_t obj = TOP(); + mp_load_method(obj, MP_QSTR___exit__, sp); + mp_load_method(obj, MP_QSTR___enter__, sp + 2); + mp_obj_t ret = mp_call_method_n_kw(0, 0, sp + 2); + sp += 1; + PUSH_EXC_BLOCK(1); + PUSH(ret); + // stack: (..., __exit__, ctx_mgr, as_value) + DISPATCH(); + } + + ENTRY(MP_BC_WITH_CLEANUP): { + MARK_EXC_IP_SELECTIVE(); + // Arriving here, there's "exception control block" on top of stack, + // and __exit__ method (with self) underneath it. Bytecode calls __exit__, + // and "deletes" it off stack, shifting "exception control block" + // to its place. + // The bytecode emitter ensures that there is enough space on the Python + // value stack to hold the __exit__ method plus an additional 4 entries. + if (TOP() == mp_const_none) { + // stack: (..., __exit__, ctx_mgr, None) + sp[1] = mp_const_none; + sp[2] = mp_const_none; + sp -= 2; + mp_call_method_n_kw(3, 0, sp); + SET_TOP(mp_const_none); + } else if (mp_obj_is_small_int(TOP())) { + // Getting here there are two distinct cases: + // - unwind return, stack: (..., __exit__, ctx_mgr, ret_val, SMALL_INT(-1)) + // - unwind jump, stack: (..., __exit__, ctx_mgr, dest_ip, SMALL_INT(num_exc)) + // For both cases we do exactly the same thing. + mp_obj_t data = sp[-1]; + mp_obj_t cause = sp[0]; + sp[-1] = mp_const_none; + sp[0] = mp_const_none; + sp[1] = mp_const_none; + mp_call_method_n_kw(3, 0, sp - 3); + sp[-3] = data; + sp[-2] = cause; + sp -= 2; // we removed (__exit__, ctx_mgr) + } else { + assert(mp_obj_is_exception_instance(TOP())); + // stack: (..., __exit__, ctx_mgr, exc_instance) + // Need to pass (exc_type, exc_instance, None) as arguments to __exit__. + sp[1] = sp[0]; + sp[0] = MP_OBJ_FROM_PTR(mp_obj_get_type(sp[0])); + sp[2] = mp_const_none; + sp -= 2; + mp_obj_t ret_value = mp_call_method_n_kw(3, 0, sp); + if (mp_obj_is_true(ret_value)) { + // We need to silence/swallow the exception. This is done + // by popping the exception and the __exit__ handler and + // replacing it with None, which signals END_FINALLY to just + // execute the finally handler normally. + SET_TOP(mp_const_none); + } else { + // We need to re-raise the exception. We pop __exit__ handler + // by copying the exception instance down to the new top-of-stack. + sp[0] = sp[3]; + } + } + DISPATCH(); + } + + ENTRY(MP_BC_UNWIND_JUMP): { + MARK_EXC_IP_SELECTIVE(); + DECODE_SLABEL; + PUSH((mp_obj_t)(mp_uint_t)(uintptr_t)(ip + slab)); // push destination ip for jump + PUSH((mp_obj_t)(mp_uint_t)(*ip)); // push number of exception handlers to unwind (0x80 bit set if we also need to pop stack) +unwind_jump:; + mp_uint_t unum = (mp_uint_t)POP(); // get number of exception handlers to unwind + while ((unum & 0x7f) > 0) { + unum -= 1; + assert(exc_sp >= exc_stack); + + if (MP_TAGPTR_TAG1(exc_sp->val_sp)) { + if (exc_sp->handler >= ip) { + // Found a finally handler that isn't active; run it. + // Getting here the stack looks like: + // (..., X, dest_ip) + // where X is pointed to by exc_sp->val_sp and in the case + // of a "with" block contains the context manager info. + assert(&sp[-1] == MP_TAGPTR_PTR(exc_sp->val_sp)); + // We're going to run "finally" code as a coroutine + // (not calling it recursively). Set up a sentinel + // on the stack so it can return back to us when it is + // done (when WITH_CLEANUP or END_FINALLY reached). + // The sentinel is the number of exception handlers left to + // unwind, which is a non-negative integer. + PUSH(MP_OBJ_NEW_SMALL_INT(unum)); + ip = exc_sp->handler; + goto dispatch_loop; + } else { + // Found a finally handler that is already active; cancel it. + CANCEL_ACTIVE_FINALLY(sp); + } + } + POP_EXC_BLOCK(); + } + ip = (const byte*)MP_OBJ_TO_PTR(POP()); // pop destination ip for jump + if (unum != 0) { + // pop the exhausted iterator + sp -= MP_OBJ_ITER_BUF_NSLOTS; + } + DISPATCH_WITH_PEND_EXC_CHECK(); + } + + ENTRY(MP_BC_SETUP_EXCEPT): + ENTRY(MP_BC_SETUP_FINALLY): { + MARK_EXC_IP_SELECTIVE(); + #if SELECTIVE_EXC_IP + PUSH_EXC_BLOCK((code_state->ip[-1] == MP_BC_SETUP_FINALLY) ? 1 : 0); + #else + PUSH_EXC_BLOCK((code_state->ip[0] == MP_BC_SETUP_FINALLY) ? 1 : 0); + #endif + DISPATCH(); + } + + ENTRY(MP_BC_END_FINALLY): + MARK_EXC_IP_SELECTIVE(); + // if TOS is None, just pops it and continues + // if TOS is an integer, finishes coroutine and returns control to caller + // if TOS is an exception, reraises the exception + assert(exc_sp >= exc_stack); + POP_EXC_BLOCK(); + if (TOP() == mp_const_none) { + sp--; + } else if (mp_obj_is_small_int(TOP())) { + // We finished "finally" coroutine and now dispatch back + // to our caller, based on TOS value + mp_int_t cause = MP_OBJ_SMALL_INT_VALUE(POP()); + if (cause < 0) { + // A negative cause indicates unwind return + goto unwind_return; + } else { + // Otherwise it's an unwind jump and we must push as a raw + // number the number of exception handlers to unwind + PUSH((mp_obj_t)cause); + goto unwind_jump; + } + } else { + assert(mp_obj_is_exception_instance(TOP())); + RAISE(TOP()); + } + DISPATCH(); + + ENTRY(MP_BC_GET_ITER): + MARK_EXC_IP_SELECTIVE(); + SET_TOP(mp_getiter(TOP(), NULL)); + DISPATCH(); + + // An iterator for a for-loop takes MP_OBJ_ITER_BUF_NSLOTS slots on + // the Python value stack. These slots are either used to store the + // iterator object itself, or the first slot is MP_OBJ_NULL and + // the second slot holds a reference to the iterator object. + ENTRY(MP_BC_GET_ITER_STACK): { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t obj = TOP(); + mp_obj_iter_buf_t *iter_buf = (mp_obj_iter_buf_t*)sp; + sp += MP_OBJ_ITER_BUF_NSLOTS - 1; + obj = mp_getiter(obj, iter_buf); + if (obj != MP_OBJ_FROM_PTR(iter_buf)) { + // Iterator didn't use the stack so indicate that with MP_OBJ_NULL. + *(sp - MP_OBJ_ITER_BUF_NSLOTS + 1) = MP_OBJ_NULL; + *(sp - MP_OBJ_ITER_BUF_NSLOTS + 2) = obj; + } + DISPATCH(); + } + + ENTRY(MP_BC_FOR_ITER): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward + code_state->sp = sp; + mp_obj_t obj; + if (*(sp - MP_OBJ_ITER_BUF_NSLOTS + 1) == MP_OBJ_NULL) { + obj = *(sp - MP_OBJ_ITER_BUF_NSLOTS + 2); + } else { + obj = MP_OBJ_FROM_PTR(&sp[-MP_OBJ_ITER_BUF_NSLOTS + 1]); + } + mp_obj_t value = mp_iternext_allow_raise(obj); + if (value == MP_OBJ_STOP_ITERATION) { + sp -= MP_OBJ_ITER_BUF_NSLOTS; // pop the exhausted iterator + ip += ulab; // jump to after for-block + } else { + PUSH(value); // push the next iteration value + #if MICROPY_PY_SYS_SETTRACE + // LINE event should trigger for every iteration so invalidate last trigger + if (code_state->frame) { + code_state->frame->lineno = 0; + } + #endif + } + DISPATCH(); + } + + ENTRY(MP_BC_POP_EXCEPT_JUMP): { + assert(exc_sp >= exc_stack); + POP_EXC_BLOCK(); + DECODE_ULABEL; + ip += ulab; + DISPATCH_WITH_PEND_EXC_CHECK(); + } + + ENTRY(MP_BC_BUILD_TUPLE): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + sp -= unum - 1; + SET_TOP(mp_obj_new_tuple(unum, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_BUILD_LIST): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + sp -= unum - 1; + SET_TOP(mp_obj_new_list(unum, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_BUILD_MAP): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + PUSH(mp_obj_new_dict(unum)); + DISPATCH(); + } + + ENTRY(MP_BC_STORE_MAP): + MARK_EXC_IP_SELECTIVE(); + sp -= 2; + mp_obj_dict_store(sp[0], sp[2], sp[1]); + DISPATCH(); + + #if MICROPY_PY_BUILTINS_SET + ENTRY(MP_BC_BUILD_SET): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + sp -= unum - 1; + SET_TOP(mp_obj_new_set(unum, sp)); + DISPATCH(); + } + #endif + + #if MICROPY_PY_BUILTINS_SLICE + ENTRY(MP_BC_BUILD_SLICE): { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t step = mp_const_none; + if (*ip++ == 3) { + // 3-argument slice includes step + step = POP(); + } + mp_obj_t stop = POP(); + mp_obj_t start = TOP(); + SET_TOP(mp_obj_new_slice(start, stop, step)); + DISPATCH(); + } + #endif + + ENTRY(MP_BC_STORE_COMP): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + mp_obj_t obj = sp[-(unum >> 2)]; + if ((unum & 3) == 0) { + mp_obj_list_append(obj, sp[0]); + sp--; + } else if (!MICROPY_PY_BUILTINS_SET || (unum & 3) == 1) { + mp_obj_dict_store(obj, sp[0], sp[-1]); + sp -= 2; + #if MICROPY_PY_BUILTINS_SET + } else { + mp_obj_set_store(obj, sp[0]); + sp--; + #endif + } + DISPATCH(); + } + + ENTRY(MP_BC_UNPACK_SEQUENCE): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + mp_unpack_sequence(sp[0], unum, sp); + sp += unum - 1; + DISPATCH(); + } + + ENTRY(MP_BC_UNPACK_EX): { + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + mp_unpack_ex(sp[0], unum, sp); + sp += (unum & 0xff) + ((unum >> 8) & 0xff); + DISPATCH(); + } + + ENTRY(MP_BC_MAKE_FUNCTION): { + DECODE_PTR; + PUSH(mp_make_function_from_proto_fun(ptr, code_state->fun_bc->context, NULL)); + DISPATCH(); + } + + ENTRY(MP_BC_MAKE_FUNCTION_DEFARGS): { + DECODE_PTR; + // Stack layout: def_tuple def_dict <- TOS + sp -= 1; + SET_TOP(mp_make_function_from_proto_fun(ptr, code_state->fun_bc->context, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_MAKE_CLOSURE): { + DECODE_PTR; + size_t n_closed_over = *ip++; + // Stack layout: closed_overs <- TOS + sp -= n_closed_over - 1; + SET_TOP(mp_make_closure_from_proto_fun(ptr, code_state->fun_bc->context, n_closed_over, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_MAKE_CLOSURE_DEFARGS): { + DECODE_PTR; + size_t n_closed_over = *ip++; + // Stack layout: def_tuple def_dict closed_overs <- TOS + sp -= 2 + n_closed_over - 1; + SET_TOP(mp_make_closure_from_proto_fun(ptr, code_state->fun_bc->context, 0x100 | n_closed_over, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_CALL_FUNCTION): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + // unum & 0xff == n_positional + // (unum >> 8) & 0xff == n_keyword + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe); + #if MICROPY_STACKLESS + if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { + code_state->ip = ip; + code_state->sp = sp; + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1); + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + deep_recursion_error: + mp_raise_recursion_depth(); + #endif + } else + #endif + { + new_state->prev = code_state; + code_state = new_state; + nlr_pop(); + goto run_code_state; + } + } + #endif + SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1)); + DISPATCH(); + } + + ENTRY(MP_BC_CALL_FUNCTION_VAR_KW): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + // unum & 0xff == n_positional + // (unum >> 8) & 0xff == n_keyword + // We have following stack layout here: + // fun arg0 arg1 ... kw0 val0 kw1 val1 ... bitmap <- TOS + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 1; + #if MICROPY_STACKLESS + if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { + code_state->ip = ip; + code_state->sp = sp; + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); + + mp_call_args_t out_args; + mp_call_prepare_args_n_kw_var(false, unum, sp, &out_args); + + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, + out_args.n_args, out_args.n_kw, out_args.args); + #if !MICROPY_ENABLE_PYSTACK + // Freeing args at this point does not follow a LIFO order so only do it if + // pystack is not enabled. For pystack, they are freed when code_state is. + mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); + #endif + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + goto deep_recursion_error; + #endif + } else + #endif + { + new_state->prev = code_state; + code_state = new_state; + nlr_pop(); + goto run_code_state; + } + } + #endif + SET_TOP(mp_call_method_n_kw_var(false, unum, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_CALL_METHOD): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + // unum & 0xff == n_positional + // (unum >> 8) & 0xff == n_keyword + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 1; + #if MICROPY_STACKLESS + if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { + code_state->ip = ip; + code_state->sp = sp; + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); + + size_t n_args = unum & 0xff; + size_t n_kw = (unum >> 8) & 0xff; + int adjust = (sp[1] == MP_OBJ_NULL) ? 0 : 1; + + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, n_args + adjust, n_kw, sp + 2 - adjust); + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + goto deep_recursion_error; + #endif + } else + #endif + { + new_state->prev = code_state; + code_state = new_state; + nlr_pop(); + goto run_code_state; + } + } + #endif + SET_TOP(mp_call_method_n_kw(unum & 0xff, (unum >> 8) & 0xff, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_CALL_METHOD_VAR_KW): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_UINT; + // unum & 0xff == n_positional + // (unum >> 8) & 0xff == n_keyword + // We have following stack layout here: + // fun self arg0 arg1 ... kw0 val0 kw1 val1 ... bitmap <- TOS + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 2; + #if MICROPY_STACKLESS + if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { + code_state->ip = ip; + code_state->sp = sp; + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); + + mp_call_args_t out_args; + mp_call_prepare_args_n_kw_var(true, unum, sp, &out_args); + + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, + out_args.n_args, out_args.n_kw, out_args.args); + #if !MICROPY_ENABLE_PYSTACK + // Freeing args at this point does not follow a LIFO order so only do it if + // pystack is not enabled. For pystack, they are freed when code_state is. + mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); + #endif + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + goto deep_recursion_error; + #endif + } else + #endif + { + new_state->prev = code_state; + code_state = new_state; + nlr_pop(); + goto run_code_state; + } + } + #endif + SET_TOP(mp_call_method_n_kw_var(true, unum, sp)); + DISPATCH(); + } + + ENTRY(MP_BC_RETURN_VALUE): + MARK_EXC_IP_SELECTIVE(); +unwind_return: + // Search for and execute finally handlers that aren't already active + while (exc_sp >= exc_stack) { + if (MP_TAGPTR_TAG1(exc_sp->val_sp)) { + if (exc_sp->handler >= ip) { + // Found a finally handler that isn't active; run it. + // Getting here the stack looks like: + // (..., X, [iter0, iter1, ...,] ret_val) + // where X is pointed to by exc_sp->val_sp and in the case + // of a "with" block contains the context manager info. + // There may be 0 or more for-iterators between X and the + // return value, and these must be removed before control can + // pass to the finally code. We simply copy the ret_value down + // over these iterators, if they exist. If they don't then the + // following is a null operation. + mp_obj_t *finally_sp = MP_TAGPTR_PTR(exc_sp->val_sp); + finally_sp[1] = sp[0]; + sp = &finally_sp[1]; + // We're going to run "finally" code as a coroutine + // (not calling it recursively). Set up a sentinel + // on a stack so it can return back to us when it is + // done (when WITH_CLEANUP or END_FINALLY reached). + PUSH(MP_OBJ_NEW_SMALL_INT(-1)); + ip = exc_sp->handler; + goto dispatch_loop; + } else { + // Found a finally handler that is already active; cancel it. + CANCEL_ACTIVE_FINALLY(sp); + } + } + POP_EXC_BLOCK(); + } + nlr_pop(); + code_state->sp = sp; + assert(exc_sp == exc_stack - 1); + MICROPY_VM_HOOK_RETURN + #if MICROPY_STACKLESS + if (code_state->prev != NULL) { + mp_obj_t res = *sp; + mp_globals_set(code_state->old_globals); + mp_code_state_t *new_code_state = code_state->prev; + #if MICROPY_ENABLE_PYSTACK + // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var + // (The latter is implicitly freed when using pystack due to its LIFO nature.) + // The sizeof in the following statement does not include the size of the variable + // part of the struct. This arg is anyway not used if pystack is enabled. + mp_nonlocal_free(code_state, sizeof(mp_code_state_t)); + #endif + code_state = new_code_state; + *code_state->sp = res; + goto run_code_state_from_return; + } + #endif + FRAME_LEAVE(); + return MP_VM_RETURN_NORMAL; + + ENTRY(MP_BC_RAISE_LAST): { + MARK_EXC_IP_SELECTIVE(); + // search for the inner-most previous exception, to reraise it + mp_obj_t obj = MP_OBJ_NULL; + for (mp_exc_stack_t *e = exc_sp; e >= exc_stack; --e) { + if (e->prev_exc != NULL) { + obj = MP_OBJ_FROM_PTR(e->prev_exc); + break; + } + } + if (obj == MP_OBJ_NULL) { + obj = mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("no active exception to reraise")); + } + RAISE(obj); + } + + ENTRY(MP_BC_RAISE_OBJ): { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t obj = mp_make_raise_obj(TOP()); + RAISE(obj); + } + + ENTRY(MP_BC_RAISE_FROM): { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t from_value = POP(); + if (from_value != mp_const_none) { + mp_warning(NULL, "exception chaining not supported"); + } + mp_obj_t obj = mp_make_raise_obj(TOP()); + RAISE(obj); + } + + ENTRY(MP_BC_YIELD_VALUE): +yield: + nlr_pop(); + code_state->ip = ip; + code_state->sp = sp; + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); + FRAME_LEAVE(); + return MP_VM_RETURN_YIELD; + + ENTRY(MP_BC_YIELD_FROM): { + MARK_EXC_IP_SELECTIVE(); + mp_vm_return_kind_t ret_kind; + mp_obj_t send_value = POP(); + mp_obj_t t_exc = MP_OBJ_NULL; + mp_obj_t ret_value; + code_state->sp = sp; // Save sp because it's needed if mp_resume raises StopIteration + if (inject_exc != MP_OBJ_NULL) { + t_exc = inject_exc; + inject_exc = MP_OBJ_NULL; + ret_kind = mp_resume(TOP(), MP_OBJ_NULL, t_exc, &ret_value); + } else { + ret_kind = mp_resume(TOP(), send_value, MP_OBJ_NULL, &ret_value); + } + + if (ret_kind == MP_VM_RETURN_YIELD) { + ip--; + PUSH(ret_value); + goto yield; + } else if (ret_kind == MP_VM_RETURN_NORMAL) { + // The generator has finished, and returned a value via StopIteration + // Replace exhausted generator with the returned value + SET_TOP(ret_value); + // If we injected GeneratorExit downstream, then even + // if it was swallowed, we re-raise GeneratorExit + if (t_exc != MP_OBJ_NULL && mp_obj_exception_match(t_exc, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { + mp_obj_t raise_t = mp_make_raise_obj(t_exc); + RAISE(raise_t); + } + DISPATCH(); + } else { + assert(ret_kind == MP_VM_RETURN_EXCEPTION); + assert(!mp_obj_exception_match(ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))); + // Pop exhausted gen + sp--; + RAISE(ret_value); + } + } + + ENTRY(MP_BC_IMPORT_NAME): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_obj_t obj = POP(); + SET_TOP(mp_import_name(qst, obj, TOP())); + DISPATCH(); + } + + ENTRY(MP_BC_IMPORT_FROM): { + FRAME_UPDATE(); + MARK_EXC_IP_SELECTIVE(); + DECODE_QSTR; + mp_obj_t obj = mp_import_from(TOP(), qst); + PUSH(obj); + DISPATCH(); + } + + ENTRY(MP_BC_IMPORT_STAR): + MARK_EXC_IP_SELECTIVE(); + mp_import_all(POP()); + DISPATCH(); + + #if MICROPY_OPT_COMPUTED_GOTO + ENTRY(MP_BC_LOAD_CONST_SMALL_INT_MULTI): + PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS)); + DISPATCH(); + + ENTRY(MP_BC_LOAD_FAST_MULTI): + obj_shared = fastn[MP_BC_LOAD_FAST_MULTI - (mp_int_t)ip[-1]]; + goto load_check; + + ENTRY(MP_BC_STORE_FAST_MULTI): + fastn[MP_BC_STORE_FAST_MULTI - (mp_int_t)ip[-1]] = POP(); + DISPATCH(); + + ENTRY(MP_BC_UNARY_OP_MULTI): + MARK_EXC_IP_SELECTIVE(); + SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP())); + DISPATCH(); + + ENTRY(MP_BC_BINARY_OP_MULTI): { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t rhs = POP(); + mp_obj_t lhs = TOP(); + SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs)); + DISPATCH(); + } + + ENTRY_DEFAULT: + MARK_EXC_IP_SELECTIVE(); + #else + ENTRY_DEFAULT: + if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM) { + PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS)); + DISPATCH(); + } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + MP_BC_LOAD_FAST_MULTI_NUM) { + obj_shared = fastn[MP_BC_LOAD_FAST_MULTI - (mp_int_t)ip[-1]]; + goto load_check; + } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + MP_BC_STORE_FAST_MULTI_NUM) { + fastn[MP_BC_STORE_FAST_MULTI - (mp_int_t)ip[-1]] = POP(); + DISPATCH(); + } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_BC_UNARY_OP_MULTI_NUM) { + SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP())); + DISPATCH(); + } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BC_BINARY_OP_MULTI_NUM) { + mp_obj_t rhs = POP(); + mp_obj_t lhs = TOP(); + SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs)); + DISPATCH(); + } else + #endif // MICROPY_OPT_COMPUTED_GOTO + { + mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NotImplementedError, MP_ERROR_TEXT("opcode")); + nlr_pop(); + code_state->state[0] = obj; + FRAME_LEAVE(); + return MP_VM_RETURN_EXCEPTION; + } + + #if !MICROPY_OPT_COMPUTED_GOTO + } // switch + #endif + +pending_exception_check: + // We've just done a branch, use this as a convenient point to + // run periodic code/checks and/or bounce the GIL.. i.e. + // not _every_ instruction but on average a branch should + // occur every few instructions. + MICROPY_VM_HOOK_LOOP + + // Check for pending exceptions or scheduled tasks to run. + // Note: it's safe to just call mp_handle_pending(true), but + // we can inline the check for the common case where there is + // neither. + if ( + #if MICROPY_ENABLE_SCHEDULER + #if MICROPY_PY_THREAD + // Scheduler + threading: Scheduler and pending exceptions are independent, check both. + MP_STATE_VM(sched_state) == MP_SCHED_PENDING || MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL + #else + // Scheduler + non-threading: Optimisation: pending exception sets sched_state, only check sched_state. + MP_STATE_VM(sched_state) == MP_SCHED_PENDING + #endif + #else + // No scheduler: Just check pending exception. + MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL + #endif + #if MICROPY_ENABLE_VM_ABORT + // Check if the VM should abort execution. + || MP_STATE_VM(vm_abort) + #endif + ) { + MARK_EXC_IP_SELECTIVE(); + mp_handle_pending(true); + } + + #if MICROPY_PY_THREAD_GIL + #if MICROPY_PY_THREAD_GIL_VM_DIVISOR + // Don't bounce the GIL too frequently (default every 32 branches). + if (--gil_divisor == 0) + #endif + { + #if MICROPY_PY_THREAD_GIL_VM_DIVISOR + gil_divisor = MICROPY_PY_THREAD_GIL_VM_DIVISOR; + #endif + #if MICROPY_ENABLE_SCHEDULER + // can only switch threads if the scheduler is unlocked + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) + #endif + { + MP_THREAD_GIL_EXIT(); + MP_THREAD_GIL_ENTER(); + } + } + #endif + + } // for loop + + } else { +exception_handler: + // exception occurred + + #if MICROPY_PY_SYS_EXC_INFO + MP_STATE_VM(cur_exception) = nlr.ret_val; + #endif + + #if SELECTIVE_EXC_IP + // with selective ip, we store the ip 1 byte past the opcode, so move ptr back + code_state->ip -= 1; + #endif + + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { + // check if it's a StopIteration within a for block + if (*code_state->ip == MP_BC_FOR_ITER) { + const byte *ip = code_state->ip + 1; + DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward + code_state->ip = ip + ulab; // jump to after for-block + code_state->sp -= MP_OBJ_ITER_BUF_NSLOTS; // pop the exhausted iterator + goto outer_dispatch_loop; // continue with dispatch loop + } else if (*code_state->ip == MP_BC_YIELD_FROM) { + // StopIteration inside yield from call means return a value of + // yield from, so inject exception's value as yield from's result + // (Instead of stack pop then push we just replace exhausted gen with value) + *code_state->sp = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val)); + code_state->ip++; // yield from is over, move to next instruction + goto outer_dispatch_loop; // continue with dispatch loop + } + } + + #if MICROPY_PY_SYS_SETTRACE + // Exceptions are traced here + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_Exception))) { + TRACE_TICK(code_state->ip, code_state->sp, true /* yes, it's an exception */); + } + #endif + +#if MICROPY_STACKLESS +unwind_loop: +#endif + // Set traceback info (file and line number) where the exception occurred, but not for: + // - constant GeneratorExit object, because it's const + // - exceptions re-raised by END_FINALLY + // - exceptions re-raised explicitly by "raise" + if (nlr.ret_val != &mp_const_GeneratorExit_obj + && *code_state->ip != MP_BC_END_FINALLY + && *code_state->ip != MP_BC_RAISE_LAST) { + const byte *ip = code_state->fun_bc->bytecode; + MP_BC_PRELUDE_SIG_DECODE(ip); + MP_BC_PRELUDE_SIZE_DECODE(ip); + const byte *line_info_top = ip + n_info; + const byte *bytecode_start = ip + n_info + n_cell; + size_t bc = code_state->ip - bytecode_start; + qstr block_name = mp_decode_uint_value(ip); + for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) { + ip = mp_decode_uint_skip(ip); + } + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + block_name = code_state->fun_bc->context->constants.qstr_table[block_name]; + qstr source_file = code_state->fun_bc->context->constants.qstr_table[0]; + #else + qstr source_file = code_state->fun_bc->context->constants.source_file; + #endif + size_t source_line = mp_bytecode_get_source_line(ip, line_info_top, bc); + mp_obj_exception_add_traceback(MP_OBJ_FROM_PTR(nlr.ret_val), source_file, source_line, block_name); + } + + while (exc_sp >= exc_stack && exc_sp->handler <= code_state->ip) { + + // nested exception + + assert(exc_sp >= exc_stack); + + // TODO make a proper message for nested exception + // at the moment we are just raising the very last exception (the one that caused the nested exception) + + // move up to previous exception handler + POP_EXC_BLOCK(); + } + + if (exc_sp >= exc_stack) { + // catch exception and pass to byte code + code_state->ip = exc_sp->handler; + mp_obj_t *sp = MP_TAGPTR_PTR(exc_sp->val_sp); + // save this exception in the stack so it can be used in a reraise, if needed + exc_sp->prev_exc = nlr.ret_val; + // push exception object so it can be handled by bytecode + PUSH(MP_OBJ_FROM_PTR(nlr.ret_val)); + code_state->sp = sp; + + #if MICROPY_STACKLESS + } else if (code_state->prev != NULL) { + mp_globals_set(code_state->old_globals); + mp_code_state_t *new_code_state = code_state->prev; + #if MICROPY_ENABLE_PYSTACK + // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var + // (The latter is implicitly freed when using pystack due to its LIFO nature.) + // The sizeof in the following statement does not include the size of the variable + // part of the struct. This arg is anyway not used if pystack is enabled. + mp_nonlocal_free(code_state, sizeof(mp_code_state_t)); + #endif + code_state = new_code_state; + size_t n_state = code_state->n_state; + fastn = &code_state->state[n_state - 1]; + exc_stack = (mp_exc_stack_t*)(code_state->state + n_state); + // variables that are visible to the exception handler (declared volatile) + exc_sp = MP_CODE_STATE_EXC_SP_IDX_TO_PTR(exc_stack, code_state->exc_sp_idx); // stack grows up, exc_sp points to top of stack + goto unwind_loop; + + #endif + } else { + // propagate exception to higher level + // Note: ip and sp don't have usable values at this point + code_state->state[0] = MP_OBJ_FROM_PTR(nlr.ret_val); // put exception here because sp is invalid + FRAME_LEAVE(); + return MP_VM_RETURN_EXCEPTION; + } + } + } +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/vmentrytable.h b/non_catalog_apps/mp_flipper/lib/micropython/py/vmentrytable.h new file mode 100644 index 00000000..79122708 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/vmentrytable.h @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ + +// *FORMAT-OFF* + +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winitializer-overrides" +#endif // __clang__ +#if __GNUC__ >= 5 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverride-init" +#endif // __GNUC__ >= 5 + +static const void *const entry_table[256] = { + [0 ... 255] = &&entry_default, + [MP_BC_LOAD_CONST_FALSE] = &&entry_MP_BC_LOAD_CONST_FALSE, + [MP_BC_LOAD_CONST_NONE] = &&entry_MP_BC_LOAD_CONST_NONE, + [MP_BC_LOAD_CONST_TRUE] = &&entry_MP_BC_LOAD_CONST_TRUE, + [MP_BC_LOAD_CONST_SMALL_INT] = &&entry_MP_BC_LOAD_CONST_SMALL_INT, + [MP_BC_LOAD_CONST_STRING] = &&entry_MP_BC_LOAD_CONST_STRING, + [MP_BC_LOAD_CONST_OBJ] = &&entry_MP_BC_LOAD_CONST_OBJ, + [MP_BC_LOAD_NULL] = &&entry_MP_BC_LOAD_NULL, + [MP_BC_LOAD_FAST_N] = &&entry_MP_BC_LOAD_FAST_N, + [MP_BC_LOAD_DEREF] = &&entry_MP_BC_LOAD_DEREF, + [MP_BC_LOAD_NAME] = &&entry_MP_BC_LOAD_NAME, + [MP_BC_LOAD_GLOBAL] = &&entry_MP_BC_LOAD_GLOBAL, + [MP_BC_LOAD_ATTR] = &&entry_MP_BC_LOAD_ATTR, + [MP_BC_LOAD_METHOD] = &&entry_MP_BC_LOAD_METHOD, + [MP_BC_LOAD_SUPER_METHOD] = &&entry_MP_BC_LOAD_SUPER_METHOD, + [MP_BC_LOAD_BUILD_CLASS] = &&entry_MP_BC_LOAD_BUILD_CLASS, + [MP_BC_LOAD_SUBSCR] = &&entry_MP_BC_LOAD_SUBSCR, + [MP_BC_STORE_FAST_N] = &&entry_MP_BC_STORE_FAST_N, + [MP_BC_STORE_DEREF] = &&entry_MP_BC_STORE_DEREF, + [MP_BC_STORE_NAME] = &&entry_MP_BC_STORE_NAME, + [MP_BC_STORE_GLOBAL] = &&entry_MP_BC_STORE_GLOBAL, + [MP_BC_STORE_ATTR] = &&entry_MP_BC_STORE_ATTR, + [MP_BC_STORE_SUBSCR] = &&entry_MP_BC_STORE_SUBSCR, + [MP_BC_DELETE_FAST] = &&entry_MP_BC_DELETE_FAST, + [MP_BC_DELETE_DEREF] = &&entry_MP_BC_DELETE_DEREF, + [MP_BC_DELETE_NAME] = &&entry_MP_BC_DELETE_NAME, + [MP_BC_DELETE_GLOBAL] = &&entry_MP_BC_DELETE_GLOBAL, + [MP_BC_DUP_TOP] = &&entry_MP_BC_DUP_TOP, + [MP_BC_DUP_TOP_TWO] = &&entry_MP_BC_DUP_TOP_TWO, + [MP_BC_POP_TOP] = &&entry_MP_BC_POP_TOP, + [MP_BC_ROT_TWO] = &&entry_MP_BC_ROT_TWO, + [MP_BC_ROT_THREE] = &&entry_MP_BC_ROT_THREE, + [MP_BC_JUMP] = &&entry_MP_BC_JUMP, + [MP_BC_POP_JUMP_IF_TRUE] = &&entry_MP_BC_POP_JUMP_IF_TRUE, + [MP_BC_POP_JUMP_IF_FALSE] = &&entry_MP_BC_POP_JUMP_IF_FALSE, + [MP_BC_JUMP_IF_TRUE_OR_POP] = &&entry_MP_BC_JUMP_IF_TRUE_OR_POP, + [MP_BC_JUMP_IF_FALSE_OR_POP] = &&entry_MP_BC_JUMP_IF_FALSE_OR_POP, + [MP_BC_SETUP_WITH] = &&entry_MP_BC_SETUP_WITH, + [MP_BC_WITH_CLEANUP] = &&entry_MP_BC_WITH_CLEANUP, + [MP_BC_UNWIND_JUMP] = &&entry_MP_BC_UNWIND_JUMP, + [MP_BC_SETUP_EXCEPT] = &&entry_MP_BC_SETUP_EXCEPT, + [MP_BC_SETUP_FINALLY] = &&entry_MP_BC_SETUP_FINALLY, + [MP_BC_END_FINALLY] = &&entry_MP_BC_END_FINALLY, + [MP_BC_GET_ITER] = &&entry_MP_BC_GET_ITER, + [MP_BC_GET_ITER_STACK] = &&entry_MP_BC_GET_ITER_STACK, + [MP_BC_FOR_ITER] = &&entry_MP_BC_FOR_ITER, + [MP_BC_POP_EXCEPT_JUMP] = &&entry_MP_BC_POP_EXCEPT_JUMP, + [MP_BC_BUILD_TUPLE] = &&entry_MP_BC_BUILD_TUPLE, + [MP_BC_BUILD_LIST] = &&entry_MP_BC_BUILD_LIST, + [MP_BC_BUILD_MAP] = &&entry_MP_BC_BUILD_MAP, + [MP_BC_STORE_MAP] = &&entry_MP_BC_STORE_MAP, + #if MICROPY_PY_BUILTINS_SET + [MP_BC_BUILD_SET] = &&entry_MP_BC_BUILD_SET, + #endif + #if MICROPY_PY_BUILTINS_SLICE + [MP_BC_BUILD_SLICE] = &&entry_MP_BC_BUILD_SLICE, + #endif + [MP_BC_STORE_COMP] = &&entry_MP_BC_STORE_COMP, + [MP_BC_UNPACK_SEQUENCE] = &&entry_MP_BC_UNPACK_SEQUENCE, + [MP_BC_UNPACK_EX] = &&entry_MP_BC_UNPACK_EX, + [MP_BC_MAKE_FUNCTION] = &&entry_MP_BC_MAKE_FUNCTION, + [MP_BC_MAKE_FUNCTION_DEFARGS] = &&entry_MP_BC_MAKE_FUNCTION_DEFARGS, + [MP_BC_MAKE_CLOSURE] = &&entry_MP_BC_MAKE_CLOSURE, + [MP_BC_MAKE_CLOSURE_DEFARGS] = &&entry_MP_BC_MAKE_CLOSURE_DEFARGS, + [MP_BC_CALL_FUNCTION] = &&entry_MP_BC_CALL_FUNCTION, + [MP_BC_CALL_FUNCTION_VAR_KW] = &&entry_MP_BC_CALL_FUNCTION_VAR_KW, + [MP_BC_CALL_METHOD] = &&entry_MP_BC_CALL_METHOD, + [MP_BC_CALL_METHOD_VAR_KW] = &&entry_MP_BC_CALL_METHOD_VAR_KW, + [MP_BC_RETURN_VALUE] = &&entry_MP_BC_RETURN_VALUE, + [MP_BC_RAISE_LAST] = &&entry_MP_BC_RAISE_LAST, + [MP_BC_RAISE_OBJ] = &&entry_MP_BC_RAISE_OBJ, + [MP_BC_RAISE_FROM] = &&entry_MP_BC_RAISE_FROM, + [MP_BC_YIELD_VALUE] = &&entry_MP_BC_YIELD_VALUE, + [MP_BC_YIELD_FROM] = &&entry_MP_BC_YIELD_FROM, + [MP_BC_IMPORT_NAME] = &&entry_MP_BC_IMPORT_NAME, + [MP_BC_IMPORT_FROM] = &&entry_MP_BC_IMPORT_FROM, + [MP_BC_IMPORT_STAR] = &&entry_MP_BC_IMPORT_STAR, + [MP_BC_LOAD_CONST_SMALL_INT_MULTI ... MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM - 1] = &&entry_MP_BC_LOAD_CONST_SMALL_INT_MULTI, + [MP_BC_LOAD_FAST_MULTI ... MP_BC_LOAD_FAST_MULTI + MP_BC_LOAD_FAST_MULTI_NUM - 1] = &&entry_MP_BC_LOAD_FAST_MULTI, + [MP_BC_STORE_FAST_MULTI ... MP_BC_STORE_FAST_MULTI + MP_BC_STORE_FAST_MULTI_NUM - 1] = &&entry_MP_BC_STORE_FAST_MULTI, + [MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + MP_BC_UNARY_OP_MULTI_NUM - 1] = &&entry_MP_BC_UNARY_OP_MULTI, + [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + MP_BC_BINARY_OP_MULTI_NUM - 1] = &&entry_MP_BC_BINARY_OP_MULTI, +}; + +#if __clang__ +#pragma clang diagnostic pop +#endif // __clang__ +#if __GNUC__ >= 5 +#pragma GCC diagnostic pop +#endif // __GNUC__ >= 5 diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/vstr.c b/non_catalog_apps/mp_flipper/lib/micropython/py/vstr.c new file mode 100644 index 00000000..fc55d694 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/vstr.c @@ -0,0 +1,246 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "py/mpconfig.h" +#include "py/runtime.h" +#include "py/mpprint.h" + +// returned value is always at least 1 greater than argument +#define ROUND_ALLOC(a) (((a) & ((~0U) - 7)) + 8) + +// Init the vstr so it allocs exactly given number of bytes. Set length to zero. +void vstr_init(vstr_t *vstr, size_t alloc) { + if (alloc < 1) { + alloc = 1; + } + vstr->alloc = alloc; + vstr->len = 0; + vstr->buf = m_new(char, vstr->alloc); + vstr->fixed_buf = false; +} + +// Init the vstr so it allocs exactly enough ram to hold a null-terminated +// string of the given length, and set the length. +void vstr_init_len(vstr_t *vstr, size_t len) { + vstr_init(vstr, len + 1); + vstr->len = len; +} + +void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf) { + vstr->alloc = alloc; + vstr->len = 0; + vstr->buf = buf; + vstr->fixed_buf = true; +} + +void vstr_init_print(vstr_t *vstr, size_t alloc, mp_print_t *print) { + vstr_init(vstr, alloc); + print->data = vstr; + print->print_strn = (mp_print_strn_t)vstr_add_strn; +} + +void vstr_clear(vstr_t *vstr) { + if (!vstr->fixed_buf) { + m_del(char, vstr->buf, vstr->alloc); + } + vstr->buf = NULL; +} + +vstr_t *vstr_new(size_t alloc) { + vstr_t *vstr = m_new_obj(vstr_t); + vstr_init(vstr, alloc); + return vstr; +} + +void vstr_free(vstr_t *vstr) { + if (vstr != NULL) { + if (!vstr->fixed_buf) { + m_del(char, vstr->buf, vstr->alloc); + } + m_del_obj(vstr_t, vstr); + } +} + +// Extend vstr strictly by requested size, return pointer to newly added chunk. +char *vstr_extend(vstr_t *vstr, size_t size) { + if (vstr->fixed_buf) { + // We can't reallocate, and the caller is expecting the space to + // be there, so the only safe option is to raise an exception. + mp_raise_msg(&mp_type_RuntimeError, NULL); + } + char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size); + char *p = new_buf + vstr->alloc; + vstr->alloc += size; + vstr->buf = new_buf; + return p; +} + +static void vstr_ensure_extra(vstr_t *vstr, size_t size) { + if (vstr->len + size > vstr->alloc) { + if (vstr->fixed_buf) { + // We can't reallocate, and the caller is expecting the space to + // be there, so the only safe option is to raise an exception. + mp_raise_msg(&mp_type_RuntimeError, NULL); + } + size_t new_alloc = ROUND_ALLOC((vstr->len + size) + 16); + char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc); + vstr->alloc = new_alloc; + vstr->buf = new_buf; + } +} + +void vstr_hint_size(vstr_t *vstr, size_t size) { + vstr_ensure_extra(vstr, size); +} + +char *vstr_add_len(vstr_t *vstr, size_t len) { + vstr_ensure_extra(vstr, len); + char *buf = vstr->buf + vstr->len; + vstr->len += len; + return buf; +} + +// Doesn't increase len, just makes sure there is a null byte at the end +char *vstr_null_terminated_str(vstr_t *vstr) { + // If there's no more room, add single byte + if (vstr->alloc == vstr->len) { + vstr_extend(vstr, 1); + } + vstr->buf[vstr->len] = '\0'; + return vstr->buf; +} + +void vstr_add_byte(vstr_t *vstr, byte b) { + byte *buf = (byte *)vstr_add_len(vstr, 1); + buf[0] = b; +} + +void vstr_add_char(vstr_t *vstr, unichar c) { + #if MICROPY_PY_BUILTINS_STR_UNICODE + // TODO: Can this be simplified and deduplicated? + // Is it worth just calling vstr_add_len(vstr, 4)? + if (c < 0x80) { + byte *buf = (byte *)vstr_add_len(vstr, 1); + *buf = (byte)c; + } else if (c < 0x800) { + byte *buf = (byte *)vstr_add_len(vstr, 2); + buf[0] = (c >> 6) | 0xC0; + buf[1] = (c & 0x3F) | 0x80; + } else if (c < 0x10000) { + byte *buf = (byte *)vstr_add_len(vstr, 3); + buf[0] = (c >> 12) | 0xE0; + buf[1] = ((c >> 6) & 0x3F) | 0x80; + buf[2] = (c & 0x3F) | 0x80; + } else { + assert(c < 0x110000); + byte *buf = (byte *)vstr_add_len(vstr, 4); + buf[0] = (c >> 18) | 0xF0; + buf[1] = ((c >> 12) & 0x3F) | 0x80; + buf[2] = ((c >> 6) & 0x3F) | 0x80; + buf[3] = (c & 0x3F) | 0x80; + } + #else + vstr_add_byte(vstr, c); + #endif +} + +void vstr_add_str(vstr_t *vstr, const char *str) { + vstr_add_strn(vstr, str, strlen(str)); +} + +void vstr_add_strn(vstr_t *vstr, const char *str, size_t len) { + vstr_ensure_extra(vstr, len); + memmove(vstr->buf + vstr->len, str, len); + vstr->len += len; +} + +static char *vstr_ins_blank_bytes(vstr_t *vstr, size_t byte_pos, size_t byte_len) { + size_t l = vstr->len; + if (byte_pos > l) { + byte_pos = l; + } + if (byte_len > 0) { + // ensure room for the new bytes + vstr_ensure_extra(vstr, byte_len); + // copy up the string to make room for the new bytes + memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos); + // increase the length + vstr->len += byte_len; + } + return vstr->buf + byte_pos; +} + +void vstr_ins_byte(vstr_t *vstr, size_t byte_pos, byte b) { + char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1); + *s = b; +} + +void vstr_ins_char(vstr_t *vstr, size_t char_pos, unichar chr) { + // TODO UNICODE + char *s = vstr_ins_blank_bytes(vstr, char_pos, 1); + *s = chr; +} + +void vstr_cut_head_bytes(vstr_t *vstr, size_t bytes_to_cut) { + vstr_cut_out_bytes(vstr, 0, bytes_to_cut); +} + +void vstr_cut_tail_bytes(vstr_t *vstr, size_t len) { + if (len > vstr->len) { + vstr->len = 0; + } else { + vstr->len -= len; + } +} + +void vstr_cut_out_bytes(vstr_t *vstr, size_t byte_pos, size_t bytes_to_cut) { + if (byte_pos >= vstr->len) { + return; + } else if (byte_pos + bytes_to_cut >= vstr->len) { + vstr->len = byte_pos; + } else { + memmove(vstr->buf + byte_pos, vstr->buf + byte_pos + bytes_to_cut, vstr->len - byte_pos - bytes_to_cut); + vstr->len -= bytes_to_cut; + } +} + +void vstr_printf(vstr_t *vstr, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vstr_vprintf(vstr, fmt, ap); + va_end(ap); +} + +void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) { + mp_print_t print = {vstr, (mp_print_strn_t)vstr_add_strn}; + mp_vprintf(&print, fmt, ap); +} diff --git a/non_catalog_apps/mp_flipper/lib/micropython/py/warning.c b/non_catalog_apps/mp_flipper/lib/micropython/py/warning.c new file mode 100644 index 00000000..cba21ba6 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/py/warning.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * Copyright (c) 2015-2018 Paul Sokolovsky + * + * 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 "py/emit.h" +#include "py/runtime.h" + +#if MICROPY_WARNINGS + +void mp_warning(const char *category, const char *msg, ...) { + if (category == NULL) { + category = "Warning"; + } + mp_print_str(MICROPY_ERROR_PRINTER, category); + mp_print_str(MICROPY_ERROR_PRINTER, ": "); + + va_list args; + va_start(args, msg); + mp_vprintf(MICROPY_ERROR_PRINTER, msg, args); + mp_print_str(MICROPY_ERROR_PRINTER, "\n"); + va_end(args); +} + +void mp_emitter_warning(pass_kind_t pass, const char *msg) { + if (pass == MP_PASS_CODE_SIZE) { + mp_warning(NULL, msg); + } +} + +#endif // MICROPY_WARNINGS diff --git a/non_catalog_apps/mp_flipper/lib/micropython/shared/runtime/gchelper.h b/non_catalog_apps/mp_flipper/lib/micropython/shared/runtime/gchelper.h new file mode 100644 index 00000000..645ee837 --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/shared/runtime/gchelper.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H +#define MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H + +#include + +#if MICROPY_GCREGS_SETJMP +#include +typedef jmp_buf gc_helper_regs_t; +#else + +#if defined(__x86_64__) +typedef uintptr_t gc_helper_regs_t[6]; +#elif defined(__i386__) +typedef uintptr_t gc_helper_regs_t[4]; +#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) +typedef uintptr_t gc_helper_regs_t[10]; +#elif defined(__aarch64__) +typedef uintptr_t gc_helper_regs_t[11]; // x19-x29 +#endif + +#endif + +void gc_helper_collect_regs_and_stack(void); + +#endif // MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H diff --git a/non_catalog_apps/mp_flipper/lib/micropython/shared/runtime/gchelper_generic.c b/non_catalog_apps/mp_flipper/lib/micropython/shared/runtime/gchelper_generic.c new file mode 100644 index 00000000..4ef2e73f --- /dev/null +++ b/non_catalog_apps/mp_flipper/lib/micropython/shared/runtime/gchelper_generic.c @@ -0,0 +1,183 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/mpstate.h" +#include "py/gc.h" +#include "shared/runtime/gchelper.h" + +#if MICROPY_ENABLE_GC + +// Even if we have specific support for an architecture, it is +// possible to force use of setjmp-based implementation. +#if !MICROPY_GCREGS_SETJMP + +// We capture here callee-save registers, i.e. ones which may contain +// interesting values held there by our callers. It doesn't make sense +// to capture caller-saved registers, because they, well, put on the +// stack already by the caller. +#if defined(__x86_64__) + +static void gc_helper_get_regs(gc_helper_regs_t arr) { + register long rbx asm ("rbx"); + register long rbp asm ("rbp"); + register long r12 asm ("r12"); + register long r13 asm ("r13"); + register long r14 asm ("r14"); + register long r15 asm ("r15"); + #ifdef __clang__ + // TODO: + // This is dirty workaround for Clang. It tries to get around + // uncompliant (wrt to GCC) behavior of handling register variables. + // Application of this patch here is random, and done only to unbreak + // MacOS build. Better, cross-arch ways to deal with Clang issues should + // be found. + asm ("" : "=r" (rbx)); + asm ("" : "=r" (rbp)); + asm ("" : "=r" (r12)); + asm ("" : "=r" (r13)); + asm ("" : "=r" (r14)); + asm ("" : "=r" (r15)); + #endif + arr[0] = rbx; + arr[1] = rbp; + arr[2] = r12; + arr[3] = r13; + arr[4] = r14; + arr[5] = r15; +} + +#elif defined(__i386__) + +static void gc_helper_get_regs(gc_helper_regs_t arr) { + register long ebx asm ("ebx"); + register long esi asm ("esi"); + register long edi asm ("edi"); + register long ebp asm ("ebp"); + #ifdef __clang__ + // TODO: + // This is dirty workaround for Clang. It tries to get around + // uncompliant (wrt to GCC) behavior of handling register variables. + // Application of this patch here is random, and done only to unbreak + // MacOS build. Better, cross-arch ways to deal with Clang issues should + // be found. + asm ("" : "=r" (ebx)); + asm ("" : "=r" (esi)); + asm ("" : "=r" (edi)); + asm ("" : "=r" (ebp)); + #endif + arr[0] = ebx; + arr[1] = esi; + arr[2] = edi; + arr[3] = ebp; +} + +#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) + +// Fallback implementation, prefer gchelper_thumb1.s or gchelper_thumb2.s + +static void gc_helper_get_regs(gc_helper_regs_t arr) { + register long r4 asm ("r4"); + register long r5 asm ("r5"); + register long r6 asm ("r6"); + register long r7 asm ("r7"); + register long r8 asm ("r8"); + register long r9 asm ("r9"); + register long r10 asm ("r10"); + register long r11 asm ("r11"); + register long r12 asm ("r12"); + register long r13 asm ("r13"); + arr[0] = r4; + arr[1] = r5; + arr[2] = r6; + arr[3] = r7; + arr[4] = r8; + arr[5] = r9; + arr[6] = r10; + arr[7] = r11; + arr[8] = r12; + arr[9] = r13; +} + +#elif defined(__aarch64__) + +static void gc_helper_get_regs(gc_helper_regs_t arr) { + const register long x19 asm ("x19"); + const register long x20 asm ("x20"); + const register long x21 asm ("x21"); + const register long x22 asm ("x22"); + const register long x23 asm ("x23"); + const register long x24 asm ("x24"); + const register long x25 asm ("x25"); + const register long x26 asm ("x26"); + const register long x27 asm ("x27"); + const register long x28 asm ("x28"); + const register long x29 asm ("x29"); + arr[0] = x19; + arr[1] = x20; + arr[2] = x21; + arr[3] = x22; + arr[4] = x23; + arr[5] = x24; + arr[6] = x25; + arr[7] = x26; + arr[8] = x27; + arr[9] = x28; + arr[10] = x29; +} + +#else + +#error "Architecture not supported for gc_helper_get_regs. Set MICROPY_GCREGS_SETJMP to use the fallback implementation." + +#endif + +#else // !MICROPY_GCREGS_SETJMP + +// Even if we have specific support for an architecture, it is +// possible to force use of setjmp-based implementation. + +static void gc_helper_get_regs(gc_helper_regs_t arr) { + setjmp(arr); +} + +#endif // MICROPY_GCREGS_SETJMP + +// Explicitly mark this as noinline to make sure the regs variable +// is effectively at the top of the stack: otherwise, in builds where +// LTO is enabled and a lot of inlining takes place we risk a stack +// layout where regs is lower on the stack than pointers which have +// just been allocated but not yet marked, and get incorrectly sweeped. +MP_NOINLINE void gc_helper_collect_regs_and_stack(void) { + gc_helper_regs_t regs; + gc_helper_get_regs(regs); + // GC stack (and regs because we captured them) + void **regs_ptr = (void **)(void *)®s; + gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)®s) / sizeof(uintptr_t)); +} + +#endif // MICROPY_ENABLE_GC diff --git a/non_catalog_apps/mp_flipper/logo.svg b/non_catalog_apps/mp_flipper/logo.svg new file mode 100644 index 00000000..f29e533c --- /dev/null +++ b/non_catalog_apps/mp_flipper/logo.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + diff --git a/non_catalog_apps/mp_flipper/publish.sh b/non_catalog_apps/mp_flipper/publish.sh new file mode 100755 index 00000000..1459bf40 --- /dev/null +++ b/non_catalog_apps/mp_flipper/publish.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +readonly BRANCH="${1}" +readonly TARGET='./temp' +readonly REMOTE='git@github.com:ofabel/mp-flipper.git' + +set -e + +rm -rf ${TARGET} + +git init -b ${BRANCH} ${TARGET} && cd ${TARGET} && git remote add origin ${REMOTE} && cd .. + +rm -rf ${TARGET}/* + +cp -r dist/pages/* ${TARGET} +touch ${TARGET}/.nojekyll + +cd ${TARGET} + +git add . && git commit -m "update docs" && git push origin ${BRANCH} --force || cd . + +cd .. + +rm -rf ${TARGET} + +exit 0 diff --git a/non_catalog_apps/mp_flipper/pyproject.toml b/non_catalog_apps/mp_flipper/pyproject.toml new file mode 100644 index 00000000..139ca62f --- /dev/null +++ b/non_catalog_apps/mp_flipper/pyproject.toml @@ -0,0 +1,44 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "flipperzero" +version = "1.3.0" +authors = [ + { name = "Oliver Fabel" }, +] +description = "Python support for Flipper Zero." +readme = "flipperzero/README.md" +license = "MIT" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Python :: Implementation :: MicroPython", + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: MIT License", + "Operating System :: Other OS", +] +keywords = [ + "flipperzero", + "Flipper Zero", + "MicroPython", + "f0", + "GPIO", + "PWM", + "ADC", + "Infrared" +] + +[project.urls] +"Documentation" = "https://ofabel.github.io/mp-flipper/" +"Source code" = "https://github.com/ofabel/mp-flipper" +"Issue Tracker" = "https://github.com/ofabel/mp-flipper/issues" +"Changelog" = "https://github.com/ofabel/mp-flipper/blob/master/CHANGELOG.md" + +[tool.hatch.build] +directory = "dist/python" +include = [ + "flipperzero/__init__.py" +] +ignore-vcs = true +only-packages = true diff --git a/non_catalog_apps/mp_flipper/requirements.txt b/non_catalog_apps/mp_flipper/requirements.txt new file mode 100644 index 00000000..a532298d --- /dev/null +++ b/non_catalog_apps/mp_flipper/requirements.txt @@ -0,0 +1,3 @@ +Sphinx==8.0.2 +myst-parser==4.0.0 +hatch==1.12.0 \ No newline at end of file diff --git a/non_catalog_apps/mp_flipper/splash.svg b/non_catalog_apps/mp_flipper/splash.svg new file mode 100644 index 00000000..b779d89a --- /dev/null +++ b/non_catalog_apps/mp_flipper/splash.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/non_catalog_apps/mp_flipper/upython.c b/non_catalog_apps/mp_flipper/upython.c new file mode 100644 index 00000000..3d3e9b96 --- /dev/null +++ b/non_catalog_apps/mp_flipper/upython.c @@ -0,0 +1,171 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include "upython_icons.h" + +#define TAG "uPython" + +typedef enum { + ActionNone, + ActionOpen, + ActionExit +} Action; + +static Action action = ActionNone; + +static void execute_file(FuriString* file) { + size_t stack; + + const char* path = furi_string_get_cstr(file); + FuriString* file_path = furi_string_alloc_printf("%s", path); + + do { + FURI_LOG_I(TAG, "executing script %s", path); + + const size_t heap_size = memmgr_get_free_heap() * 0.1; + const size_t stack_size = 2 * 1024; + uint8_t* heap = malloc(heap_size * sizeof(uint8_t)); + + FURI_LOG_D(TAG, "initial heap size is %zu bytes", heap_size); + FURI_LOG_D(TAG, "stack size is %zu bytes", stack_size); + + size_t index = furi_string_search_rchar(file_path, '/'); + + furi_check(index != FURI_STRING_FAILURE); + + bool is_py_file = furi_string_end_with_str(file_path, ".py"); + + furi_string_left(file_path, index); + + mp_flipper_set_root_module_path(furi_string_get_cstr(file_path)); + + mp_flipper_init(heap, heap_size, stack_size, &stack); + + if(is_py_file) { + mp_flipper_exec_py_file(path); + } else { + mp_flipper_exec_mpy_file(path); + } + + mp_flipper_deinit(); + + free(heap); + } while(false); + + furi_string_free(file_path); +} + +static bool select_python_file(FuriString* file_path) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + + DialogsFileBrowserOptions browser_options; + + dialog_file_browser_set_basic_options(&browser_options, "py", NULL); + + browser_options.hide_ext = false; + browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; + + bool result = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + + return result; +} + +static void on_input(const void* event, void* ctx) { + UNUSED(ctx); + + InputKey key = ((InputEvent*)event)->key; + InputType type = ((InputEvent*)event)->type; + + if(type != InputTypeRelease) { + return; + } + + switch(key) { + case InputKeyOk: + action = ActionOpen; + break; + case InputKeyBack: + action = ActionExit; + break; + default: + action = ActionNone; + break; + } +} + +static void show_splash_screen() { + Gui* gui = furi_record_open(RECORD_GUI); + FuriPubSub* input_event_queue = furi_record_open(RECORD_INPUT_EVENTS); + FuriPubSubSubscription* input_event = furi_pubsub_subscribe(input_event_queue, on_input, NULL); + + ViewPort* view_port = view_port_alloc(); + + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + Canvas* canvas = gui_direct_draw_acquire(gui); + + canvas_draw_icon(canvas, 0, 0, &I_splash); + canvas_draw_icon(canvas, 82, 17, &I_qrcode); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 66, 3, AlignLeft, AlignTop, "Micro"); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 90, 2, AlignLeft, AlignTop, "Python"); + + canvas_set_font(canvas, FontSecondary); + + canvas_draw_icon(canvas, 65, 53, &I_Pin_back_arrow_10x8); + canvas_draw_str_aligned(canvas, 78, 54, AlignLeft, AlignTop, "Exit"); + + canvas_draw_icon(canvas, 98, 54, &I_ButtonCenter_7x7); + canvas_draw_str_aligned(canvas, 107, 54, AlignLeft, AlignTop, "Open"); + + canvas_commit(canvas); + + while(action == ActionNone) { + furi_delay_ms(1); + } + + furi_pubsub_unsubscribe(input_event_queue, input_event); + + gui_direct_draw_release(gui); + gui_remove_view_port(gui, view_port); + + view_port_free(view_port); + + furi_record_close(RECORD_INPUT_EVENTS); + furi_record_close(RECORD_GUI); +} + +int32_t upython(void* p) { + UNUSED(p); + + do { + show_splash_screen(); + + if(action == ActionExit) { + break; + } + + FuriString* file_path = furi_string_alloc_set_str(APP_ASSETS_PATH("upython")); + + if(select_python_file(file_path)) { + execute_file(file_path); + } + + furi_string_free(file_path); + + action = ActionNone; + } while(true); + + return 0; +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/LICENSE b/non_catalog_apps/simultaneous_rfid_reader/LICENSE new file mode 100644 index 00000000..7b29a521 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2024] [William Riley Haffner] + +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. diff --git a/non_catalog_apps/simultaneous_rfid_reader/app.c b/non_catalog_apps/simultaneous_rfid_reader/app.c new file mode 100644 index 00000000..d05ccbdb --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/app.c @@ -0,0 +1,221 @@ +#include "app.h" +#include "structures.h" + +/** + * @brief Callback for exiting the application. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to exit the application. + * @param context The context - unused + * @return next view id +*/ +uint32_t uhf_reader_navigation_exit_callback(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +/** + * @brief Handle submenu item selection. + * @details This function is called when user selects an item from the submenu. + * @param context The context - UHFReaderApp object. + * @param index The UHFReaderSubmenuIndex item that was clicked. +*/ +void uhf_reader_submenu_callback(void* context, uint32_t index) { + UHFReaderApp* App = (UHFReaderApp*)context; + switch(index) { + case UHFReaderSubmenuIndexRead: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewRead); + break; + case UHFReaderSubmenuIndexSaved: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSaved); + break; + case UHFReaderSubmenuIndexConfig: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewConfigure); + break; + case UHFReaderSubmenuIndexAbout: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewAbout); + break; + default: + break; + } +} + +/** + * @brief Allocates Main Menu + * @details Allocates the main submenu with the read, saved, config, and about submenu items + * @param app The UHFReaderApp object. +*/ +void main_menu_alloc(UHFReaderApp* App) { + App->Submenu = submenu_alloc(); + submenu_set_header(App->Submenu, "UHF RFID Reader"); + submenu_add_item( + App->Submenu, "Read", UHFReaderSubmenuIndexRead, uhf_reader_submenu_callback, App); + submenu_add_item( + App->Submenu, "Saved", UHFReaderSubmenuIndexSaved, uhf_reader_submenu_callback, App); + submenu_add_item( + App->Submenu, "Configure", UHFReaderSubmenuIndexConfig, uhf_reader_submenu_callback, App); + submenu_add_item( + App->Submenu, "About", UHFReaderSubmenuIndexAbout, uhf_reader_submenu_callback, App); + view_set_previous_callback( + submenu_get_view(App->Submenu), uhf_reader_navigation_exit_callback); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSubmenu, submenu_get_view(App->Submenu)); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSubmenu); +} + +/** + * @brief Allocate the UHF RFID Reader Application. + * @details This function allocates all resources for the UHF RFID Reader Application. + * @return UHFReaderApp object. +*/ +static UHFReaderApp* uhf_reader_app_alloc() { + //Allocating storage for the saved_epcs and index file + UHFReaderApp* App = (UHFReaderApp*)malloc(sizeof(UHFReaderApp)); + Storage* Storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* File = flipper_format_file_alloc(Storage); + FlipperFormat* IndexFile = flipper_format_file_alloc(Storage); + App->TagStorage = Storage; + App->EpcFile = File; + App->EpcIndexFile = IndexFile; + + //Initializing the arrays for storing all tag information from the read screen + App->EpcValues = (char**)malloc(150 * 26); + App->TidValues = (char**)malloc(150 * 41); + App->ResValues = (char**)malloc(150 * 17); + App->MemValues = (char**)malloc(150 * 33); + App->EpcToSave = (char*)malloc(25); + App->NumberOfEpcsToRead = 0; + + //Initializing the indices for each array and the file name + App->NameSize = 36; + App->NameSizeParse = 27; + App->CurEpcIndex = 26; + App->CurTidIndex = 1; + App->CurResIndex = 1; + App->CurMemIndex = 1; + App->FileName = (char*)malloc(App->NameSize); + + //Creating the initial GUI + Gui* Gui = furi_record_open(RECORD_GUI); + App->ViewDispatcher = view_dispatcher_alloc(); + //view_dispatcher_enable_queue(App->ViewDispatcher); + view_dispatcher_attach_to_gui(App->ViewDispatcher, Gui, ViewDispatcherTypeFullscreen); + view_dispatcher_set_event_callback_context(App->ViewDispatcher, App); + + //Allocating the different views, menus, and widgets for the app + main_menu_alloc(App); + view_config_alloc(App); + view_read_alloc(App); + view_saved_menu_alloc(App); + view_tag_actions_alloc(App); + view_lock_alloc(App); + view_kill_alloc(App); + view_write_alloc(App); + view_epc_alloc(App); + view_epc_info_alloc(App); + view_delete_alloc(App); + view_delete_success_alloc(App); + view_about_alloc(App); + App->Notifications = furi_record_open(RECORD_NOTIFICATION); + +#ifdef BACKLIGHT_ON + notification_message(App->Notifications, &sequence_display_backlight_enforce_on); +#endif + //Create the UART helper object used to communicate with the RPi Zero via UART + App->UartHelper = uart_helper_alloc(); + uart_helper_set_baud_rate(App->UartHelper, DEVICE_BAUDRATE); + uart_helper_set_delimiter(App->UartHelper, LINE_DELIMITER, INCLUDE_LINE_DELIMITER); + uart_helper_set_callback(App->UartHelper, uart_demo_process_line, App); + + return App; +} + +/** + * @brief Free the UHFReaderApp application. + * @details This function frees the UHF RFID Reader application resources. + * @param app The UHFReaderApp application object. +*/ +static void uhf_reader_app_free(UHFReaderApp* App) { +#ifdef BACKLIGHT_ON + notification_message(App->Notifications, &sequence_display_backlight_enforce_auto); +#endif + //Freeing the notification and storage records + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_GUI); + + //Freeing the UART helper + if(App->UHFModuleType != YRM100X_MODULE){ + uart_helper_free(App->UartHelper); + } + else{ + //Free Tag Wrapper + uhf_tag_wrapper_free(App->YRM100XWorker->uhf_tag_wrapper); + + //Freeing yrm100x worker + uhf_worker_stop(App->YRM100XWorker); + uhf_worker_free(App->YRM100XWorker); + } + + + + //Freeing all views, widgets, and menus + view_delete_free(App); + view_delete_success_free(App); + view_about_free(App); + view_epc_free(App); + view_epc_info_free(App); + view_read_free(App); + view_write_free(App); + view_config_free(App); + view_saved_free(App); + view_tag_actions_free(App); + view_lock_free(App); + view_kill_free(App); + + //Freeing the main menu view + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSubmenu); + submenu_free(App->Submenu); + view_dispatcher_free(App->ViewDispatcher); + + //Freeing variables used in app + furi_string_free(App->EpcName); + furi_string_free(App->EpcDelete); + furi_string_free(App->EpcNameDelete); + furi_string_free(App->EpcToWrite); + free(App->EpcValues); + free(App->TidValues); + free(App->ResValues); + free(App->MemValues); + free(App->EpcToSave); + free(App); +} + +/** + * @brief Main function for UHF RFID Reader application. + * @details This function is the entry point for the UHF RFID Reader application. + * @param _p Input parameter - unused + * @return 0 - Success +*/ +int32_t main_uhf_reader_app(void* _p) { + UNUSED(_p); + Expansion* expansion = furi_record_open(RECORD_EXPANSION); + expansion_disable(expansion); + + bool PowerOn = false; + + if(!furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + PowerOn = true; + } + + UHFReaderApp* App = uhf_reader_app_alloc(); + view_dispatcher_run(App->ViewDispatcher); + + if(PowerOn) { + furi_hal_power_disable_otg(); + } + uhf_reader_app_free(App); + expansion_enable(expansion); + furi_record_close(RECORD_EXPANSION); + return 0; +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/app.h b/non_catalog_apps/simultaneous_rfid_reader/app.h new file mode 100644 index 00000000..66a8d3db --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/app.h @@ -0,0 +1,64 @@ +#pragma once +#include "structures.h" + +//Defining the UART parameters for communicating with the Raspberry Pi Zero +#define DEVICE_BAUDRATE 9600 +#define LINE_DELIMITER '\n' +//#define YRM100X_LINE_DELIMITER 0x7E +#define INCLUDE_LINE_DELIMITER false + +//Setting the backlight on +#define BACKLIGHT_ON 1 + +//Creating different messages used for display +#define TAG "simultaneous_rfid_reader" +#define WRITE_EPC_VAL "EPC Value" +#define WRITE_RES_MEM "Reserved Memory" +#define WRITE_USR_MEM "User Memory" +#define WRITE_TID_MEM "TID Value" +#define WRITE_EPC_OK "EPC Written!" +#define WRITE_EPC_CANCELED "Write Canceled!" +#define WRITE_EPC_FAIL "Write Failed!" + +//Content for the about screen +#define UHF_RFID_VERSION_APP "0.1.1" +#define UHF_RFID_MEM_DEVELOPER "@Haffnerriley" +#define UHF_RFID_GITHUB "https://github.com/haffnerriley" +#define UHF_RFID_NAME "\e#\e! UHF RFID Reader \e!\n" +#define UHF_RFID_BLANK_INV "\e#\e!" +#define YRM100X_MODULE 1 +#define M6E_NANO_MODULE 2 +#define M7E_HECTO_MODULE 3 + +#define NO_SAVE_ON_WRITE 1 +#define YES_SAVE_ON_WRITE 2 +//Defining different region values +#define USA_REGION 0 +#define EU_REGION 1 +#define KOREA_REGION 2 +#define CHINA_800_REGION 3 +#define CHINA_900_REGION 4 + +//Including the different views and helper files +#include "views/view_about.h" +#include "views/view_saved.h" +#include "views/view_config.h" +#include "views/view_write.h" +#include "views/view_delete.h" +#include "views/view_epc.h" +#include "views/view_delete_success.h" +#include "views/view_epc_info.h" +#include "views/view_read.h" +#include "views/view_tag_actions.h" +#include "views/view_lock.h" +#include "views/view_kill.h" +#include "helpers/extract_tag_info.h" +#include "helpers/uart_process.h" +#include "helpers/saved_epc_functions.h" + +//Function declarations +void main_menu_alloc(UHFReaderApp* App); + +uint32_t uhf_reader_navigation_exit_callback(void* context); + +void uhf_reader_submenu_callback(void* context, uint32_t index); diff --git a/non_catalog_apps/simultaneous_rfid_reader/app.png b/non_catalog_apps/simultaneous_rfid_reader/app.png new file mode 100644 index 00000000..4817a360 Binary files /dev/null and b/non_catalog_apps/simultaneous_rfid_reader/app.png differ diff --git a/non_catalog_apps/simultaneous_rfid_reader/application.fam b/non_catalog_apps/simultaneous_rfid_reader/application.fam new file mode 100644 index 00000000..d5054bcb --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/application.fam @@ -0,0 +1,16 @@ +App( + appid="simultaneous_rfid_reader", + name="Simultaneous UHF RFID Reader", + apptype=FlipperAppType.EXTERNAL, + entry_point="main_uhf_reader_app", + stack_size=28 * 1024, + requires=["gui"], + fap_icon="app.png", + fap_category="GPIO", + fap_icon_assets="icons", + fap_icon_assets_symbol="simultaneous_rfid_reader", + fap_author="@RileyHaffner", + fap_weburl="https://github.com/haffnerriley/Simultaneous-UHF-RFID-FlipperZero", + fap_version="1.1", + fap_description="Simultaneous UHF RFID Reader that supports the M6E Nano, M7E Hecto, and YRM1000 series Readers. Read up to 150 UHF tags per second [Using ThingMagic Readers]!", +) diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/extract_tag_info.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/extract_tag_info.c new file mode 100644 index 00000000..26e94d93 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/extract_tag_info.c @@ -0,0 +1,370 @@ +#include "extract_tag_info.h" + +/** + * @brief Function to extract the EPC value + * @details This function extracts the EPC from the YRM100 Buffer + * @param array The uint8_t* array + * @param length. The length of the array + * @return The converted value from the given input +*/ +char* convertToHexString(uint8_t* array, size_t length) { + if(array == NULL || length == 0) { + return " "; + } + FuriString* temp_str = furi_string_alloc(); + + for(size_t i = 0; i < length; i++) { + furi_string_cat_printf(temp_str, "%02X", array[i]); + } + const char* furi_str = furi_string_get_cstr(temp_str); + + size_t str_len = strlen(furi_str); + char* str = (char*)malloc(sizeof(char) * str_len); + + memcpy(str, furi_str, str_len); + furi_string_free(temp_str); + return str; +} + +/** + * @brief Function to extract the TID value from the saved epcs file + * @details This function extracts the TID from the saved epcs file. + * @param Input The char* input for the saved UHF tag in the file + * @return The TID value from the given input +*/ +char* extract_epc(const char* Input) { + + // Find the position of the first colon + const char* FirstColonPos = strchr(Input, ':'); + + if(FirstColonPos != NULL) { + + const char* StartOfSecondValue = FirstColonPos + 1; + // Move to the character after the first colon + const char* SecondColonPos = strchr(FirstColonPos + 1, ':'); + + if(SecondColonPos != NULL) { + size_t SecondValueLen = SecondColonPos - StartOfSecondValue; + // Move to the character after the second colon + char* Result = malloc(SecondValueLen + 1); + + if(Result != NULL) { + + // Copy the third value into the result + strncpy(Result, StartOfSecondValue, SecondValueLen); + + // Null-terminate the string + Result[SecondValueLen] = '\0'; + return Result; + } + + } + } + + return NULL; +} + +/** + * @brief Function to extract the Reserved Memory value from the saved epcs file + * @details This function extracts the Reserved Memory from the saved epcs file. + * @param Input The char* input for the saved UHF tag in the file + * @return The Reserved Memory value from the given input +*/ +char* extract_res(const char* Input) { + + // There is probably a better way to do this... + // Find the position of the first colon + const char* FirstColonPos = strchr(Input, ':'); + + if(FirstColonPos != NULL) { + + // Move to the character after the first colon + const char* SecondColonPos = strchr(FirstColonPos + 1, ':'); + + if(SecondColonPos != NULL) { + + // Move to the character after the second colon + const char* ThirdColonPos = strchr(SecondColonPos + 1, ':'); + + if(ThirdColonPos != NULL) { + + // Move to the character after the third colon + const char* StartOfFourthValue = ThirdColonPos + 1; + + // Find the next colon after the start of the fourth value + const char* FourthColonPos = strchr(StartOfFourthValue, ':'); + + if(FourthColonPos != NULL) { + + // Calculate the length of the fourth value by subtracting the positions + size_t FourthValueLen = FourthColonPos - StartOfFourthValue; + + // Allocate memory for the fourth value + char* Result = malloc(FourthValueLen + 1); + + if(Result != NULL) { + + // Copy the fourth value into the result + strncpy(Result, StartOfFourthValue, FourthValueLen); + + // Null-terminate the string + Result[FourthValueLen] = '\0'; + + return Result; + } + } + } + } + } + + return NULL; +} + +/** + * @brief Function to extract the sixth value from the saved epcs file + * @details This function extracts the sixth value from the saved epcs file. + * @param Input The char* input for the saved UHF tag in the file + * @return The sixth value from the given input + */ +char* extract_pc(const char* Input) { + + // Find the position of the first colon + const char* FirstColonPos = strchr(Input, ':'); + + if(FirstColonPos != NULL) { + + // Move to the character after the first colon + const char* SecondColonPos = strchr(FirstColonPos + 1, ':'); + + if(SecondColonPos != NULL) { + + // Move to the character after the second colon + const char* ThirdColonPos = strchr(SecondColonPos + 1, ':'); + + if(ThirdColonPos != NULL) { + + // Move to the character after the third colon + const char* FourthColonPos = strchr(ThirdColonPos + 1, ':'); + + if(FourthColonPos != NULL) { + + // Move to the character after the fourth colon + const char* FifthColonPos = strchr(FourthColonPos + 1, ':'); + + if(FifthColonPos != NULL) { + + // Move to the character after the fifth colon + const char* StartOfSixthValue = FifthColonPos + 1; + + // Find the next colon after the start of the sixth value + const char* SixthColonPos = strchr(StartOfSixthValue, ':'); + + // Calculate the length of the sixth value + size_t SixthValueLen; + if (SixthColonPos != NULL) { + SixthValueLen = SixthColonPos - StartOfSixthValue; + } else { + // If there is no sixth colon, take the rest of the string + SixthValueLen = strlen(StartOfSixthValue); + } + + // Allocate memory for the sixth value + char* Result = malloc(SixthValueLen + 1); + + if(Result != NULL) { + + // Copy the sixth value into the result + strncpy(Result, StartOfSixthValue, SixthValueLen); + + // Null-terminate the string + Result[SixthValueLen] = '\0'; + + return Result; + } + } + } + } + } + } + + return NULL; +} + + +/** + * @brief Function to extract the TID value from the saved epcs file + * @details This function extracts the TID from the saved epcs file. + * @param Input The char* input for the saved UHF tag in the file + * @return The TID value from the given input +*/ +char* extract_tid(const char* Input) { + + // Find the position of the first colon + const char* FirstColonPos = strchr(Input, ':'); + + if(FirstColonPos != NULL) { + + // Move to the character after the first colon + const char* SecondColonPos = strchr(FirstColonPos + 1, ':'); + + if(SecondColonPos != NULL) { + + // Move to the character after the second colon + const char* StartOfThirdValue = SecondColonPos + 1; + + // Find the next colon after the start of the third value + const char* ThirdColonPos = strchr(StartOfThirdValue, ':'); + + if(ThirdColonPos != NULL) { + + // Calculate the length of the third value by subtracting the positions + size_t ThirdValueLen = ThirdColonPos - StartOfThirdValue; + + // Allocate memory for the third value + char* Result = malloc(ThirdValueLen + 1); + + if(Result != NULL) { + + // Copy the third value into the result + strncpy(Result, StartOfThirdValue, ThirdValueLen); + + // Null-terminate the string + Result[ThirdValueLen] = '\0'; + return Result; + } + } + } + } + + return NULL; +} + + +/** + * @brief Function to extract the fifth value from the saved epcs file + * @details This function extracts the fifth value from the saved epcs file. + * @param Input The char* input for the saved UHF tag in the file + * @return The fifth value from the given input + */ +char* extract_mem(const char* Input) { + + // Find the position of the first colon + const char* FirstColonPos = strchr(Input, ':'); + + if(FirstColonPos != NULL) { + + // Move to the character after the first colon + const char* SecondColonPos = strchr(FirstColonPos + 1, ':'); + + if(SecondColonPos != NULL) { + + // Move to the character after the second colon + const char* ThirdColonPos = strchr(SecondColonPos + 1, ':'); + + if(ThirdColonPos != NULL) { + + // Move to the character after the third colon + const char* FourthColonPos = strchr(ThirdColonPos + 1, ':'); + + if(FourthColonPos != NULL) { + + // Move to the character after the fourth colon + const char* StartOfFifthValue = FourthColonPos + 1; + + // Find the next colon after the start of the fifth value + const char* FifthColonPos = strchr(StartOfFifthValue, ':'); + + if(FifthColonPos != NULL) { + + // Calculate the length of the fifth value by subtracting the positions + size_t FifthValueLen = FifthColonPos - StartOfFifthValue; + + // Allocate memory for the fifth value + char* Result = malloc(FifthValueLen + 1); + + if(Result != NULL) { + + // Copy the fifth value into the result + strncpy(Result, StartOfFifthValue, FifthValueLen); + + // Null-terminate the string + Result[FifthValueLen] = '\0'; + + return Result; + } + } + } + } + } + } + + return NULL; +} +/** + * @brief Function to extract the crc value from the saved epcs file + * @details This function extracts the crc from the saved epcs file. + * @param Input The char* input for the saved UHF tag in the file + * @return The crc value from the given input +*/ +char* extract_crc(const char* Input) { + + // Find the position of the last colon + const char* LastColonPos = strrchr(Input, ':'); + + if(LastColonPos != NULL) { + + // Move to the character after the last colon + const char* StartOfLastValue = LastColonPos + 1; + + // Calculate the length of the last value + size_t LastValueLen = strlen(StartOfLastValue); + + // Allocate memory for the last value + char* Result = malloc(LastValueLen + 1); + + if(Result != NULL) { + + // Copy the last value into the result + strncpy(Result, StartOfLastValue, LastValueLen); + + // Null-terminate the string + Result[LastValueLen] = '\0'; + + return Result; + } + } + + return NULL; +} + +/** + * @brief Function to extract the Name from the saved epcs file + * @details This function extracts the Name from the saved epcs file. + * @param Input The char* input for the saved UHF tag in the file + * @return The Name from the given input +*/ +char* extract_name(const char* Input) { + + // Find the position of the colon + const char* ColonPos = strchr(Input, ':'); + + if(ColonPos != NULL) { + + // Calculate the length of the name + size_t Len = ColonPos - Input; + + // Allocate memory for the name + char* Result = malloc(Len + 1); + + if(Result != NULL) { + + // Copy the name into the result + strncpy(Result, Input, Len); + + // Null-terminate the string + Result[Len] = '\0'; + return Result; + } + } + return NULL; +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/extract_tag_info.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/extract_tag_info.h new file mode 100644 index 00000000..9bf9b55b --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/extract_tag_info.h @@ -0,0 +1,20 @@ +#include +#include +#include + +//Function Declarations +char* extract_epc(const char* Input); + +char* extract_res(const char* Input); + +char* extract_tid(const char* Input); + +char* extract_mem(const char* Input); + +char* extract_name(const char* Input); + +char* convertToHexString(uint8_t* array, size_t length); + +char* extract_crc(const char* Input); + +char* extract_pc(const char* Input); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/ring_buffer.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/ring_buffer.c new file mode 100644 index 00000000..c8ced8b8 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/ring_buffer.c @@ -0,0 +1,166 @@ +/** + * A ring buffer. This is a circular buffer that can be used to store data until a + * delimiter is found, at which point the line can be extracted. The ring buffer is a + * fixed size and will overwrite old data if the buffer is full. + * + * @author CodeAllNight +*/ + +#include + +/** + * The size of the ring buffer. This is the maximum number of bytes that can be stored in the + * buffer. If the buffer is full, the oldest data will be overwritten. +*/ +static const uint32_t ring_buffer_size = 4096; + +/** + * A ring buffer. This is used to store data received from the UART. The data is stored in a + * ring buffer so that it can be processed in the background while the main loop is doing other + * things. +*/ +typedef struct { + // The delimiter character + char delimiter; + + // If true, the delimiter will be included in the extracted line + bool include_delimiter; + + // The ring buffer. This is a circular buffer that can be used to store data until a + // delimiter is found, at which point the line can be extracted. The ring buffer is a + // fixed size and will overwrite old data if the buffer is full. + uint8_t* ring_buffer; + + // The next index to read from the ring buffer. An empty buffer will have + // ring_buffer_read == ring_buffer_write + size_t ring_buffer_read; + + // The next index to write to the ring buffer + size_t ring_buffer_write; +} RingBuffer; + +RingBuffer* ring_buffer_alloc() { + RingBuffer* buffer = malloc(sizeof(RingBuffer)); + buffer->ring_buffer = malloc(ring_buffer_size); + buffer->ring_buffer_read = 0; + buffer->ring_buffer_write = 0; + buffer->delimiter = '\n'; + buffer->include_delimiter = false; + return buffer; +} + +void ring_buffer_free(RingBuffer* buffer) { + free(buffer->ring_buffer); + free(buffer); +} + +void ring_buffer_set_delimiter(RingBuffer* rb, char delimiter, bool include_delimiter) { + rb->delimiter = delimiter; + rb->include_delimiter = include_delimiter; +} + +size_t ring_buffer_available(RingBuffer* rb) { + size_t available; + if(rb->ring_buffer_write == rb->ring_buffer_read) { + // Empty buffer has size - 1 available bytes + available = ring_buffer_size - 1; + } else if(rb->ring_buffer_read > rb->ring_buffer_write) { + // Write can go up to read - 1 + available = rb->ring_buffer_read - rb->ring_buffer_write; + } else { + // Write can go up to end of buffer, then from start to read - 1 + available = (ring_buffer_size - rb->ring_buffer_write) + rb->ring_buffer_read; + } + return available; +} + +bool ring_buffer_add(RingBuffer* rb, uint8_t* data, size_t length) { + bool hasDelim = false; + + for(size_t i = 0; i < length; i++) { + // Copy the data into the ring buffer + rb->ring_buffer[rb->ring_buffer_write] = data[i]; + + // Check if the data is the delimiter + if(data[i] == (uint8_t)rb->delimiter) { + hasDelim = true; + } + + // Update the write pointer, wrapping if necessary + if(++rb->ring_buffer_write >= ring_buffer_size) { + rb->ring_buffer_write = 0; + } + + // Check if the buffer is full + if(rb->ring_buffer_write == rb->ring_buffer_read) { + // ERROR: buffer is full, discard oldest byte (read index) + if(++rb->ring_buffer_read >= ring_buffer_size) { + rb->ring_buffer_read = 0; + } + } + } + + return hasDelim; +} + +size_t ring_buffer_find_delim(RingBuffer* rb) { + size_t index = FURI_STRING_FAILURE; + + // Search for the delimiter, starting at the read index + size_t i = rb->ring_buffer_read; + + // While the buffer is not empty and the delimiter has not been found + while(i != rb->ring_buffer_write) { + // Check if the current byte is the delimiter + if(rb->ring_buffer[i] == (uint8_t)rb->delimiter) { + // Found the delimiter + index = i; + break; + } + + // Update the index, wrapping if necessary + if(++i >= ring_buffer_size) { + i = 0; + } + } + + return index; +} + +void ring_buffer_extract_line(RingBuffer* rb, size_t delim_index, FuriString* line) { + if(delim_index > rb->ring_buffer_read) { + // line is in one chunk + furi_string_set_strn( + line, + (char*)&rb->ring_buffer[rb->ring_buffer_read], + delim_index - rb->ring_buffer_read + (rb->include_delimiter ? 1 : 0)); + } else { + // line is split across the buffer wrap, so we need to copy it in two chunks + // first chunk is from read index to end of buffer + furi_string_set_strn( + line, + (char*)&rb->ring_buffer[rb->ring_buffer_read], + ring_buffer_size - rb->ring_buffer_read); + + // second chunk is from start of buffer to delimiter + for(size_t i = 0; i < delim_index; i++) { + furi_string_push_back(line, (char)rb->ring_buffer[i]); + } + + // add the delimiter if required + if(rb->include_delimiter) { + furi_string_push_back(line, (char)rb->ring_buffer[delim_index]); + } + } + + // update the buffer read pointer, wrapping if necessary + rb->ring_buffer_read = delim_index + 1; + if(rb->ring_buffer_read >= ring_buffer_size) { + rb->ring_buffer_read = 0; + } +} + +void ring_buffer_clear(RingBuffer* rb) { + rb->ring_buffer_read = 0; + rb->ring_buffer_write = 0; +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/ring_buffer.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/ring_buffer.h new file mode 100644 index 00000000..be0f0250 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/ring_buffer.h @@ -0,0 +1,88 @@ +/** + * A ring buffer. This is a circular buffer that can be used to store data until a + * delimiter is found, at which point the line can be extracted. The ring buffer is a + * fixed size and will overwrite old data if the buffer is full. + * + * @author CodeAllNight +*/ + +#include + +/** + * Ring buffer structure +*/ +typedef struct RingBuffer RingBuffer; + +/** + * Allocates a new ring buffer. This is a circular buffer that can be used to store data + * until a delimiter is found, at which point the line can be extracted. The ring buffer + * is a fixed size and will overwrite old data if the buffer is full. + * + * @return A new ring buffer +*/ +RingBuffer* ring_buffer_alloc(); + +/** + * Frees a ring buffer. + * + * @param rb The ring buffer to free +*/ +void ring_buffer_free(RingBuffer* rb); + +/** + * Sets the delimiter for the ring buffer. The delimiter is the character that will be + * searched for when extracting a line from the buffer. + * + * @param rb The ring buffer + * @param delimiter The delimiter character + * @param include_delimiter If true, the delimiter will be included in the extracted line +*/ +void ring_buffer_set_delimiter(RingBuffer* rb, char delimiter, bool include_delimiter); + +/** + * Returns the number of bytes available in the ring buffer. This is the number of bytes + * that can be added to the buffer before it is full. + * + * @param rb The ring buffer + * + * @return The number of bytes available in the ring buffer +*/ +size_t ring_buffer_available(RingBuffer* rb); + +/** + * Adds data to the ring buffer. If the buffer is full, the oldest data will be overwritten. + * + * @param rb The ring buffer + * @param data The data to add + * @param length The length of the data to add + * + * @return True if the data was added successfully, false if the buffer is full +*/ +bool ring_buffer_add(RingBuffer* rb, uint8_t* data, size_t length); + +/** + * Searches the ring buffer for the delimiter. If the delimiter is found, the index of the + * delimiter is returned. If the delimiter is not found, the function returns FURI_STRING_FAILURE + * (-1). + * + * @param rb The ring buffer + * + * @return The index of the delimiter, or FURI_STRING_FAILURE if the delimiter is not found +*/ +size_t ring_buffer_find_delim(RingBuffer* rb); + +/** + * Extracts a line from the ring buffer. The line is stored in the provided FuriString. + * + * @param rb The ring buffer + * @param delim_index The index of the delimiter + * @param line The FuriString to store the line in +*/ +void ring_buffer_extract_line(RingBuffer* rb, size_t delim_index, FuriString* line); + +/** + * Clears the ring buffer. This will remove all data from the buffer. + * + * @param rb The ring buffer +*/ +void ring_buffer_clear(RingBuffer* rb); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/saved_epc_functions.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/saved_epc_functions.c new file mode 100644 index 00000000..dc944790 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/saved_epc_functions.c @@ -0,0 +1,256 @@ +#include "saved_epc_functions.h" + +/** + * @brief Function to update the saved epcs file + * @details This function updates the dictionary that is being used to store all the saved epcs. + * @param context - The UHFReaderApp +*/ +void update_dictionary_keys(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + // Updating the saved epcs menu + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSaved); + submenu_free(App->SubmenuSaved); + App->SubmenuSaved = submenu_alloc(); + submenu_set_header(App->SubmenuSaved, "Saved EPCs"); + uint32_t TotalTags = App->NumberOfSavedTags; + + // Open the saved epcs file and extract the tag name and create the submenu items + if(flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + for(uint32_t i = 0; i < TotalTags; i++) { + + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + furi_string_printf(TempStr, "Tag%ld", i + 1); + + if(!flipper_format_read_string( + App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", i + 1); + } else { + + // Extract the name of the saved UHF Tag + const char* InputString = furi_string_get_cstr(TempTag); + char* ExtractedName = extract_name(InputString); + + if(ExtractedName != NULL) { + submenu_add_item( + App->SubmenuSaved, + ExtractedName, + (i + 1), + uhf_reader_submenu_saved_callback, + App); + free(ExtractedName); + } + } + furi_string_free(TempStr); + furi_string_free(TempTag); + } + } + view_set_previous_callback( + submenu_get_view(App->SubmenuSaved), uhf_reader_navigation_saved_exit_callback); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSaved, submenu_get_view(App->SubmenuSaved)); + flipper_format_file_close(App->EpcFile); +} + +/** + * @brief Function to delete and update a saved tag in the saved epcs file + * @details This function deletes the specified tag and updates the saved epcs file + * @param context - The UHFReaderApp + * @param key_to_delete - The index of the saved UHF tag to delete +*/ +void delete_and_update_entry(void* context, uint32_t KeyToDelete) { + UHFReaderApp* App = (UHFReaderApp*)context; + uint32_t TotalTags = App->NumberOfSavedTags; + FuriString* EpcToDelete = furi_string_alloc(); + + // Open the saved epcs file + if(flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + + // Update subsequent keys + for(uint32_t i = 1; i <= TotalTags; i++) { + FuriString* TempStrOld = furi_string_alloc(); + FuriString* TempStrNew = furi_string_alloc(); + furi_string_printf(TempStrOld, "Tag%ld", i); + + // Calculate the new key based on the deletion + uint32_t NewKey = (i > KeyToDelete) ? i - 1 : i; + + // Skip the deleted key + if(i != KeyToDelete) { + furi_string_printf(TempStrNew, "Tag%ld", NewKey); + FuriString* TempTag = furi_string_alloc(); + if(!flipper_format_read_string( + App->EpcFile, furi_string_get_cstr(TempStrOld), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", i); + } else { + if(!flipper_format_update_string_cstr( + App->EpcFile, + furi_string_get_cstr(TempStrNew), + furi_string_get_cstr(TempTag))) { + FURI_LOG_D(TAG, "Could not update tag %ld data", i); + flipper_format_write_string_cstr( + App->EpcFile, + furi_string_get_cstr(TempStrNew), + furi_string_get_cstr(TempTag)); + } + } + + furi_string_free(TempTag); + } + furi_string_free(TempStrOld); + furi_string_free(TempStrNew); + } + + furi_string_printf(EpcToDelete, "Tag%ld", App->NumberOfSavedTags); + if(!flipper_format_delete_key(App->EpcFile, furi_string_get_cstr(EpcToDelete))) { + FURI_LOG_D( + TAG, "Could not delete saved tag with index %ld", App->NumberOfSavedTags); + } + + // Update the total number of saved tags + App->NumberOfSavedTags--; + flipper_format_file_close(App->EpcFile); + } + + // Update the index file + FuriString* NewNumEpcs = furi_string_alloc(); + furi_string_printf(NewNumEpcs, "%ld", App->NumberOfSavedTags); + if(flipper_format_file_open_existing(App->EpcIndexFile, APP_DATA_PATH("Index_File.txt"))) { + if(!flipper_format_write_string_cstr( + App->EpcIndexFile, "Number of Tags", furi_string_get_cstr(NewNumEpcs))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } else { + FURI_LOG_E(TAG, "Updated index file!"); + } + } + furi_string_free(EpcToDelete); + flipper_format_file_close(App->EpcIndexFile); + furi_string_free(NewNumEpcs); +} + + +/** + * @brief Function to convert a hex character to its integer value + * @details This function converts a hex character to its integer value in ASCII + * @param char c - The char to convert + * @return the int - the converted integer +*/ +uint8_t hex_char_to_int(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + return 0; +} + +/** + * @brief Function to convert a hex string to byte array + * @details This function converts a hex string to a byte array + * @param char hex_string - The string to convert + * @param the byte_array - the allocated byte array that should be empty and passed in + * @param the lenght - The length variable fort the byte array +*/ +void hex_string_to_bytes(const char* hex_string, uint8_t* byte_array, size_t* byte_array_len) { + size_t hex_len = strlen(hex_string); + if (hex_len % 2 != 0) { + // Handle error: hex string length must be even + *byte_array_len = 0; + return; + } + + *byte_array_len = hex_len / 2; + for (size_t i = 0; i < *byte_array_len; i++) { + byte_array[i] = (hex_char_to_int(hex_string[2 * i]) << 4) | hex_char_to_int(hex_string[2 * i + 1]); + } +} + +/** + * @brief Function to convert a hex string to byte array (uint16) + * @details This function converts a hex string to a byte array + * @param char hex_string - The string to convert + * @param the byte_array - the allocated byte array that should be empty and passed in + * @param the lenght - The length variable fort the byte array +*/ +void hex_string_to_uint16(const char* hex_string, uint16_t* uint16_array, size_t* uint16_array_len) { + size_t hex_len = strlen(hex_string); + if (hex_len % 4 != 0) { + // Handle error: hex string length must be a multiple of 4 for uint16_t conversion + *uint16_array_len = 0; + return; + } + + *uint16_array_len = hex_len / 4; + for (size_t i = 0; i < *uint16_array_len; i++) { + uint16_array[i] = (hex_char_to_int(hex_string[4 * i]) << 12) | + (hex_char_to_int(hex_string[4 * i + 1]) << 8) | + (hex_char_to_int(hex_string[4 * i + 2]) << 4) | + hex_char_to_int(hex_string[4 * i + 3]); + } +} + +/** + * @brief Function to convert a uint16 value to a hex string + * @details Function to convert a uint16 value to a hex string + * @param uint16 value - value to convert + * @return char* pointer to the string +*/ +char* uint16_to_hex_string(uint16_t value) { + // Allocate memory for the hex string (4 characters for hex + 1 for null terminator) + char* hex_string = (char*)malloc(5 * sizeof(char)); + if (hex_string == NULL) { + // Handle memory allocation failure + return NULL; + } + + // Convert the value to hex and store it in the string + snprintf(hex_string, 5, "%04X", value); + + return hex_string; +} + +/** + * @brief Function to combine two arrays together + * @details Function to combine two arrays together specifically for the PCs and CRCs... + * @param array1 - the first array to combine + * @param array2 - the second array to combine + * @return char* pointer to the string +*/ +char* combineArrays(const char* array1, const char* array2) { + // Allocate memory for the new array (size 4 + 4 = 8) + char* combinedArray = (char*)malloc(16 * sizeof(char)); + if (combinedArray == NULL) { + return NULL; + } + + // Copy the elements from the first array + memcpy(combinedArray, array1, 8 * sizeof(char)); + + // Copy the elements from the second array + memcpy(combinedArray + 8, array2, 8 * sizeof(char)); + + return combinedArray; +} + +/** + * @brief Function that converts byte array to uint32 + * @details Function that converts byte array to 32-bit int + * @param bytes - the byte array + * @param length - the length of the byte array + * @return uint32 the 32 bit int +*/ +uint32_t bytes_to_uint32(uint8_t* bytes, size_t length) { + if (length > sizeof(uint32_t)) { + return 0; + } + + uint32_t result = 0; + for (size_t i = 0; i < length; i++) { + result = (result << 8) | bytes[i]; + } + + return result; +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/saved_epc_functions.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/saved_epc_functions.h new file mode 100644 index 00000000..2aa54215 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/saved_epc_functions.h @@ -0,0 +1,19 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void delete_and_update_entry(void* context, uint32_t KeyToDelete); + +void update_dictionary_keys(void* context); + +uint8_t hex_char_to_int(char c); + +void hex_string_to_bytes(const char* hex_string, uint8_t* byte_array, size_t* byte_array_len); + +char* uint16_to_hex_string(uint16_t value); + +void hex_string_to_uint16(const char* hex_string, uint16_t* uint16_array, size_t* uint16_array_len); + +char* combineArrays(const char* array1, const char* array2); + +uint32_t bytes_to_uint32(uint8_t* bytes, size_t length); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_helper.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_helper.c new file mode 100644 index 00000000..edb8b873 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_helper.c @@ -0,0 +1,272 @@ +/** + * UartHelper is a utility class that helps with reading lines of data from a UART. + * It uses a stream buffer to receive data from the UART ISR, and a worker thread + * to dequeue data from the stream buffer and process it. The worker thread uses + * a ring buffer to hold data until a delimiter is found, at which point the line + * is extracted and the process_line callback is invoked. + * + * @author CodeAllNight +*/ + +#include +#include "ring_buffer.h" + +/** + * Callback invoked when a line is read from the UART. +*/ +typedef void (*ProcessLine)(FuriString* line, void* context); + +/** + * UartHelper is a utility class that helps with reading lines of data from a UART. +*/ +typedef struct { + // UART bus & channel to use + FuriHalBus uart_bus; + FuriHalSerialHandle* serial_handle; + bool uart_init_by_app; + + // Stream buffer to hold incoming data (worker will dequeue and process) + FuriStreamBuffer* rx_stream; + + // Worker thread that dequeues data from the stream buffer and processes it + FuriThread* worker_thread; + + // Buffer to hold data until a delimiter is found + RingBuffer* ring_buffer; + + // Callback to invoke when a line is read + ProcessLine process_line; + void* context; +} UartHelper; + +/** + * WorkerEventFlags are used to signal the worker thread to exit or to process data. + * Each flag is a bit in a 32-bit integer, so we can use the FuriThreadFlags API to + * wait for either flag to be set. +*/ +typedef enum { + WorkerEventDataWaiting = 1 << 0, // bit flag 0 - data is waiting to be processed + WorkerEventExiting = 1 << 1, // bit flag 1 - worker thread is exiting +} WorkerEventFlags; + +/** + * Invoked when a byte of data is received on the UART bus. This function + * adds the byte to the stream buffer and sets the WorkerEventDataWaiting flag. + * + * @param handle Serial handle + * @param event FuriHalSerialRxEvent + * @param context UartHelper instance +*/ +static void uart_helper_received_byte_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + UartHelper* helper = context; + + if(event == FuriHalSerialRxEventData) { + uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(helper->rx_stream, (void*)&data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(helper->worker_thread), WorkerEventDataWaiting); + } +} + +/** + * Worker thread that dequeues data from the stream buffer and processes it. When + * a delimiter is found in the data, the line is extracted and the process_line callback + * is invoked. This thread will exit when the WorkerEventExiting flag is set. + * + * @param context UartHelper instance + * @return 0 +*/ +static int32_t uart_helper_worker(void* context) { + UartHelper* helper = context; + FuriString* line = furi_string_alloc(); + uint32_t events; + + do { + events = furi_thread_flags_wait( + WorkerEventDataWaiting | WorkerEventExiting, FuriFlagWaitAny, FuriWaitForever); + + if(events & WorkerEventDataWaiting) { + size_t length_read = 0; + do { + // We will read out of the stream into this temporary read_buffer in chunks + // of up to 32 bytes. A tricker implementation would be to read directly + // into our ring_buffer, avoiding the extra copy. + uint8_t read_buffer[32]; + + size_t available = ring_buffer_available(helper->ring_buffer); + if(available == 0) { + // In our case, the buffer is large enough to hold the entire response, + // so we should never hit this case & data loss is fine in the + // exceptional case. + + // If loosing data is unacceptable, you could transfer the buffer into + // a string and prepend that to the response, or you could increase the + // size of the buffer. + + // Buffer is full, so data loss will happen at beginning of string! + // We read one byte (hopefully the delimiter). + available = 1; + } + + // We will read up to 32 bytes, but no more than the available space in the + // ring buffer. + size_t request_bytes = available < COUNT_OF(read_buffer) ? available : + COUNT_OF(read_buffer); + + // hasDelim will be true if at least one delimiter was found in the data. + bool hasDelim = false; + + // Read up to 32 bytes from the stream buffer into our temporary read_buffer. + length_read = + furi_stream_buffer_receive(helper->rx_stream, read_buffer, request_bytes, 0); + + // Add the data to the ring buffer, check for delimiters, and process lines. + if(length_read > 0) { + // Add the data to the ring buffer. If at least one delimiter is found, + // hasDelim will be true. + hasDelim = ring_buffer_add(helper->ring_buffer, read_buffer, length_read); + + if(hasDelim) { + size_t index; + do { + // Find the next delimiter in the ring buffer. + index = ring_buffer_find_delim(helper->ring_buffer); + + // If a delimiter was found, extract the line and process it. + if(index != FURI_STRING_FAILURE) { + // Extract the line from the ring buffer, advancing the read + // pointer to the next byte after the delimiter. + ring_buffer_extract_line(helper->ring_buffer, index, line); + + // Invoke the callback to process the line. + if(helper->process_line) { + helper->process_line(line, helper->context); + } + } + } while(index != FURI_STRING_FAILURE); + } + } + // Keep reading until we have read all of the data from the stream buffer. + } while(length_read > 0); + } + // Keep processing data until the WorkerEventExiting flag is set. + } while((events & WorkerEventExiting) != WorkerEventExiting); + + furi_string_free(line); + + return 0; +} + +UartHelper* uart_helper_alloc() { + // rx_buffer_size should be large enough to hold the entire response from the device. + const size_t rx_buffer_size = 2048; + + // worker_stack_size should be large enough stack for the worker thread (including functions it calls). + const size_t worker_stack_size = 1024; + + // uart_baud is the default baud rate for the UART. + const uint32_t uart_baud = 115200; + + // The uart_helper uses USART1. + UartHelper* helper = malloc(sizeof(UartHelper)); + helper->uart_bus = FuriHalBusUSART1; + helper->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart); + // Typically the UART is already enabled and will assert if you try to enable again. + helper->uart_init_by_app = !furi_hal_bus_is_enabled(helper->uart_bus); + if(helper->uart_init_by_app) { + furi_hal_serial_init(helper->serial_handle, uart_baud); + } else { + // If UART is already initialized, disable the console so it doesn't write to the UART. + // furi_hal_console_disable(); + } + + // process_line callback gets invoked when a line is read. By default the callback is not set. + helper->process_line = NULL; + + // Set the baud rate for the UART + furi_hal_serial_set_br(helper->serial_handle, uart_baud); + + // When a byte of data is received, it will be appended to the rx_stream. A worker thread + // will dequeue the data from the rx_stream. + helper->rx_stream = furi_stream_buffer_alloc(rx_buffer_size, 1); + + // The worker thread will remove the data from the rx_stream and copy it into the ring_buffer. + // The worker thread will then process the data in the ring_buffer, invoking the process_line + // callback whenever a delimiter is found in the data. + helper->ring_buffer = ring_buffer_alloc(); + + // worker_thread is the routine that will process data from the rx_stream. + helper->worker_thread = + furi_thread_alloc_ex("UartHelperWorker", worker_stack_size, uart_helper_worker, helper); + furi_thread_start(helper->worker_thread); + + // Set the callback to invoke when data is received. + furi_hal_serial_async_rx_start( + helper->serial_handle, uart_helper_received_byte_callback, helper, false); + + return helper; +} + +void uart_helper_set_delimiter(UartHelper* helper, char delimiter, bool include_delimiter) { + // Update the delimiter character and flag to determine if delimiter should be part + // of the response to the process_line callback. + ring_buffer_set_delimiter(helper->ring_buffer, delimiter, include_delimiter); +} + +void uart_helper_set_callback(UartHelper* helper, ProcessLine process_line, void* context) { + // Set the process_line callback and context. + helper->process_line = process_line; + helper->context = context; +} + +void uart_helper_set_baud_rate(UartHelper* helper, uint32_t baud_rate) { + // Update the baud rate for the UART. + furi_hal_serial_set_br(helper->serial_handle, baud_rate); + ring_buffer_clear(helper->ring_buffer); +} + +void uart_helper_send(UartHelper* helper, const char* data, size_t length) { + // Transmit data via UART TX. + furi_hal_serial_tx(helper->serial_handle, (uint8_t*)data, length); +} + +void uart_helper_send_string(UartHelper* helper, FuriString* string) { + const char* str = furi_string_get_cstr(string); + + // UTF-8 strings can have character counts different then lengths! + // Count the bytes until a null is encountered. + size_t length = 0; + while(str[length] != 0) { + length++; + } + + // Transmit data + uart_helper_send(helper, str, length); +} + +void uart_helper_free(UartHelper* helper) { + // Signal that we want the worker to exit. It may be doing other work. + furi_thread_flags_set(furi_thread_get_id(helper->worker_thread), WorkerEventExiting); + + // Wait for the worker_thread to complete it's work and release its resources. + furi_thread_join(helper->worker_thread); + + // Free the worker_thread. + furi_thread_free(helper->worker_thread); + + furi_hal_serial_control_release(helper->serial_handle); + // If the UART was initialized by the application, deinitialize it, otherwise re-enable the console. + if(helper->uart_init_by_app) { + furi_hal_serial_deinit(helper->serial_handle); + } else { + // furi_hal_console_enable(); + } + + // Free the rx_stream and ring_buffer. + furi_stream_buffer_free(helper->rx_stream); + ring_buffer_free(helper->ring_buffer); + + free(helper); +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_helper.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_helper.h new file mode 100644 index 00000000..0752fb2a --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_helper.h @@ -0,0 +1,75 @@ +/** + * UartHelper is a utility class that helps with reading lines of data from a UART. + * It uses a stream buffer to receive data from the UART ISR, and a worker thread + * to dequeue data from the stream buffer and process it. The worker thread uses + * a ring buffer to hold data until a delimiter is found, at which point the line + * is extracted and the process_line callback is invoked. + * + * @author CodeAllNight +*/ + +#include + +/** + * A helper class for parsing data received over UART. +*/ +typedef struct UartHelper UartHelper; + +/** + * Callback function for processing a line of data. + * + * @param line The line of data to process. +*/ +typedef void (*ProcessLine)(FuriString* line, void* context); + +/** + * Allocates a new UartHelper. The UartHelper will be initialized with a baud rate of 115200. + * Log messages will be disabled since they also use the UART. + * + * IMPORTANT -- FURI_LOG_x calls will not work! + * + * @return A new UartHelper. +*/ +UartHelper* uart_helper_alloc(); + +/** + * Sets the delimiter to use when parsing data. The default delimeter is '\n' and + * the default value for include_delimiter is false. + * + * @param helper The UartHelper. + * @param delimiter The delimiter to use. + * @param include_delimiter If true, the delimiter will be included in the line of data passed to the callback function. +*/ +void uart_helper_set_delimiter(UartHelper* helper, char delimiter, bool include_delimiter); + +/** + * Sets the callback function to be called when a line of data is received. + * + * @param helper The UartHelper. + * @param process_line The callback function. + * @param context The context to pass to the callback function. +*/ +void uart_helper_set_callback(UartHelper* helper, ProcessLine process_line, void* context); + +/** + * Sets the baud rate for the UART. The default is 115200. + * + * @param helper The UartHelper. + * @param baud_rate The baud rate. +*/ +void uart_helper_set_baud_rate(UartHelper* helper, uint32_t baud_rate); + +/** + * Sends data over the UART TX pin. +*/ +void uart_helper_send(UartHelper* helper, const char* data, size_t length); + +/** + * Sends a string over the UART TX pin. +*/ +void uart_helper_send_string(UartHelper* helper, FuriString* string); + +/** + * Frees the UartHelper & enables log messages. +*/ +void uart_helper_free(UartHelper* helper); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_process.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_process.c new file mode 100644 index 00000000..61aa6c1a --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_process.c @@ -0,0 +1,433 @@ +#include "../app.h" + +/** + * @brief Callback for handling UART input for the M6E & M7E + * @details This function is called there is content to read in the buffer. This will be modified to support locking, killing, and other commands + * @param line The current line in the buffer to read + * @param context The UHFReaderApp +*/ +void uart_demo_process_line(FuriString* Line, void* Context) { + UHFReaderApp* App = (UHFReaderApp*)Context; + bool Redraw = true; + + switch(App->State) { + // Handle the Idle State + case UHFReaderStateIdle: + + // Handles the EPC values + if(strcmp(furi_string_get_cstr(Line), "TR") == 0) { + App->State = UHFReaderStateWaitForNumber; + } + + // Handles the TID values + else if(strcmp(furi_string_get_cstr(Line), "TID") == 0) { + // The TID values are sent first, so initialize the index for each type of field + App->CurEpcIndex = 26; + App->CurTidIndex = 1; + App->CurResIndex = 1; + App->CurMemIndex = 1; + + bool Redraw = true; + with_view_model( + App->ViewRead, UHFReaderConfigModel * Model, { Model->CurEpcIndex = 1; }, Redraw); + App->State = UHFReaderStateWaitForTID; + } + + // Handles the Reserved Memory Values + else if(strcmp(furi_string_get_cstr(Line), "RES") == 0) { + App->State = UHFReaderStateWaitForRES; + + } + + // Handles the User Memory Values + else if(strcmp(furi_string_get_cstr(Line), "MEM") == 0) { + App->State = UHFReaderStateWaitForMEM; + + } + + // Handles modified EPC value + else if(strcmp(furi_string_get_cstr(Line), "EVOK") == 0) { + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } + + FuriString* NumEpcs = furi_string_alloc(); + FuriString* EpcAndName = furi_string_alloc(); + FuriString* TempTid = furi_string_alloc(); + FuriString* TempRes = furi_string_alloc(); + FuriString* TempMem = furi_string_alloc(); + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(TempTid, Model->TidValue); + furi_string_set(TempRes, Model->ResValue); + furi_string_set(TempMem, Model->MemValue); + furi_string_set(Model->WriteFunction, WRITE_EPC_OK); + }, + Redraw); + + furi_string_printf(NumEpcs, "Tag%ld", App->SelectedTagIndex); + + furi_string_printf( + EpcAndName, + "%s:%s:%s:%s:%s", + furi_string_get_cstr(App->EpcName), + App->TempSaveBuffer, + furi_string_get_cstr(TempTid), + furi_string_get_cstr(TempRes), + furi_string_get_cstr(TempMem)); + + if(!flipper_format_update_string_cstr( + App->EpcFile, furi_string_get_cstr(NumEpcs), furi_string_get_cstr(EpcAndName))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } + + flipper_format_file_close(App->EpcFile); + furi_string_free(NumEpcs); + furi_string_free(EpcAndName); + furi_string_free(TempTid); + furi_string_free(TempRes); + furi_string_free(TempMem); + dolphin_deed(DolphinDeedRfidAdd); + notification_message(App->Notifications, &sequence_success); + } else if(strcmp(furi_string_get_cstr(Line), "EVBAD") == 0) { + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { furi_string_set(Model->WriteFunction, WRITE_EPC_FAIL); }, + Redraw); + } else if(strcmp(furi_string_get_cstr(Line), "RVOK") == 0) { + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } + + FuriString* NumEpcs = furi_string_alloc(); + FuriString* EpcAndName = furi_string_alloc(); + FuriString* TempTid = furi_string_alloc(); + FuriString* TempEpc = furi_string_alloc(); + FuriString* TempMem = furi_string_alloc(); + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(TempTid, Model->TidValue); + furi_string_set(TempEpc, App->EpcToWrite); + furi_string_set(TempMem, Model->MemValue); + furi_string_set(Model->WriteFunction, WRITE_EPC_OK); + }, + Redraw); + + furi_string_printf(NumEpcs, "Tag%ld", App->SelectedTagIndex); + + furi_string_printf( + EpcAndName, + "%s:%s:%s:%s:%s", + furi_string_get_cstr(App->EpcName), + furi_string_get_cstr(TempEpc), + furi_string_get_cstr(TempTid), + App->TempSaveBuffer, + furi_string_get_cstr(TempMem)); + + if(!flipper_format_update_string_cstr( + App->EpcFile, furi_string_get_cstr(NumEpcs), furi_string_get_cstr(EpcAndName))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } + + flipper_format_file_close(App->EpcFile); + furi_string_free(NumEpcs); + furi_string_free(EpcAndName); + furi_string_free(TempTid); + furi_string_free(TempEpc); + furi_string_free(TempMem); + dolphin_deed(DolphinDeedRfidAdd); + notification_message(App->Notifications, &sequence_success); + } + // Handles error if write failed for reserved memory + else if(strcmp(furi_string_get_cstr(Line), "RVBAD") == 0) { + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { furi_string_set(Model->WriteFunction, WRITE_EPC_FAIL); }, + Redraw); + + } + + // Handles TID modified values + else if(strcmp(furi_string_get_cstr(Line), "TVOK") == 0) { + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } + + FuriString* NumEpcs = furi_string_alloc(); + FuriString* EpcAndName = furi_string_alloc(); + FuriString* TempRes = furi_string_alloc(); + FuriString* TempEpc = furi_string_alloc(); + FuriString* TempMem = furi_string_alloc(); + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(TempRes, Model->ResValue); + furi_string_set(TempEpc, App->EpcToWrite); + furi_string_set(TempMem, Model->MemValue); + furi_string_set(Model->WriteFunction, WRITE_EPC_OK); + }, + Redraw); + + furi_string_printf(NumEpcs, "Tag%ld", App->SelectedTagIndex); + + furi_string_printf( + EpcAndName, + "%s:%s:%s:%s:%s", + furi_string_get_cstr(App->EpcName), + furi_string_get_cstr(TempEpc), + App->TempSaveBuffer, + furi_string_get_cstr(TempRes), + furi_string_get_cstr(TempMem)); + + if(!flipper_format_update_string_cstr( + App->EpcFile, furi_string_get_cstr(NumEpcs), furi_string_get_cstr(EpcAndName))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } + + flipper_format_file_close(App->EpcFile); + furi_string_free(NumEpcs); + furi_string_free(EpcAndName); + furi_string_free(TempRes); + furi_string_free(TempEpc); + furi_string_free(TempMem); + dolphin_deed(DolphinDeedRfidAdd); + notification_message(App->Notifications, &sequence_success); + } + + // Handles error if write failed for TID + else if(strcmp(furi_string_get_cstr(Line), "TVBAD") == 0) { + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { furi_string_set(Model->WriteFunction, WRITE_EPC_FAIL); }, + Redraw); + + } + + // Handles modified user memory values + else if(strcmp(furi_string_get_cstr(Line), "UVOK") == 0) { + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } + + FuriString* NumEpcs = furi_string_alloc(); + FuriString* EpcAndName = furi_string_alloc(); + FuriString* TempTid = furi_string_alloc(); + FuriString* TempEpc = furi_string_alloc(); + FuriString* TempRes = furi_string_alloc(); + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(TempTid, Model->TidValue); + furi_string_set(TempEpc, App->EpcToWrite); + furi_string_set(TempRes, Model->ResValue); + furi_string_set(Model->WriteFunction, WRITE_EPC_OK); + }, + Redraw); + + furi_string_printf(NumEpcs, "Tag%ld", App->SelectedTagIndex); + + furi_string_printf( + EpcAndName, + "%s:%s:%s:%s:%s", + furi_string_get_cstr(App->EpcName), + furi_string_get_cstr(TempEpc), + furi_string_get_cstr(TempTid), + furi_string_get_cstr(TempRes), + App->TempSaveBuffer); + + if(!flipper_format_update_string_cstr( + App->EpcFile, furi_string_get_cstr(NumEpcs), furi_string_get_cstr(EpcAndName))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } + + flipper_format_file_close(App->EpcFile); + furi_string_free(NumEpcs); + furi_string_free(EpcAndName); + furi_string_free(TempTid); + furi_string_free(TempEpc); + furi_string_free(TempRes); + dolphin_deed(DolphinDeedRfidAdd); + notification_message(App->Notifications, &sequence_success); + } + + // Handles failed User Memory writes + else if(strcmp(furi_string_get_cstr(Line), "UVBAD") == 0) { + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { furi_string_set(Model->WriteFunction, WRITE_EPC_FAIL); }, + Redraw); + } + break; + + // Wait for the number of tags read + case UHFReaderStateWaitForNumber: + App->NumberOfEpcsToRead = atoi(furi_string_get_cstr(Line)); + memset(App->EpcValues, 0, 127 * 26); + App->State = UHFReaderStateCollectEPCs; + break; + + // Collect each EPC value + case UHFReaderStateCollectEPCs: + if(strcmp(furi_string_get_cstr(Line), "end") == 0) { + uart_helper_send(App->UartHelper, "done\n", 5); + App->State = UHFReaderStateDoneCollecting; + + } else { + App->EpcValues[App->CurEpcIndex] = strdup(furi_string_get_cstr(Line)); + App->CurEpcIndex += 26; + } + break; + + // Wait for the number of TIDs read + case UHFReaderStateWaitForTID: + App->NumberOfTidsToRead = atoi(furi_string_get_cstr(Line)); + memset(App->TidValues, 0, 127 * 41); + App->State = UHFReaderStateCollectTIDs; + break; + + // Wait for number of reserved reads + case UHFReaderStateWaitForRES: + App->NumberOfResToRead = atoi(furi_string_get_cstr(Line)); + memset(App->ResValues, 0, 127 * 17); + App->State = UHFReaderStateCollectRESs; + break; + + // Wait for number of user reads + case UHFReaderStateWaitForMEM: + App->NumberOfMemToRead = atoi(furi_string_get_cstr(Line)); + memset(App->MemValues, 0, 127 * 33); + App->State = UHFReaderStateCollectMEMs; + break; + + // Collect the TIDs read + case UHFReaderStateCollectTIDs: + if(strcmp(furi_string_get_cstr(Line), "end") == 0) { + uart_helper_send(App->UartHelper, "done\n", 5); + App->State = UHFReaderStateDoneCollectingTIDs; + } else { + App->TidValues[App->CurTidIndex * 41] = strdup(furi_string_get_cstr(Line)); + App->CurTidIndex += 1; + } + break; + + // Collect the reserved memory blocks read + case UHFReaderStateCollectRESs: + if(strcmp(furi_string_get_cstr(Line), "end") == 0) { + uart_helper_send(App->UartHelper, "done\n", 5); + App->State = UHFReaderStateDoneCollectingRESs; + + } else { + App->ResValues[App->CurResIndex * 17] = strdup(furi_string_get_cstr(Line)); + App->CurResIndex += 1; + } + break; + + // Collect the User memory blocks read + case UHFReaderStateCollectMEMs: + if(strcmp(furi_string_get_cstr(Line), "end") == 0) { + uart_helper_send(App->UartHelper, "done\n", 5); + App->State = UHFReaderStateDoneCollectingMEMs; + } else { + App->MemValues[App->CurMemIndex * 33] = strdup(furi_string_get_cstr(Line)); + App->CurMemIndex += 1; + } + break; + + // State after done collecting TIDs + case UHFReaderStateDoneCollectingTIDs: + App->CurTidIndex = 1; + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * Model, + { + if(App->NumberOfTidsToRead > 0) { + furi_string_set_str(Model->Tid, App->TidValues[App->CurTidIndex * 41]); + } + }, + Redraw); + + // Send the next command to read the EPCs from the RPi + uart_helper_send(App->UartHelper, "EPCS\n", 5); + App->State = UHFReaderStateIdle; + break; + + // State after done collecting Reserved Mem Blocks + case UHFReaderStateDoneCollectingRESs: + App->CurResIndex = 1; + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * Model, + { + if(App->NumberOfResToRead > 0) { + furi_string_set_str(Model->Reserved, App->ResValues[App->CurResIndex * 17]); + } + }, + Redraw); + uart_helper_send(App->UartHelper, "MEM\n", 4); + App->State = UHFReaderStateIdle; + break; + + // State after done collecting User Mem Blocks + case UHFReaderStateDoneCollectingMEMs: + App->CurMemIndex = 1; + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * Model, + { + if(App->NumberOfMemToRead > 0) { + furi_string_set_str(Model->User, App->MemValues[App->CurMemIndex * 33]); + } + }, + Redraw); + App->IsReading = false; + with_view_model( + App->ViewRead, UHFReaderConfigModel * Model, { Model->IsReading = false; }, Redraw); + App->State = UHFReaderStateIdle; + dolphin_deed(DolphinDeedRfidReadSuccess); + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_success); + break; + // State after done collecting EPCs + case UHFReaderStateDoneCollecting: + App->CurEpcIndex = 26; + bool Redraw = true; + with_view_model( + App->ViewRead, + UHFReaderConfigModel * Model, + { + if(App->NumberOfEpcsToRead > 0) { + furi_string_set_str(Model->EpcValue, App->EpcValues[Model->CurEpcIndex * 26]); + + Model->NumEpcsRead = App->NumberOfEpcsToRead; + } + }, + Redraw); + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * Model, + { + if(App->NumberOfEpcsToRead > 0) { + furi_string_set_str(Model->Epc, App->EpcValues[App->CurTidIndex * 26]); + } + }, + Redraw); + + uart_helper_send(App->UartHelper, "RES\n", 4); + App->State = UHFReaderStateIdle; + break; + } +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_process.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_process.h new file mode 100644 index 00000000..126c34bc --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/uart_process.h @@ -0,0 +1,4 @@ +#pragma once + +//Function Declaration +void uart_demo_process_line(FuriString* Line, void* Context); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_buffer.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_buffer.c new file mode 100644 index 00000000..f72e1d76 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_buffer.c @@ -0,0 +1,76 @@ +#include "yrm100x_buffer.h" +#include +#include + +/** + * File that handles the buffer for the YRM100 reader + * @author frux-c +*/ + +Buffer* uhf_buffer_alloc(size_t initial_capacity) { + Buffer* buf = (Buffer*)malloc(sizeof(Buffer)); + buf->data = (uint8_t*)malloc(sizeof(uint8_t) * initial_capacity); + if(!buf->data) { + free(buf); + return NULL; + } + buf->size = 0; + buf->capacity = initial_capacity; + buf->head = 0; + buf->tail = 0; + return buf; +} + +bool uhf_buffer_append_single(Buffer* buf, uint8_t data) { + if(buf->closed) return false; + buf->data[buf->tail] = data; + buf->tail = (buf->tail + 1) % buf->capacity; + if(buf->size < buf->capacity) { + buf->size++; + } else { + buf->head = (buf->head + 1) % buf->capacity; + } + return true; +} + +bool uhf_buffer_append(Buffer* buf, uint8_t* data, size_t data_size) { + if(buf->closed) return false; + for(size_t i = 0; i < data_size; i++) { + buf->data[buf->tail] = data[i]; + buf->tail = (buf->tail + 1) % buf->capacity; + if(buf->size < buf->capacity) { + buf->size++; + } else { + buf->head = (buf->head + 1) % buf->capacity; + } + } + return true; +} + +uint8_t* uhf_buffer_get_data(Buffer* buf) { + return &buf->data[buf->head]; +} + +size_t uhf_buffer_get_size(Buffer* buf) { + return buf->size; +} + +bool uhf_is_buffer_closed(Buffer* buf) { + return buf->closed; +} + +void uhf_buffer_close(Buffer* buf) { + buf->closed = true; +} + +void uhf_buffer_reset(Buffer* buf) { + buf->head = 0; + buf->tail = 0; + buf->size = 0; + buf->closed = false; +} + +void uhf_buffer_free(Buffer* buf) { + free(buf->data); + free(buf); +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_buffer.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_buffer.h new file mode 100644 index 00000000..c64a0cfb --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_buffer.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include + +/** + * File that handles the buffer for the YRM100 reader + * @author frux-c +*/ + + +#define MAX_BUFFER_SIZE 200 + +typedef struct { + uint8_t* data; + size_t size; + size_t capacity; + size_t head; + size_t tail; + bool closed; +} Buffer; + +Buffer* uhf_buffer_alloc(size_t inital_capacity); +bool uhf_buffer_append_single(Buffer* buf, uint8_t value); +bool uhf_buffer_append(Buffer* buf, uint8_t* data, size_t size); + +uint8_t* uhf_buffer_get_data(Buffer* buf); +size_t uhf_buffer_get_size(Buffer* buf); +bool uhf_is_buffer_closed(Buffer* buf); +void uhf_buffer_close(Buffer* buf); +void uhf_buffer_reset(Buffer* buf); +void uhf_buffer_free(Buffer* buf); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module.c new file mode 100644 index 00000000..48915891 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module.c @@ -0,0 +1,612 @@ +#include "yrm100x_module.h" +#include "yrm100x_module_cmd.h" +#include "saved_epc_functions.h" + +/** + * File that handles the YRM100 module + * @author frux-c + * @author modified by haffnerriley +*/ + +#define DELAY_MS 100 +#define WAIT_TICK 4000 // max wait time in between each byte + +static M100ResponseType setup_and_send_rx(M100Module* module, uint8_t* cmd, size_t cmd_length) { + UHFUart* uart = module->uart; + Buffer* buffer = uart->buffer; + // clear buffer + uhf_buffer_reset(buffer); + // send cmd + uhf_uart_send_wait(uart, cmd, cmd_length); + // wait for response by polling + while(!uhf_is_buffer_closed(buffer) && !uhf_uart_tick(uart)) { + } + // reset tick + uhf_uart_tick_reset(uart); + // Validation Checks + uint8_t* data = uhf_buffer_get_data(buffer); + size_t length = uhf_buffer_get_size(buffer); + // check if size > 0 + if(!length) return M100EmptyResponse; + // check if data is valid + if(data[0] != FRAME_START || data[length - 1] != FRAME_END) return M100ValidationFail; + // check if checksum is correct + if(checksum(data + 1, length - 3) != data[length - 2]) return M100ChecksumFail; + return M100SuccessResponse; +} + +M100ModuleInfo* m100_module_info_alloc() { + M100ModuleInfo* module_info = (M100ModuleInfo*)malloc(sizeof(M100ModuleInfo)); + return module_info; +} + +void m100_module_info_free(M100ModuleInfo* module_info) { + if(module_info->hw_version != NULL) free(module_info->hw_version); + if(module_info->sw_version != NULL) free(module_info->sw_version); + if(module_info->manufacturer != NULL) free(module_info->manufacturer); + free(module_info); +} + +M100Module* m100_module_alloc() { + M100Module* module = (M100Module*)malloc(sizeof(M100Module)); + module->transmitting_power = DEFAULT_TRANSMITTING_POWER; + module->region = DEFAULT_WORKING_REGION; + module->info = m100_module_info_alloc(); + module->uart = uhf_uart_alloc(); + module->write_mask = WRITE_EPC; + return module; +} + +void m100_module_free(M100Module* module) { + m100_module_info_free(module->info); + uhf_uart_free(module->uart); + free(module); +} + +uint8_t checksum(const uint8_t* data, size_t length) { + // CheckSum8 Modulo 256 + // Sum of Bytes % 256 + uint64_t sum_val = 0x00; + for(size_t i = 0; i < length; i++) { + sum_val += data[i]; + } + return (uint8_t)(sum_val % 0x100); +} + +uint16_t crc16_genibus(const uint8_t* data, size_t length) { + uint16_t crc = 0xFFFF; // Initial value + uint16_t polynomial = 0x1021; // CRC-16/GENIBUS polynomial + + for(size_t i = 0; i < length; i++) { + crc ^= (data[i] << 8); // Move byte into MSB of 16bit CRC + for(int j = 0; j < 8; j++) { + if(crc & 0x8000) { + crc = (crc << 1) ^ polynomial; + } else { + crc <<= 1; + } + } + } + + return crc ^ 0xFFFF; // Post-inversion +} + +char* _m100_info_helper(M100Module* module, char** info) { + if(!uhf_buffer_get_size(module->uart->buffer)) return NULL; + uint8_t* data = uhf_buffer_get_data(module->uart->buffer); + uint16_t payload_len = data[3]; + payload_len = (payload_len << 8) + data[4]; + FuriString* temp_str = furi_string_alloc(); + for(int i = 0; i < payload_len; i++) { + furi_string_cat_printf(temp_str, "%c", data[6 + i]); + } + if(*info == NULL) { + *info = (char*)malloc(sizeof(char) * payload_len); + } else { + for(size_t i = 0; i < strlen(*info); i++) { + (*info)[i] = 0; + } + } + memcpy(*info, furi_string_get_cstr(temp_str), payload_len); + furi_string_free(temp_str); + return *info; +} + +char* m100_get_hardware_version(M100Module* module) { + setup_and_send_rx(module, (uint8_t*)&CMD_HW_VERSION.cmd[0], CMD_HW_VERSION.length); + return _m100_info_helper(module, &module->info->hw_version); +} +char* m100_get_software_version(M100Module* module) { + setup_and_send_rx(module, (uint8_t*)&CMD_SW_VERSION.cmd[0], CMD_SW_VERSION.length); + return _m100_info_helper(module, &module->info->sw_version); +} +char* m100_get_manufacturers(M100Module* module) { + setup_and_send_rx(module, (uint8_t*)&CMD_MANUFACTURERS.cmd[0], CMD_MANUFACTURERS.length); + return _m100_info_helper(module, &module->info->manufacturer); +} + +M100ResponseType m100_single_poll(M100Module* module, UHFTag* uhf_tag) { + M100ResponseType rp_type = + setup_and_send_rx(module, (uint8_t*)&CMD_SINGLE_POLLING.cmd[0], CMD_SINGLE_POLLING.length); + if(rp_type != M100SuccessResponse) return rp_type; + uint8_t* data = uhf_buffer_get_data(module->uart->buffer); + uint16_t pc = data[6]; + uint16_t crc = 0; + // mask out epc length from protocol control + size_t epc_len = pc; + epc_len >>= 3; + epc_len *= 2; + // get protocol control + pc <<= 8; + pc += data[7]; + // get cyclic redundency check + crc = data[8 + epc_len]; + crc <<= 8; + crc += data[8 + epc_len + 1]; + // validate crc + if(crc16_genibus(data + 6, epc_len + 2) != crc) return M100ValidationFail; + uhf_tag_set_epc_pc(uhf_tag, pc); + uhf_tag_set_epc_crc(uhf_tag, crc); + uhf_tag_set_epc(uhf_tag, data + 8, epc_len); + return M100SuccessResponse; +} + +M100ResponseType m100_set_select(M100Module* module, UHFTag* uhf_tag) { + // Set select + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_SET_SELECT_PARAMETER.length; + size_t mask_length_bytes = uhf_tag->epc->size; + size_t mask_length_bits = mask_length_bytes * 8; + // payload len == sel param len + ptr len + mask len + epc len + size_t payload_len = 7 + mask_length_bytes; + memcpy(cmd, CMD_SET_SELECT_PARAMETER.cmd, cmd_length); + // set new length + cmd_length = 12 + mask_length_bytes + 2; + // set payload length + cmd[3] = (payload_len >> 8) & 0xFF; + cmd[4] = payload_len & 0xFF; + // set select param + cmd[5] = 0x01; // 0x00=rfu, 0x01=epc, 0x10=tid, 0x11=user + // set ptr + cmd[9] = 0x20; // epc data begins after 0x20 + // set mask length + cmd[10] = mask_length_bits; + // truncate + cmd[11] = false; + // set mask + memcpy((void*)&cmd[12], uhf_tag->epc->data, mask_length_bytes); + + // set checksum + cmd[cmd_length - 2] = checksum(cmd + 1, 11 + mask_length_bytes); + // end frame + cmd[cmd_length - 1] = FRAME_END; + + M100ResponseType rp_type = setup_and_send_rx(module, cmd, 12 + mask_length_bytes + 3); + + if(rp_type != M100SuccessResponse) return rp_type; + + uint8_t* data = uhf_buffer_get_data(module->uart->buffer); + if(data[5] != 0x00) return M100ValidationFail; // error if not 0 + + return M100SuccessResponse; +} + +void m100_enable_write_mask(M100Module* module, WriteMask mask) { + module->write_mask |= mask; +} + +void m100_disable_write_mask(M100Module* module, WriteMask mask) { + module->write_mask &= ~mask; +} + +bool m100_is_write_mask_enabled(M100Module* module, WriteMask mask) { + return (module->write_mask & mask) == mask; +} + +UHFTag* m100_get_select_param(M100Module* module) { + uhf_buffer_reset(module->uart->buffer); + // furi_hal_uart_set_irq_cb(FuriHalUartIdLPUART1, rx_callback, module->uart->buffer); + // furi_hal_uart_tx( + // FuriHalUartIdUSART1, + // (uint8_t*)&CMD_GET_SELECT_PARAMETER.cmd, + // CMD_GET_SELECT_PARAMETER.length); + // furi_delay_ms(DELAY_MS); + // UHFTag* uhf_tag = uhf_tag_alloc(); + // uint8_t* data = buffer_get_data(module->uart->buffer); + // size_t mask_length = + // uhf_tag_set_epc(uhf_tag, data + 12, ) + // TODO : implement + return NULL; +} + +//Modified by William Riley Haffner (haffnerriley) +M100ResponseType m100_read_label_data_storage( + M100Module* module, + UHFTag* uhf_tag, + BankType bank, + uint32_t access_pwd, + uint16_t word_count) { + /* + Will probably remove UHFTag as param and get it from get selected tag + */ + if(bank == EPCBank) return M100SuccessResponse; + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_READ_LABEL_DATA_STORAGE_AREA.length; + memcpy(cmd, CMD_READ_LABEL_DATA_STORAGE_AREA.cmd, cmd_length); + // set access password + cmd[5] = (access_pwd >> 24) & 0xFF; + cmd[6] = (access_pwd >> 16) & 0xFF; + cmd[7] = (access_pwd >> 8) & 0xFF; + cmd[8] = access_pwd & 0xFF; + // set mem bank + cmd[9] = (uint8_t)bank; + // set word counter + + if(bank == ReservedBank) { + word_count = 4; + } + cmd[12] = (word_count >> 8) & 0xFF; + cmd[13] = word_count & 0xFF; + // calc checksum + cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3); + + M100ResponseType rp_type = setup_and_send_rx(module, cmd, cmd_length); + if(rp_type != M100SuccessResponse) return rp_type; + + uint8_t* data = uhf_buffer_get_data(module->uart->buffer); + + uint8_t rtn_command = data[2]; + uint16_t payload_len = data[3]; + payload_len = (payload_len << 8) + data[4]; + + if(rtn_command == 0xFF) { + if(payload_len == 0x01) return M100NoTagResponse; + return M100MemoryOverrun; + } + + size_t ptr_offset = 5 /*<-ptr offset*/ + uhf_tag_get_epc_size(uhf_tag) + 3 /*<-pc + ul*/; + size_t bank_data_length = payload_len - (ptr_offset - 5 /*dont include the offset*/); + + if(bank == TIDBank) { + uhf_tag_set_tid(uhf_tag, data + ptr_offset, bank_data_length); + } else if(bank == UserBank) { + uhf_tag_set_user(uhf_tag, data + ptr_offset, bank_data_length); + } else if(bank == ReservedBank) { + uhf_tag_set_kill_pwd(uhf_tag, data + ptr_offset, bank_data_length); + uhf_tag_set_access_pwd(uhf_tag, data + ptr_offset, bank_data_length); + } + + return M100SuccessResponse; +} + + +//Created by William Riley Haffner (haffnerriley) +//Function that handles creating the lock parameter bytes in the lock payload +//Follows the Gen2 Standards: https://www.gs1.org/sites/default/files/docs/epc/Gen2_Protocol_Standard.pdf +uint32_t get_lock_param(uint32_t lock_param, BankType bank, LockType lockfunction){ + + if(lockfunction == Lock){ + switch (bank) { + case KillPwd: + lock_param = 0x80200; + break; + case AccessPwd: + lock_param = 0x20080; + break; + case EPCBank: + lock_param = 0x08020; + break; + case TIDBank: + lock_param = 0x02008; + break; + case FileZero: + lock_param = 0x00802; + break; + default: + return 0x02008; + } + } + else if(lockfunction == PermaLock){ + switch (bank) { + case KillPwd: + lock_param = 0xC0300; + break; + case AccessPwd: + lock_param = 0x300C0; + break; + case EPCBank: + lock_param = 0x0C030; + break; + case TIDBank: + lock_param = 0x0300C; + break; + case FileZero: + lock_param = 0x00C03; + break; + default: + return 0x300C0; + } + } + else if (lockfunction == PermaUnlock){ + switch (bank) { + case KillPwd: + lock_param = 0x40100; + break; + case AccessPwd: + lock_param = 0x10040; + break; + case EPCBank: + lock_param = 0x04010; + break; + case TIDBank: + lock_param = 0x01004; + break; + case FileZero: + lock_param = 0x00401; + break; + default: + return 0x10040; + } + } + else { + switch (bank) { + case KillPwd: + lock_param = 0xC0000; + break; + case AccessPwd: + lock_param = 0x30000; + break; + case EPCBank: + lock_param = 0x0C000; + break; + case TIDBank: + lock_param = 0x03000; + break; + case FileZero: + lock_param = 0x00C00; + break; + default: + return 0x30000; + } + } + + return lock_param; + + +} +//Created by William Riley Haffner +//Handles locking the uhf tag +M100ResponseType m100_lock_label_data(M100Module* module, BankType bank, uint32_t access_pwd, LockType lockfunction) { + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_LOCK_LABEL_DATA_STORE.length; + uint32_t lock_param = 0; + memcpy(cmd, CMD_LOCK_LABEL_DATA_STORE.cmd, cmd_length); + + cmd[5] = (access_pwd >> 24) & 0xFF; + cmd[6] = (access_pwd >> 16) & 0xFF; + cmd[7] = (access_pwd >> 8) & 0xFF; + cmd[8] = access_pwd & 0xFF; + + lock_param = get_lock_param(lock_param, bank, lockfunction); + + cmd[9] = (lock_param >> 16) & 0xFF; + cmd[10] = (lock_param >> 8) & 0xFF; + cmd[11] = lock_param & 0xFF; + + + // Calculate checksum + cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3); + + // Set end frame + cmd[cmd_length - 1] = FRAME_END; + + // Send command and receive response + M100ResponseType rp_type = setup_and_send_rx(module, cmd, cmd_length); + if(rp_type != M100SuccessResponse) return rp_type; + + uint8_t* data = uhf_buffer_get_data(module->uart->buffer); + + // Validate response + uint8_t rtn_command = data[2]; + uint16_t payload_len = data[3]; + payload_len = (payload_len << 8) + data[4]; + + + //Need to add a check here for an incorrect password. Look for an error code of some sort + if(rtn_command == 0xFF) { + if(payload_len == 0x01) return M100NoTagResponse; + + else if(data[2] == 0xFF && (payload_len == 16 && data[5] == 0x16)) return M100APWrong; + + return M100MemoryOverrun; + } + + return M100SuccessResponse; +} + +//Created by William Riley Haffner (haffnerriley) +//Kills the tag +M100ResponseType m100_kill_tag(M100Module* module, uint32_t kill_pwd) { + + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_INACTIVATE_KILL_TAG.length; + memcpy(cmd, CMD_INACTIVATE_KILL_TAG.cmd, cmd_length); + + cmd[5] = (kill_pwd >> 24) & 0xFF; + cmd[6] = (kill_pwd >> 16) & 0xFF; + cmd[7] = (kill_pwd >> 8) & 0xFF; + cmd[8] = kill_pwd & 0xFF; + + // Calculate checksum + cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3); + + // Set end frame + cmd[cmd_length - 1] = FRAME_END; + + // Send command and receive response + M100ResponseType rp_type = setup_and_send_rx(module, cmd, cmd_length); + if(rp_type != M100SuccessResponse) return rp_type; + + uint8_t* data = uhf_buffer_get_data(module->uart->buffer); + + // Validate response + uint8_t rtn_command = data[2]; + uint16_t payload_len = data[3]; + payload_len = (payload_len << 8) + data[4]; + + if(rtn_command == 0xFF) { + if(payload_len == 0x01) return M100NoTagResponse; + else if(data[2] == 0xFF && (payload_len == 16 && data[5] == 0x12)) return M100APWrong; + return M100MemoryOverrun; + } + + return M100SuccessResponse; +} + +//Modified by William Riley Haffner to add TID and reserved write support +M100ResponseType m100_write_label_data_storage( + M100Module* module, + UHFTag* saved_tag, + UHFTag* selected_tag, + BankType bank, + uint16_t source_address, + uint32_t access_pwd) { + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_WRITE_LABEL_DATA_STORE.length; + memcpy(cmd, CMD_WRITE_LABEL_DATA_STORE.cmd, cmd_length); + uint16_t payload_len = 9; + uint16_t data_length = 0; + if(bank == ReservedBank) { + + //Handles writing the access password and uses the saved password + if(source_address == 32) { + source_address = 0; + payload_len += 8; + data_length = 8; + memcpy(cmd + 14, uhf_tag_get_kill_pwd(saved_tag), 4); + memcpy(cmd + 18, uhf_tag_get_access_pwd(saved_tag), 4); + + } else if(source_address == 1) { + //For both + source_address = 0; + payload_len += 8; + data_length = 8; + memcpy(cmd + 14, uhf_tag_get_kill_pwd(saved_tag), 4); + memcpy(cmd + 18, uhf_tag_get_access_pwd(saved_tag), 4); + } else { + payload_len += 4; + data_length = 4; + memcpy(cmd + 14, uhf_tag_get_kill_pwd(saved_tag), 4); + } + } else if(bank == EPCBank) { + // epc len + pc len + payload_len += 4 + uhf_tag_get_epc_size(saved_tag); + data_length = 4 + uhf_tag_get_epc_size(saved_tag); + // set data + uint8_t tmp_arr[4]; + tmp_arr[0] = (uint8_t)((uhf_tag_get_epc_crc(selected_tag) >> 8) & 0xFF); + tmp_arr[1] = (uint8_t)(uhf_tag_get_epc_crc(selected_tag) & 0xFF); + tmp_arr[2] = (uint8_t)((uhf_tag_get_epc_pc(saved_tag) >> 8) & 0xFF); + tmp_arr[3] = (uint8_t)(uhf_tag_get_epc_pc(saved_tag) & 0xFF); + memcpy(cmd + 14, tmp_arr, 4); + memcpy(cmd + 18, uhf_tag_get_epc(saved_tag), uhf_tag_get_epc_size(saved_tag)); + } else if(bank == TIDBank) { + payload_len += uhf_tag_get_tid_size(saved_tag); + data_length = uhf_tag_get_tid_size(saved_tag); + // set data + memcpy(cmd + 14, uhf_tag_get_tid(saved_tag), uhf_tag_get_tid_size(saved_tag)); + } else if(bank == UserBank) { + payload_len += uhf_tag_get_user_size(saved_tag); + data_length = uhf_tag_get_user_size(saved_tag); + // set data + memcpy(cmd + 14, uhf_tag_get_user(saved_tag), uhf_tag_get_user_size(saved_tag)); + } + + // set payload length + cmd[3] = (payload_len >> 8) & 0xFF; + cmd[4] = payload_len & 0xFF; + // set access password + cmd[5] = (access_pwd >> 24) & 0xFF; + cmd[6] = (access_pwd >> 16) & 0xFF; + cmd[7] = (access_pwd >> 8) & 0xFF; + cmd[8] = access_pwd & 0xFF; + // set membank + cmd[9] = (uint8_t)bank; + // set source address + cmd[10] = (source_address >> 8) & 0xFF; + cmd[11] = source_address & 0xFF; + // set data length + size_t data_length_words = data_length / 2; + cmd[12] = (data_length_words >> 8) & 0xFF; + cmd[13] = data_length_words & 0xFF; + // update cmd len + cmd_length = 7 + payload_len; + // calculate checksum + cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3); + cmd[cmd_length - 1] = FRAME_END; + // send cmd + M100ResponseType rp_type = setup_and_send_rx(module, cmd, cmd_length); + if(rp_type != M100SuccessResponse) return rp_type; + uint8_t* buff_data = uhf_buffer_get_data(module->uart->buffer); + size_t buff_length = uhf_buffer_get_size(module->uart->buffer); + if(buff_data[2] == 0xFF && buff_length == 8) + return M100NoTagResponse; + else if(buff_data[2] == 0xFF && (buff_length == 23 || buff_data[5] == 0x16)) + return M100APWrong; + else if(buff_data[2] == 0xFF) + return M100ValidationFail; + return M100SuccessResponse; +} + +void m100_set_baudrate(M100Module* module, uint32_t baudrate) { + size_t length = CMD_SET_COMMUNICATION_BAUD_RATE.length; + uint8_t cmd[length]; + memcpy(cmd, CMD_SET_COMMUNICATION_BAUD_RATE.cmd, length); + uint16_t br_mod = baudrate / 100; // module format + cmd[6] = 0xFF & br_mod; // pow LSB + cmd[5] = 0xFF & (br_mod >> 8); // pow MSB + cmd[length - 2] = checksum(cmd + 1, length - 3); + // setup_and_send_rx(module, cmd, length); + uhf_uart_send_wait(module->uart, cmd, length); + uhf_uart_set_baudrate(module->uart, baudrate); + module->uart->baudrate = baudrate; +} + +bool m100_set_working_region(M100Module* module, WorkingRegion region) { + size_t length = CMD_SET_WORK_AREA.length; + uint8_t cmd[length]; + memcpy(cmd, CMD_SET_WORK_AREA.cmd, length); + cmd[5] = (uint8_t)region; + cmd[length - 2] = checksum(cmd + 1, length - 3); + setup_and_send_rx(module, cmd, length); + module->region = region; + return true; +} + +bool m100_set_transmitting_power(M100Module* module, uint16_t power) { + size_t length = CMD_SET_TRANSMITTING_POWER.length; + uint8_t cmd[length]; + memcpy(cmd, CMD_SET_TRANSMITTING_POWER.cmd, length); + cmd[5] = (power >> 8) & 0xFF; + cmd[6] = power & 0xFF; + cmd[length - 2] = checksum(cmd + 1, length - 3); + setup_and_send_rx(module, cmd, length); + module->transmitting_power = power; + return true; +} + +bool m100_set_freq_hopping(M100Module* module, bool hopping) { + UNUSED(module); + UNUSED(hopping); + return true; +} + +bool m100_set_power(M100Module* module, uint8_t* power) { + UNUSED(module); + UNUSED(power); + return true; +} + +uint32_t m100_get_baudrate(M100Module* module) { + return module->uart->baudrate; +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module.h new file mode 100644 index 00000000..1868090e --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module.h @@ -0,0 +1,112 @@ +#pragma once +#include +#include +#include +#include +#include "yrm100x_uart.h" +#include "yrm100x_tag.h" +#include "yrm100x_buffer.h" +#include "yrm100x_module_settings.h" + +/** + * File that handles the YRM100 module + * @author frux-c + * @author modified by haffnerriley +*/ + + +#define FRAME_START 0xBB +#define FRAME_END 0x7E +#define DEFAULT_BAUDRATE BAUD_RATES[BAUD_RATES_COUNT - 1] +#define DEFAULT_TRANSMITTING_POWER POWER_DBM[POWER_DBM_COUNT - 1] +#define DEFAULT_WORKING_REGION WR_US + + + +typedef struct { + char* hw_version; + char* sw_version; + char* manufacturer; +} M100ModuleInfo; + +typedef enum { + M100SuccessResponse, + M100ValidationFail, + M100NoTagResponse, + M100MemoryOverrun, + M100EmptyResponse, + M100ChecksumFail, + M100APWrong +} M100ResponseType; + +typedef enum { + WRITE_EPC = 1 << 0, + WRITE_TID = 1 << 1, + WRITE_USER = 1 << 2, + WRITE_RFU = 1 << 3 +} WriteMask; + +typedef struct { + M100ModuleInfo* info; + WorkingRegion region; + uint16_t region_frequency; + uint16_t transmitting_power; + uint16_t max_transmitting_power; + uint16_t write_mask; + bool freq_hopping; + UHFUart* uart; +} M100Module; + +M100ModuleInfo* m100_module_info_alloc(); +void m100_module_info_free(M100ModuleInfo* module_info); + +M100Module* m100_module_alloc(); +void m100_module_free(M100Module* module); +uint16_t crc16_genibus(const uint8_t* data, size_t length); +uint8_t checksum(const uint8_t* data, size_t length); +uint8_t get_baudrate_count(); + +// Function prototypes +char* m100_get_hardware_version(M100Module* module); +char* m100_get_software_version(M100Module* module); +char* m100_get_manufacturers(M100Module* module); + +void m100_set_baudrate(M100Module* module, uint32_t baudrate); +bool m100_set_working_region(M100Module* module, WorkingRegion region); +bool m100_set_transmitting_power(M100Module* module, uint16_t power); +bool m100_set_freq_hopping(M100Module* module, bool hopping); +bool m100_set_power(M100Module* module, uint8_t* power); + +// gen2 cmds +M100ResponseType m100_single_poll(M100Module* module, UHFTag* uhf_tag); +M100ResponseType m100_set_select(M100Module* module, UHFTag* uhf_tag); +M100ResponseType m100_read_label_data_storage( + M100Module* module, + UHFTag* uhf_tag, + BankType bank, + uint32_t access_pwd, + uint16_t word_count); + +M100ResponseType m100_write_label_data_storage( + M100Module* module, + UHFTag* saved_tag, + UHFTag* selected_tag, + BankType bank, + uint16_t source_address, + uint32_t access_pwd); + +//Written by William Riley Haffner +M100ResponseType m100_lock_label_data(M100Module* module, BankType bank, uint32_t access_pwd, LockType lockfunction); +//Written by William Riley Haffner +M100ResponseType m100_kill_tag( + M100Module* module, + uint32_t kill_pwd); + +//Written by William Riley Haffner +uint32_t get_lock_param(uint32_t lock_param, BankType bank, LockType lockfunction); + + +uint32_t m100_get_baudrate(M100Module* module); +void m100_enable_write_mask(M100Module* module, WriteMask mask); +void m100_disable_write_mask(M100Module* module, WriteMask mask); +bool m100_is_write_mask_enabled(M100Module* module, WriteMask mask); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module_cmd.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module_cmd.h new file mode 100644 index 00000000..2e4cb7c2 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module_cmd.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include + +/** + * File that handles the commands for the YRM100 module + * @author frux-c +*/ + +typedef struct { + const uint8_t* cmd; + size_t length; +} Command; + +// Define the command data arrays +static const uint8_t CMD_HW_VERSION_DATA[] = {0xBB, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x7E}; +static const uint8_t CMD_SW_VERSION_DATA[] = {0xBB, 0x00, 0x03, 0x00, 0x01, 0x01, 0x05, 0x7E}; +static const uint8_t CMD_MANUFACTURERS_DATA[] = {0xBB, 0x00, 0x03, 0x00, 0x01, 0x02, 0x06, 0x7E}; +static const uint8_t CMD_SINGLE_POLLING_DATA[] = {0xBB, 0x00, 0x22, 0x00, 0x00, 0x22, 0x7E}; +static const uint8_t CMD_MULTIPLE_POLLING_DATA[] = {0xBB, 0x00, 0x27, 0x00, 0x03, 0x22, 0x27, 0x10, 0x83, 0x7E}; +static const uint8_t CMD_STOP_MULTIPLE_POLLING_DATA[] = {0xBB, 0x00, 0x28, 0x00, 0x00, 0x28, 0x7E}; +static const uint8_t CMD_SET_SELECT_PARAMETER_DATA[] = {0xBB, 0x00, 0x0C, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x20, 0x60, 0x00, 0x30, 0x75, 0x1F, 0xEB, 0x70, 0x5C, 0x59, 0x04, 0xE3, 0xD5, 0x0D, 0x70, 0xAD, 0x7E}; +static const uint8_t CMD_GET_SELECT_PARAMETER_DATA[] = {0xBB, 0x00, 0x0B, 0x00, 0x00, 0x0B, 0x7E}; +static const uint8_t CMD_SET_SELECT_MODE_DATA[] = {0xBB, 0x00, 0x12, 0x00, 0x01, 0x01, 0x14, 0x7E}; +static const uint8_t CMD_READ_LABEL_DATA_STORAGE_AREA_DATA[] = {0xBB, 0x00, 0x39, 0x00, 0x09, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x02, 0x45, 0x7E}; +static const uint8_t CMD_WRITE_LABEL_DATA_STORE_DATA[] = {0xBB, 0x00, 0x49, 0x00, 0x0D, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78, 0x6D, 0x7E}; +static const uint8_t CMD_LOCK_LABEL_DATA_STORE_DATA[] = {0xBB, 0x00, 0x82, 0x00, 0x07, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0x80, 0x09, 0x7E}; +static const uint8_t CMD_INACTIVATE_KILL_TAG_DATA[] = {0xBB, 0x00, 0x65, 0x00, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x67, 0x7E}; +static const uint8_t CMD_SET_COMMUNICATION_BAUD_RATE_DATA[] = {0xBB, 0x00, 0x11, 0x00, 0x02, 0x00, 0xC0, 0xD3, 0x7E}; +static const uint8_t CMD_GET_QUERY_PARAMETERS_DATA[] = {0xBB, 0x00, 0x0D, 0x00, 0x00, 0x0D, 0x7E}; +static const uint8_t CMD_SET_QUERY_PARAMETER_DATA[] = {0xBB, 0x00, 0x0E, 0x00, 0x02, 0x10, 0x20, 0x40, 0x7E}; +static const uint8_t CMD_SET_WORK_AREA_DATA[] = {0xBB, 0x00, 0x07, 0x00, 0x01, 0x01, 0x09, 0x7E}; +static const uint8_t CMD_SET_TRANSMITTING_POWER_DATA[] = {0xBB, 0x00, 0xB6, 0x00, 0x02, 0x07, 0xD0, 0x8F, 0x7E}; + + +// Define the Command structs +static const Command CMD_HW_VERSION = {CMD_HW_VERSION_DATA, sizeof(CMD_HW_VERSION_DATA)}; +static const Command CMD_SW_VERSION = {CMD_SW_VERSION_DATA, sizeof(CMD_SW_VERSION_DATA)}; +static const Command CMD_MANUFACTURERS = {CMD_MANUFACTURERS_DATA, sizeof(CMD_MANUFACTURERS_DATA)}; +static const Command CMD_SINGLE_POLLING = {CMD_SINGLE_POLLING_DATA, sizeof(CMD_SINGLE_POLLING_DATA)}; +static const Command CMD_MULTIPLE_POLLING = {CMD_MULTIPLE_POLLING_DATA, sizeof(CMD_MULTIPLE_POLLING_DATA)}; +static const Command CMD_STOP_MULTIPLE_POLLING = {CMD_STOP_MULTIPLE_POLLING_DATA, sizeof(CMD_STOP_MULTIPLE_POLLING_DATA)}; +static const Command CMD_SET_SELECT_PARAMETER = {CMD_SET_SELECT_PARAMETER_DATA, sizeof(CMD_SET_SELECT_PARAMETER_DATA)}; +static const Command CMD_GET_SELECT_PARAMETER = {CMD_GET_SELECT_PARAMETER_DATA, sizeof(CMD_GET_SELECT_PARAMETER_DATA)}; +static const Command CMD_SET_SELECT_MODE = {CMD_SET_SELECT_MODE_DATA, sizeof(CMD_SET_SELECT_MODE_DATA)}; +static const Command CMD_READ_LABEL_DATA_STORAGE_AREA = {CMD_READ_LABEL_DATA_STORAGE_AREA_DATA, sizeof(CMD_READ_LABEL_DATA_STORAGE_AREA_DATA)}; +static const Command CMD_WRITE_LABEL_DATA_STORE = {CMD_WRITE_LABEL_DATA_STORE_DATA, sizeof(CMD_WRITE_LABEL_DATA_STORE_DATA)}; +static const Command CMD_LOCK_LABEL_DATA_STORE = {CMD_LOCK_LABEL_DATA_STORE_DATA, sizeof(CMD_LOCK_LABEL_DATA_STORE_DATA)}; +static const Command CMD_INACTIVATE_KILL_TAG = {CMD_INACTIVATE_KILL_TAG_DATA, sizeof(CMD_INACTIVATE_KILL_TAG_DATA)}; +static const Command CMD_SET_COMMUNICATION_BAUD_RATE = {CMD_SET_COMMUNICATION_BAUD_RATE_DATA, sizeof(CMD_SET_COMMUNICATION_BAUD_RATE_DATA)}; +static const Command CMD_GET_QUERY_PARAMETERS = {CMD_GET_QUERY_PARAMETERS_DATA, sizeof(CMD_GET_QUERY_PARAMETERS_DATA)}; +static const Command CMD_SET_QUERY_PARAMETER = {CMD_SET_QUERY_PARAMETER_DATA, sizeof(CMD_SET_QUERY_PARAMETER_DATA)}; +static const Command CMD_SET_WORK_AREA = {CMD_SET_WORK_AREA_DATA, sizeof(CMD_SET_WORK_AREA_DATA)}; +static const Command CMD_SET_TRANSMITTING_POWER = {CMD_SET_TRANSMITTING_POWER_DATA, sizeof(CMD_SET_TRANSMITTING_POWER_DATA)}; \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module_settings.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module_settings.h new file mode 100644 index 00000000..ff080f02 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_module_settings.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +/** + * File that handles the YRM100 Module settings + * @author frux-c +*/ + + +// UHF module regions +typedef enum { + WR_CHINA_900 = 1, // Freq_CH-920.125M + WR_US, // Freq_CH-902.25M + WR_EU, // Freq_CH-865.1M + WR_CHINA_800, // Freq_CH-840.125M + WR_KOREA = 6 // Freq_CH-917.1M +} WorkingRegion; + +// UHF module baudrates +static const uint32_t BAUD_RATES[] = {9600, 19200, 115200}; +static const uint8_t BAUD_RATES_COUNT = sizeof(BAUD_RATES) / sizeof(BAUD_RATES[0]); +// RF Power Setting +static const uint8_t POWER_DBM[] = {12, 14, 17, 20}; // To be determined ... +static const uint8_t POWER_DBM_COUNT = sizeof(POWER_DBM) / sizeof(POWER_DBM[0]); +// UHF WorkingArea +static const char* WORKING_REGIONS_STR[] = {"CN1", "US", "EU", "CN2", "KR"}; +static const uint8_t __working_region_str = + sizeof(WORKING_REGIONS_STR) / sizeof(WORKING_REGIONS_STR[0]); +static const WorkingRegion WORKING_REGIONS[] = {WR_CHINA_900, WR_US, WR_EU, WR_CHINA_800, WR_KOREA}; +static const uint8_t WORKING_REGIONS_COUNT = sizeof(WORKING_REGIONS) / sizeof(WORKING_REGIONS[0]); +// UHF WorkingChannel +// static const string WORKING_CHANNELS_STR[] = {"China 900MHz", "US", "EU", "China 800MHz", "Korea"}; +// static const WorkingChannel WORKING_CHANNELS[] = {WC_CHINA_900, WC_US, WC_EU, WC_CHINA_800, WC_KOREA}; +// static const uint8_t WORKING_CHANNELS_COUNT = sizeof(WORKING_CHANNELS) / sizeof(WORKING_CHANNELS[0]); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_tag.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_tag.c new file mode 100644 index 00000000..e3d29f68 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_tag.c @@ -0,0 +1,151 @@ +#include "yrm100x_tag.h" +#include +#include + +/** + * File that handles the tag objects for the YRM100 + * @author frux-c + * @author modified by haffnerriley +*/ + +UHFTagWrapper* uhf_tag_wrapper_alloc() { + UHFTagWrapper* uhf_tag_wrapper = (UHFTagWrapper*)malloc(sizeof(UHFTagWrapper)); + uhf_tag_wrapper->uhf_tag = NULL; + return uhf_tag_wrapper; +} + +void uhf_tag_wrapper_set_tag(UHFTagWrapper* uhf_tag_wrapper, UHFTag* uhf_tag) { + if(uhf_tag_wrapper->uhf_tag != NULL) { + uhf_tag_free(uhf_tag_wrapper->uhf_tag); + } + uhf_tag_wrapper->uhf_tag = uhf_tag; +} + +void uhf_tag_wrapper_free(UHFTagWrapper* uhf_tag_wrapper) { + uhf_tag_free(uhf_tag_wrapper->uhf_tag); + free(uhf_tag_wrapper); +} + +UHFTag* uhf_tag_alloc() { + UHFTag* uhf_tag = (UHFTag*)malloc(sizeof(UHFTag)); + uhf_tag->reserved = (ReservedMemoryBank*)malloc(sizeof(ReservedMemoryBank)); + uhf_tag->epc = (EPCMemoryBank*)malloc(sizeof(EPCMemoryBank)); + uhf_tag->tid = (TIDMemoryBank*)malloc(sizeof(TIDMemoryBank)); + uhf_tag->user = (UserMemoryBank*)malloc(sizeof(UserMemoryBank)); + return uhf_tag; +} + +void uhf_tag_reset(UHFTag* uhf_tag) { + uhf_tag->epc->crc = 0; + uhf_tag->epc->pc = 0; + uhf_tag->epc->size = 0; + uhf_tag->tid->size = 0; + uhf_tag->user->size = 0; + memset(uhf_tag->epc->data, 0, 200); + memset(uhf_tag->tid->data, 0, 200); + memset(uhf_tag->user->data, 0, 200); + memset(uhf_tag->reserved->kill_password, 0, 4); + memset(uhf_tag->reserved->access_password, 0, 4); +} + +void uhf_tag_free(UHFTag* uhf_tag) { + if(uhf_tag == NULL) return; + free(uhf_tag->reserved); + free(uhf_tag->epc); + free(uhf_tag->tid); + free(uhf_tag->user); + free(uhf_tag); +} + +void uhf_tag_set_epc_pc(UHFTag* uhf_tag, uint16_t pc) { + uhf_tag->epc->pc = pc; +} + + +void uhf_tag_set_kill_pwd(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + if (size >= 4) { + memcpy(uhf_tag->reserved->kill_password, data_in, 4); + } +} + +void uhf_tag_set_access_pwd(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + if (size >= 8) { + memcpy(uhf_tag->reserved->access_password, data_in + size - 4, 4); + } + else if (size >= 4) { + memcpy(uhf_tag->reserved->access_password, data_in, 4); + } +} + +void uhf_tag_set_epc_crc(UHFTag* uhf_tag, uint16_t crc) { + uhf_tag->epc->crc = crc; +} + +void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + memcpy(uhf_tag->epc->data, data_in, size); + uhf_tag->epc->size = size; +} + +void uhf_tag_set_epc_size(UHFTag* uhf_tag, size_t size) { + uhf_tag->epc->size = size; +} + +void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + memcpy(uhf_tag->tid->data, data_in, size); + uhf_tag->tid->size = size; +} + +void uhf_tag_set_tid_size(UHFTag* uhf_tag, size_t size) { + uhf_tag->tid->size = size; +} + +void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + memcpy(uhf_tag->user->data, data_in, size); + uhf_tag->user->size = size; +} + +void uhf_tag_set_user_size(UHFTag* uhf_tag, size_t size) { + uhf_tag->user->size = size; +} + +// getters + +uint8_t* uhf_tag_get_epc(UHFTag* uhf_tag) { + return uhf_tag->epc->data; +} + +size_t uhf_tag_get_epc_size(UHFTag* uhf_tag) { + return uhf_tag->epc->size; +} + +uint16_t uhf_tag_get_epc_pc(UHFTag* uhf_tag) { + return uhf_tag->epc->pc; +} + +uint16_t uhf_tag_get_epc_crc(UHFTag* uhf_tag) { + return uhf_tag->epc->crc; +} + +uint8_t* uhf_tag_get_tid(UHFTag* uhf_tag) { + return uhf_tag->tid->data; +} + +size_t uhf_tag_get_tid_size(UHFTag* uhf_tag) { + return uhf_tag->tid->size; +} + +uint8_t* uhf_tag_get_user(UHFTag* uhf_tag) { + return uhf_tag->user->data; +} + +size_t uhf_tag_get_user_size(UHFTag* uhf_tag) { + return uhf_tag->user->size; +} + +uint8_t* uhf_tag_get_access_pwd(UHFTag* uhf_tag) { + return uhf_tag->reserved->access_password; +} +uint8_t* uhf_tag_get_kill_pwd(UHFTag* uhf_tag) { + return uhf_tag->reserved->kill_password; +} + diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_tag.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_tag.h new file mode 100644 index 00000000..f97d9bd9 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_tag.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include + +/** + * File that handles the tag objects for the YRM100 + * @author frux-c + * @author modified by haffnerriley +*/ + + +#define MAX_BANK_SIZE 200 +// storage enum +typedef enum { ReservedBank, EPCBank, TIDBank, UserBank, KillPwd, AccessPwd, FileZero} BankType; + +typedef enum { PermaLock, Lock, Unlock, PermaUnlock} LockType; + +// Reserved Memory Bank ***Revised by Wiliam Riley Haffner*** +typedef struct { + uint8_t kill_password[4]; // 4 bytes (32 bits) for kill password + uint8_t access_password[4]; // 4 bytes (32 bits) for access password +} ReservedMemoryBank; + +// EPC Memory Bank +typedef struct { + size_t size; // Size of EPC memory data + uint8_t data[MAX_BANK_SIZE]; // 2 bytes for CRC16, 2 bytes for PC, and max 14 bytes for EPC + uint16_t pc; + uint16_t crc; +} EPCMemoryBank; + +// TID Memory Bank +typedef struct { + size_t size; // Size of TID memory data + uint8_t data[MAX_BANK_SIZE]; // 4 bytes for Class ID +} TIDMemoryBank; + +// User Memory Bank +typedef struct { + size_t size; // Size of user memory data + uint8_t data[MAX_BANK_SIZE]; // Assuming max 512 bits (64 bytes) for User Memory +} UserMemoryBank; + +// EPC Gen 2 Tag containing all memory banks +typedef struct { + ReservedMemoryBank* reserved; + EPCMemoryBank* epc; + TIDMemoryBank* tid; + UserMemoryBank* user; +} UHFTag; + +typedef struct UHFTagWrapper { + UHFTag* uhf_tag; +} UHFTagWrapper; + +UHFTagWrapper* uhf_tag_wrapper_alloc(); +void uhf_tag_wrapper_set_tag(UHFTagWrapper* uhf_tag_wrapper, UHFTag* uhf_tag); +void uhf_tag_wrapper_free(UHFTagWrapper* uhf_tag_wrapper); + +UHFTag* uhf_tag_alloc(); +void uhf_tag_reset(UHFTag* uhf_tag); +void uhf_tag_free(UHFTag* uhf_tag); + +void uhf_tag_set_kill_pwd(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_access_pwd(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_epc_pc(UHFTag* uhf_tag, uint16_t pc); +void uhf_tag_set_epc_crc(UHFTag* uhf_tag, uint16_t crc); +void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_epc_size(UHFTag* uhf_tag, size_t size); +void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_tid_size(UHFTag* uhf_tag, size_t size); +void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_user_size(UHFTag* uhf_tag, size_t size); +void uhf_tag_set_reserved(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +uint8_t* uhf_tag_get_kill_pwd(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_access_pwd(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_epc(UHFTag* uhf_tag); +uint16_t uhf_tag_get_epc_pc(UHFTag* uhf_tag); +uint16_t uhf_tag_get_epc_crc(UHFTag* uhf_tag); +size_t uhf_tag_get_epc_size(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_tid(UHFTag* uhf_tag); +size_t uhf_tag_get_tid_size(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_user(UHFTag* uhf_tag); +size_t uhf_tag_get_user_size(UHFTag* uhf_tag); + +// debug +char* uhf_tag_get_cstr(UHFTag* uhf_tag); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_uart.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_uart.c new file mode 100644 index 00000000..7c4c8ea6 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_uart.c @@ -0,0 +1,94 @@ +#include "yrm100x_uart.h" + +/** + * File that handles the uart for the YRM100 + * @author frux-c +*/ +void uhf_uart_default_rx_callback(FuriHalSerialHandle *handle, FuriHalSerialRxEvent event, void* ctx) { + UHFUart* uart = (UHFUart*)ctx; + // FURI_LOG_E("UHF_UART", "UHF UART RX CALLBACK"); + if((event & FuriHalSerialRxEventData) == FuriHalSerialRxEventData){ + uint8_t data = furi_hal_serial_async_rx(handle); + // if(data == UHF_UART_FRAME_START){ + // uhf_buffer_reset(uart->buffer); + // } + if(uhf_is_buffer_closed(uart->buffer)){ + return; + } + if(data == UHF_UART_FRAME_END){ + uhf_buffer_append_single(uart->buffer, data); + uhf_buffer_close(uart->buffer); + FURI_LOG_E("UHF_UART", "UHF Total length read = %u", uhf_buffer_get_size(uart->buffer)); + } + uhf_buffer_append_single(uart->buffer, data); + uhf_uart_tick_reset(uart); + // furi_stream_buffer_send(uart->rx_buff_stream, (void*)&data, 1, 0); + // furi_thread_flags_set(furi_thread_get_id(uart->thread), UHFUartWorkerWaitingDataFlag); + } +} + +UHFUart* uhf_uart_alloc(){ + UHFUart *uart = (UHFUart*)malloc(sizeof(UHFUart)); + uart->bus = FuriHalBusUSART1; + uart->handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart); + // uart->rx_buff_stream = furi_stream_buffer_alloc(UHF_UART_RX_BUFFER_SIZE, 1); + uart->init_by_app = !furi_hal_bus_is_enabled(uart->bus); + uart->tick = UHF_UART_WAIT_TICK; + uart->baudrate = UHF_UART_DEFAULT_BAUDRATE; + // expansion_disable(); + if(uart->init_by_app){ + FURI_LOG_E("UHF_UART", "UHF UART INIT BY APP"); + furi_hal_serial_init(uart->handle, uart->baudrate); + } + else{ + FURI_LOG_E("UHF_UART", "UHF UART INIT BY HAL"); + } + uart->buffer = uhf_buffer_alloc(UHF_UART_RX_BUFFER_SIZE); + furi_hal_serial_async_rx_start(uart->handle, uhf_uart_default_rx_callback, uart, false); + return uart; +} + +void uhf_uart_free(UHFUart* uart){ + furi_assert(uart); + // furi_assert(uart->thread); + // furi_thread_flags_set(furi_thread_get_id(uart->thread), UHFUartWorkerExitingFlag); + // furi_thread_join(uart->thread); + // furi_thread_free(uart->thread); + // furi_stream_buffer_free(uart->rx_buff_stream); + uhf_buffer_free(uart->buffer); + if(uart->init_by_app){ + furi_hal_serial_deinit(uart->handle); + } + furi_hal_serial_control_release(uart->handle); + free(uart); +} + +void uhf_uart_set_receive_byte_callback(UHFUart* uart, FuriHalSerialAsyncRxCallback callback, void *ctx, bool report_errors){ + furi_hal_serial_async_rx_start(uart->handle, callback, ctx, report_errors); +} + +void uhf_uart_send(UHFUart* uart, uint8_t* data, size_t size){ + furi_hal_serial_tx(uart->handle, data, size); +} + +void uhf_uart_send_wait(UHFUart* uart, uint8_t* data, size_t size){ + uhf_uart_send(uart, data, size); + furi_hal_serial_tx_wait_complete(uart->handle); + // furi_thread_flags_set(furi_thread_get_id(uart->thread), UHFUartWorkerWaitingDataFlag); +} + +void uhf_uart_set_baudrate(UHFUart* uart, uint32_t baudrate){ + furi_hal_serial_set_br(uart->handle, baudrate); + uart->baudrate = baudrate; +} + +bool uhf_uart_tick(UHFUart* uart){ + if(uart->tick > 0){ + uart->tick--; + } + return uart->tick == 0; +} + +void uhf_uart_tick_reset(UHFUart* uart){ + uart->tick = UHF_UART_WAIT_TICK; +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_uart.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_uart.h new file mode 100644 index 00000000..424cf993 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_uart.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include +#include +#include "yrm100x_buffer.h" + +/** + * File that handles the uart for the YRM100 + * @author frux-c +*/ + +#define UHF_UART_RX_BUFFER_SIZE 250 +#define UHF_UART_DEFAULT_BAUDRATE 115200 +#define UHF_UART_FRAME_START 0xBB +#define UHF_UART_FRAME_END 0x7E +#define UHF_UART_WAIT_TICK 1000 + +typedef void (*CallbackFunction)(uint8_t *data, void *ctx); + + +typedef enum{ + UHFUartWorkerWaitingDataFlag = 1 << 0, + UHFUartWorkerExitingFlag = 1 << 2, +}UHFUartWorkerEventFlag; + +typedef struct{ + FuriHalBus bus; + FuriHalSerialHandle *handle; + // FuriStreamBuffer *rx_buff_stream; + // FuriThread *thread; + CallbackFunction callback; + Buffer *buffer; + uint32_t baudrate; + bool init_by_app; + void *ctx; + volatile int tick; +} UHFUart; + +int32_t uhf_uart_worker_callback(void *ctx); + +UHFUart* uhf_uart_alloc(); +void uhf_uart_free(UHFUart* uart); +void uhf_uart_send(UHFUart* uart, uint8_t* data, size_t size); +void uhf_uart_send_wait(UHFUart* uart, uint8_t* data, size_t size); +void uhf_uart_set_receive_byte_callback(UHFUart* uart, FuriHalSerialAsyncRxCallback callback, void *ctx, bool report_errors); +void uhf_uart_set_baudrate(UHFUart* uart, uint32_t baudrate); +bool uhf_uart_tick(UHFUart* uart); +void uhf_uart_tick_reset(UHFUart* uart); diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_worker.c b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_worker.c new file mode 100644 index 00000000..928a6291 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_worker.c @@ -0,0 +1,239 @@ +#include "yrm100x_tag.h" +#include "yrm100x_worker.h" + +/** + * File that handles the worker for the YRM100 + * @author frux-c + * @author modified by haffnerriley +*/ + +// yrm100 module commands +UHFWorkerEvent verify_module_connected(UHFWorker* uhf_worker) { + char* hw_version = m100_get_hardware_version(uhf_worker->module); + char* sw_version = m100_get_software_version(uhf_worker->module); + char* manufacturer = m100_get_manufacturers(uhf_worker->module); + // verify all data exists + if(hw_version == NULL || sw_version == NULL || manufacturer == NULL) return UHFWorkerEventFail; + return UHFWorkerEventSuccess; +} + +UHFTag* send_polling_command(UHFWorker* uhf_worker) { + // read epc bank + UHFTag* uhf_tag = uhf_tag_alloc(); + M100ResponseType status; + do { + if(uhf_worker->state == UHFWorkerStateStop) { + uhf_tag_free(uhf_tag); + return NULL; + } + status = m100_single_poll(uhf_worker->module, uhf_tag); + } while(status != M100SuccessResponse); + return uhf_tag; +} + +//Modified by William Riley Haffner to use a default access password that is set through the ap UI +UHFWorkerEvent read_bank_till_max_length(UHFWorker* uhf_worker, UHFTag* uhf_tag, BankType bank) { + unsigned int word_low = 0, word_high = 64; + unsigned int word_size; + M100ResponseType status; + do { + if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted; + if(word_low >= word_high) return UHFWorkerEventSuccess; + word_size = (word_low + word_high) / 2; + + status = m100_read_label_data_storage( + uhf_worker->module, + uhf_tag, + bank, + uhf_worker->DefaultAP, + word_size); + if(status == M100SuccessResponse) { + word_low = word_size + 1; + } else if(status == M100MemoryOverrun) { + word_high = word_size - 1; + } + } while(true); + return UHFWorkerEventSuccess; +} + +//Modified by Riley Haffner to read the reserved bank using the default AP set by the user +UHFWorkerEvent read_single_card(UHFWorker* uhf_worker) { + UHFTag* uhf_tag = send_polling_command(uhf_worker); + if(uhf_tag == NULL) return UHFWorkerEventAborted; + uhf_tag_wrapper_set_tag(uhf_worker->uhf_tag_wrapper, uhf_tag); + // set select + while(m100_set_select(uhf_worker->module, uhf_tag) != M100SuccessResponse) { + } + // read tid + + //create a default password attribute for the worker.... + UHFWorkerEvent event; + event = read_bank_till_max_length(uhf_worker, uhf_tag, TIDBank); + if(event != UHFWorkerEventSuccess) return event; + // read user + event = read_bank_till_max_length(uhf_worker, uhf_tag, UserBank); + if(event != UHFWorkerEventSuccess) return event; + event = read_bank_till_max_length(uhf_worker, uhf_tag, ReservedBank); + if(event != UHFWorkerEventSuccess) return event; + return UHFWorkerEventSuccess; +} + +//Modified by Riley Haffner to be able to write to the reserved bank +UHFWorkerEvent write_single_card(UHFWorker* uhf_worker) { + //uhf_worker->TagToWrite + UHFTag* uhf_tag_des = send_polling_command( + uhf_worker); + if(uhf_tag_des == NULL) return UHFWorkerEventAborted; + + UHFTag* uhf_tag_from = + uhf_worker->NewTag; + M100ResponseType rp_type; + do { + rp_type = m100_set_select(uhf_worker->module, uhf_tag_des); + if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted; + if(rp_type == M100SuccessResponse) break; + } while(true); + while(m100_is_write_mask_enabled(uhf_worker->module, WRITE_USER)) { + rp_type = m100_write_label_data_storage( + uhf_worker->module, uhf_tag_from, uhf_tag_des, UserBank, 0, uhf_worker->DefaultAP); + if(uhf_worker->state == UHFWorkerStateStop) { + m100_disable_write_mask(uhf_worker->module, WRITE_USER); + return UHFWorkerEventAborted; + } + if(rp_type == M100SuccessResponse) { + m100_disable_write_mask(uhf_worker->module, WRITE_USER); + break; + } + } + while(m100_is_write_mask_enabled(uhf_worker->module, WRITE_TID)) { + rp_type = m100_write_label_data_storage( + uhf_worker->module, uhf_tag_from, uhf_tag_des, TIDBank, 0, uhf_worker->DefaultAP); + if(uhf_worker->state == UHFWorkerStateStop) { + m100_disable_write_mask(uhf_worker->module, WRITE_TID); + return UHFWorkerEventAborted; + } + if(rp_type == M100SuccessResponse) { + m100_disable_write_mask(uhf_worker->module, WRITE_TID); + break; + } + } + while(m100_is_write_mask_enabled(uhf_worker->module, WRITE_EPC)) { + rp_type = m100_write_label_data_storage( + uhf_worker->module, uhf_tag_from, uhf_tag_des, EPCBank, 0, uhf_worker->DefaultAP); + if(uhf_worker->state == UHFWorkerStateStop) { + m100_disable_write_mask(uhf_worker->module, WRITE_EPC); + return UHFWorkerEventAborted; + } + if(rp_type == M100SuccessResponse) { + m100_disable_write_mask(uhf_worker->module, WRITE_EPC); + break; + } + } + while(m100_is_write_mask_enabled(uhf_worker->module, WRITE_RFU)) { + + if(uhf_worker->KillPwd && uhf_worker->AccessPwd){ + rp_type = m100_write_label_data_storage( + uhf_worker->module, + uhf_tag_from, + uhf_tag_des, + ReservedBank, + 1, + uhf_worker->DefaultAP); + } + else if(uhf_worker->KillPwd){ + rp_type = m100_write_label_data_storage( + uhf_worker->module, + uhf_tag_from, + uhf_tag_des, + ReservedBank, + 0, + uhf_worker->DefaultAP); + } + else if (uhf_worker->AccessPwd){ + rp_type = m100_write_label_data_storage( + uhf_worker->module, + uhf_tag_from, + uhf_tag_des, + ReservedBank, + 32, + uhf_worker->DefaultAP); + } + + + if(uhf_worker->state == UHFWorkerStateStop) { + m100_disable_write_mask(uhf_worker->module, WRITE_RFU); + return UHFWorkerEventAborted; + } + if(rp_type == M100SuccessResponse) { + m100_disable_write_mask(uhf_worker->module, WRITE_RFU); + break; + } + if(rp_type == M100APWrong){ + m100_disable_write_mask(uhf_worker->module, WRITE_RFU); + return UHFWorkerEventAborted; + } + } + return UHFWorkerEventSuccess; +} + +int32_t uhf_worker_task(void* ctx) { + UHFWorker* uhf_worker = ctx; + if(uhf_worker->state == UHFWorkerStateVerify) { + UHFWorkerEvent event = verify_module_connected(uhf_worker); + uhf_worker->callback(event, uhf_worker->ctx); + } else if(uhf_worker->state == UHFWorkerStateDetectSingle) { + UHFWorkerEvent event = read_single_card(uhf_worker); + uhf_worker->callback(event, uhf_worker->ctx); + } else if(uhf_worker->state == UHFWorkerStateWriteSingle) { + UHFWorkerEvent event = write_single_card(uhf_worker); + uhf_worker->callback(event, uhf_worker->ctx); + } + return 0; +} + +UHFWorker* uhf_worker_alloc() { + UHFWorker* uhf_worker = (UHFWorker*)malloc(sizeof(UHFWorker)); + uhf_worker->thread = + furi_thread_alloc_ex("UHFWorker", UHF_WORKER_STACK_SIZE, uhf_worker_task, uhf_worker); + uhf_worker->module = m100_module_alloc(); + uhf_worker->callback = NULL; + uhf_worker->ctx = NULL; + uhf_worker->NewTag = uhf_tag_alloc(); + uhf_worker->KillPwd = false; + uhf_worker->AccessPwd = false; + uhf_worker->DefaultAP = 0; + return uhf_worker; +} + +void uhf_worker_change_state(UHFWorker* worker, UHFWorkerState state) { + worker->state = state; +} + +void uhf_worker_start( + UHFWorker* uhf_worker, + UHFWorkerState state, + UHFWorkerCallback callback, + void* ctx) { + uhf_worker->state = state; + uhf_worker->callback = callback; + uhf_worker->ctx = ctx; + furi_thread_start(uhf_worker->thread); +} + +void uhf_worker_stop(UHFWorker* uhf_worker) { + furi_assert(uhf_worker); + furi_assert(uhf_worker->thread); + + if(furi_thread_get_state(uhf_worker->thread) != FuriThreadStateStopped) { + uhf_worker_change_state(uhf_worker, UHFWorkerStateStop); + furi_thread_join(uhf_worker->thread); + } +} + +void uhf_worker_free(UHFWorker* uhf_worker) { + furi_assert(uhf_worker); + furi_thread_free(uhf_worker->thread); + m100_module_free(uhf_worker->module); + uhf_tag_free(uhf_worker->NewTag); + free(uhf_worker); +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_worker.h b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_worker.h new file mode 100644 index 00000000..5b4fc55f --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/helpers/yrm100x_worker.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include "yrm100x_module.h" + +/** + * File that handles the worker for the YRM100 + * @author frux-c + * @author modified by haffnerriley +*/ + +#define UHF_WORKER_STACK_SIZE 1 * 1024 + +typedef enum { + // Init states + UHFWorkerStateNone, + UHFWorkerStateBroken, + UHFWorkerStateReady, + UHFWorkerStateVerify, + // Main worker states + UHFWorkerStateDetectSingle, + UHFWorkerStateWriteSingle, + UHFWorkerStateWriteKey, + //UHFWorkerStateKillTag, + // Transition + UHFWorkerStateStop, +} UHFWorkerState; + +typedef enum { + UHFWorkerEventSuccess, + UHFWorkerEventFail, + UHFWorkerEventNoTagDetected, + UHFWorkerEventAborted, + UHFWorkerEventCardDetected, +} UHFWorkerEvent; + +typedef void (*UHFWorkerCallback)(UHFWorkerEvent event, void* ctx); +//Modified by William Riley Haffner +typedef struct UHFWorker { + FuriThread* thread; + M100Module* module; + UHFWorkerCallback callback; + UHFWorkerState state; + UHFTagWrapper* uhf_tag_wrapper; + //Adding tags for writing + bool KillPwd; + bool AccessPwd; + UHFTag* NewTag; + uint32_t DefaultAP; + //uint32_t write_ap; + void* ctx; +} UHFWorker; + +int32_t uhf_worker_task(void* ctx); +UHFWorker* uhf_worker_alloc(); +void uhf_worker_change_state(UHFWorker* worker, UHFWorkerState state); +void uhf_worker_start( + UHFWorker* uhf_worker, + UHFWorkerState state, + UHFWorkerCallback callback, + void* ctx); +void uhf_worker_stop(UHFWorker* uhf_worker); +void uhf_worker_free(UHFWorker* uhf_worker); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/icons/RFIDDolphinReceive_97x61.png b/non_catalog_apps/simultaneous_rfid_reader/icons/RFIDDolphinReceive_97x61.png new file mode 100644 index 00000000..e1f5f9f8 Binary files /dev/null and b/non_catalog_apps/simultaneous_rfid_reader/icons/RFIDDolphinReceive_97x61.png differ diff --git a/non_catalog_apps/simultaneous_rfid_reader/structures.h b/non_catalog_apps/simultaneous_rfid_reader/structures.h new file mode 100644 index 00000000..b97295a6 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/structures.h @@ -0,0 +1,364 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "helpers/uart_helper.h" +#include "helpers/yrm100x_uart.h" +#include "helpers/yrm100x_buffer.h" +#include "helpers/yrm100x_worker.h" +#include +//Submenu enums for different screens +typedef enum { + UHFReaderSubmenuIndexRead, + UHFReaderSubmenuIndexDelete, + UHFReaderSubmenuIndexTagInfo, + UHFReaderSubmenuIndexTagRename, + UHFReaderSubmenuIndexEpcLock, + UHFReaderSubmenuIndexTidLock, + UHFReaderSubmenuIndexUserLock, + UHFReaderSubmenuIndexApLock, + UHFReaderSubmenuIndexKillLock, + UHFReaderSubmenuIndexTagWrite, + UHFReaderSubmenuIndexTagLock, + UHFReaderSubmenuIndexTagKill, + UHFReaderSubmenuIndexSetKillPwd, + UHFReaderSubmenuIndexKillTag, + UHFReaderSubmenuIndexTagDelete, + UHFReaderSubmenuIndexSaved, + UHFReaderSubmenuIndexConfig, + UHFReaderSubmenuIndexAbout, + UHFReaderSubmenuIndexStartReading, + UHFReaderSubmenuIndexStartWriting, + UHFReaderSubmenuIndexSelectTag, + UHFReaderSubmenuIndexSetPower, + UHFReaderSubmenuIndexTagAction, + UHFReaderSubmenuIndexSetAccessPwd, +} UHFReaderSubmenuIndex; + +//Defining views for the application +typedef enum { + UHFReaderViewSubmenu, + UHFReaderViewDeleteSuccess, + UHFReaderViewConfigure, + UHFReaderViewEpcDump, + UHFReaderViewEpcInfo, + UHFReaderViewDelete, + UHFReaderViewAbout, + UHFReaderViewRead, + UHFReaderViewSaveInput, + UHFReaderViewRenameInput, + UHFReaderViewEpcWriteInput, + UHFReaderViewWrite, + UHFReaderViewLock, + UHFReaderViewKill, + UHFReaderViewSaved, + UHFReaderViewTagAction, + UHFReaderViewSelectSavedTag, + UHFReaderViewSetPower, + UHFReaderViewSetReadAp, + UHFReaderViewSetKillPwd, + UHFReaderViewSetAccessPwd, + UHFReaderViewKillConfirm, + UHFReaderViewLockPopup, +} UHFReaderView; + +//Event IDs for the app +typedef enum { + UHFReaderEventIdRedrawScreen = 0, + UHFReaderEventIdOkPressed = 42, + UHFCustomEventReserved = 100, + UHFCustomEventWorkerExit = 105, + UHFCustomEventWorkerExitAborted = 106, +} UHFReaderEventId; + +//State of the reader app when communicating with raspberry pi zero over uart +typedef enum { + UHFReaderStateIdle, + UHFReaderStateWaitForNumber, + UHFReaderStateCollectEPCs, + UHFReaderStateDoneCollecting, + UHFReaderStateWaitForTID, + UHFReaderStateCollectTIDs, + UHFReaderStateDoneCollectingTIDs, + UHFReaderStateWaitForRES, + UHFReaderStateCollectRESs, + UHFReaderStateDoneCollectingRESs, + UHFReaderStateWaitForMEM, + UHFReaderStateCollectMEMs, + UHFReaderStateDoneCollectingMEMs +} UHFReaderState; + +//LED blinking notification sequence +static const NotificationSequence uhf_sequence_blink_start_cyan = { + &message_blink_start_10, + &message_blink_set_color_cyan, + &message_do_not_reset, + NULL, +}; + +//LED blinking notification sequence for stopping +static const NotificationSequence uhf_sequence_blink_stop = { + &message_blink_stop, + NULL, +}; +//The main UHFReaderApp Struct +typedef struct { + ViewDispatcher* ViewDispatcher; + NotificationApp* Notifications; + + Submenu* Submenu; + Submenu* SubmenuSaved; + Submenu* SubmenuTagActions; + Submenu* SubmenuLockActions; + Submenu* SubmenuKillActions; + + TextInput* TextInput; + ByteInput* ApInput; + ByteInput* KillInput; + ByteInput* SetApInput; + ByteInput* KillConfirmInput; + + TextInput* SaveInput; + TextInput* RenameInput; + TextInput* EpcWrite; + + Popup* LockPopup; + + VariableItemList* VariableItemListConfig; + VariableItemList* VariableItemListLock; + VariableItem* Setting2Item; + VariableItem* SettingApPwdItem; + VariableItem* SettingLockApPwdItem; + VariableItem* SettingLockResultItem; + VariableItem* WriteSettingApPwdItem; + Widget* WidgetAbout; + + View* ViewRead; + View* ViewWrite; + View* ViewDelete; + View* ViewLock; + View* ViewDeleteSuccess; + View* ViewEpc; + View* ViewEpcInfo; + + char* TempBuffer; + uint8_t* ApTempBuffer; + uint8_t* KillPwdTempBuffer; + uint8_t* KillConfirmPwdTempBuffer; + uint8_t* SetPwdTempBuffer; + char* TempSaveBuffer; + char* FileName; + char* EpcToSave; + char* Setting1ConfigLabel; + char* Setting1Names[2]; + char* SettingLockBankConfigLabel; + char* SettingLockBankNames[5]; + char* SettingLockActionConfigLabel; + char* SettingLockActionNames[4]; + char* Setting2ConfigLabel; + char* Setting2EntryText; + char* Setting2DefaultValue; + char* Setting3ConfigLabel; + char* Setting3Names[2]; + char* SettingModuleNames[3]; + char* SettingSavingNames[2]; + char* SettingBaudNames[3]; + char* SettingRegionNames[5]; + char* SettingModuleConfigLabel; + char* SettingSavingConfigLabel; + char* SettingBaudConfigLabel; + char* SettingRegionConfigLabel; + char* SettingLockExecuteConfigLabel; + char* SettingLockExecuteResult; + + uint32_t TempBufferSize; + uint8_t ApInputBufferSize; + uint8_t KillPwdInputBufferSize; + uint32_t TempBufferSaveSize; + uint32_t NameSize; + uint32_t SelectedTagIndex; + uint32_t NumberOfSavedTags; + uint32_t NumberOfTidsToRead; + uint32_t NumberOfResToRead; + uint32_t NumberOfMemToRead; + uint32_t CurEpcIndex; + uint32_t CurTidIndex; + uint32_t CurResIndex; + uint32_t CurMemIndex; + uint32_t UHFBaudRate; + + UartHelper* UartHelper; + + FuriString* EpcNameDelete; + FuriString* EpcDelete; + FuriString* EpcName; + FuriString* Setting2PowerStr; + FuriString* DefaultLockAccessPwdStr; + FuriString* DefaultLockResultStr; + FuriString* EpcToWrite; + FuriString* DefaultAccessPwdStr; + + bool IsReading; + bool IsWriting; + bool ReaderConnected; + + FuriTimer* Timer; + + Storage* TagStorage; + FlipperFormat* EpcFile; + FlipperFormat* EpcIndexFile; + + UHFReaderState State; + + size_t NumberOfEpcsToRead; + size_t NameSizeParse; + + uint8_t Setting3Index; + uint8_t SettingModuleIndex; + uint8_t SettingSavingIndex; + uint8_t UHFSaveType; + uint8_t SettingBaudIndex; + uint8_t SettingRegionIndex; + uint8_t SettingLockBankIndex; + uint8_t SettingLockActionIndex; + uint8_t Setting1Index; + uint8_t Setting1Values[2]; + uint8_t SettingLockBankValues[5]; + uint8_t SettingLockActionValues[4]; + uint8_t Setting3Values[2]; + uint8_t SettingModuleValues[3]; + uint8_t SettingSavingValues[2]; + uint8_t SettingBaudValues[3]; + uint8_t SettingRegionValues[5]; + uint8_t UHFModuleType; + uint8_t UHFRegionType; + + + char** EpcValues; + char** TidValues; + char** ResValues; + char** MemValues; + + UHFWorker* YRM100XWorker; + + char* ReadAccessPasswordLabel; + char* AccessPasswordPlaceHolder; + char* DefaultAccessPassword; + + char* SettingApLabel; + char* SettingApDefaultPassword; + + char* KillPasswordPlaceHolder; + char* DefaultKillPassword; + + char* SetAccessPasswordPlaceHolder; + char* KillConfirmPasswordPlaceHolder; + + BankType DefaultLockBank; + LockType DefaultLockType; + + //Buffers for YRM100 functionality + size_t EpcBytesLen; + size_t ResBytesLen; + size_t TidBytesLen; + size_t UserBytesLen; + size_t PcBytesLen; + size_t CrcBytesLen; + uint8_t* EpcBytes; + uint8_t* ResBytes; + uint8_t* TidBytes; + uint8_t* UserBytes; + uint16_t* PcBytes; + uint16_t* CrcBytes; +} UHFReaderApp; + +//The model for the configure/read screen +typedef struct { + uint32_t Setting1Index; + FuriString* Setting2Power; + FuriString* SettingReadAp; + + uint32_t Setting3Index; + bool IsReading; + FuriString* EpcName; + uint32_t CurEpcIndex; + FuriString* EpcValue; + uint32_t NumEpcsRead; + FuriString* Setting1Value; + FuriString* Setting3Value; + FuriString* Pc; + FuriString* Crc; + uint32_t ScrollOffset; + char* ScrollingText; +} UHFReaderConfigModel; + +//Model for the write screen +typedef struct { + uint32_t Setting1Index; + FuriString* Setting2Power; + uint32_t Setting3Index; + bool IsWriting; + FuriString* EpcName; + FuriString* WriteFunction; + FuriString* EpcValue; + FuriString* WriteStatus; + FuriString* NewEpcValue; + FuriString* TidValue; + FuriString* NewTidValue; + FuriString* ResValue; + FuriString* NewResValue; + FuriString* MemValue; + FuriString* NewMemValue; + FuriString* Setting1Value; + FuriString* Setting3Value; + FuriString* Crc; + FuriString* Pc; + FuriString* SettingKillPwd; +} UHFReaderWriteModel; + +//Model for the delete screen +typedef struct { + uint32_t SelectedTagIndex; + FuriString* SelectedTagName; + FuriString* SelectedTagEpc; + uint32_t ScrollOffset; + char* ScrollingText; +} UHFReaderDeleteModel; + +//Model use for handling UHF RFID tag data +typedef struct { + FuriString* Reserved; + FuriString* Epc; + FuriString* Tid; + FuriString* User; + FuriString* Crc; + FuriString* Pc; + uint32_t CurEpcIndex; + uint32_t ScrollOffsetEpc; + char* ScrollingTextEpc; + uint32_t ScrollOffsetTid; + char* ScrollingTextTid; + uint32_t ScrollOffsetRes; + char* ScrollingTextRes; + uint32_t ScrollOffsetMem; + char* ScrollingTextMem; +} UHFRFIDTagModel; diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_about.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_about.c new file mode 100644 index 00000000..110d95e5 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_about.c @@ -0,0 +1,131 @@ +#include "view_about.h" + +/** + * @brief Callback for returning to submenu. + * @details This function is called when user press back button. + * @param context The context - unused + * @return next view id +*/ +uint32_t uhf_reader_navigation_about_submenu_callback(void* context) { + UNUSED(context); + return UHFReaderViewSubmenu; +} + +/** + * @brief Allocates the about screen + * @details Allocates the contents of the about screen. + * @param app The UHFReaderApp used to allocate variables +*/ +void view_about_alloc(UHFReaderApp* App) { + + //Creating the about widget + App->WidgetAbout = widget_alloc(); + FuriString* TmpString = furi_string_alloc(); + widget_add_text_box_element( + App->WidgetAbout, 0, 0, 128, 14, AlignCenter, AlignBottom, UHF_RFID_BLANK_INV, false); + widget_add_text_box_element( + App->WidgetAbout, 0, 0, 128, 14, AlignCenter, AlignBottom, UHF_RFID_NAME, false); + + //Adding version and developer information + furi_string_printf(TmpString, "\e#%s\n", "Information:"); + furi_string_cat_printf(TmpString, "Version: %s\n", UHF_RFID_VERSION_APP); + furi_string_cat_printf(TmpString, "Developed by: %s\n", UHF_RFID_MEM_DEVELOPER); + furi_string_cat_printf(TmpString, "Github: %s\n\n", UHF_RFID_GITHUB); + furi_string_cat_printf(TmpString, "\e#%s\n", "Description:"); + + //Section with high level overview of app functions + furi_string_cat_printf( + TmpString, + "UHF RFID Reader\n" + "Made for use with a M6E, M7E, or YRM100 compatible reader.\n" + "Can read up to 150 tags simultaneously using M6E or M7E reader!\n" + "Can read/write, save, lock, kill, and dump data from read tags.\n\n"); + + //Hardware requirements + furi_string_cat_printf(TmpString, "\e#%s\n", "Hardware Requirements:"); + furi_string_cat_printf( + TmpString, + "Any of these options work, however, changes to the wiring and configuration may be necessary.\n" + "A M6E, M7E, or YRM100 UHF RFID Reader is required!\n" + "If using a M6E or M7E reader, the following are required:\n" + "- ThingMagic Nano Embedded RFID Reader Module (M6E or M7E variant)\n" + "- SparkFun Simultaneous RFID Reader(M6E or M7E variant)\n" + "- Raspberry Pi Zero (To connect to M6E/M7E board)\n" + "- Custom UHF RFID Flipper Zero Board (Coming Soon)\n\n"); + + //The configuration screen + furi_string_cat_printf(TmpString, "\e#%s\n", "Configuration:"); + furi_string_cat_printf( + TmpString, + "To operate, select the reader module first, then connect.\n" + "Next, you may toggle other reader settings.\n" + " The configuration menu has the following options:\n" + "- Select reader module\n" + "- Set the power of the reader(M6E/M7E power range 0-2700, YRM100X 1-27)\n" + "- Change the Baud Rate\n" + "- Change the Region\n" + "- Set default access password for reading/writing\n" + "- Set write save mode (Turn on to save and update stored fields of tag written)\n" + "- Save configuration settings (Future)\n" + "- Set/Detect UHF RFID Tag Type (Future)\n" + "- Toggle the antenna selection (Future for M6E and M7E Only)\n\n"); + + + //Read screen information + furi_string_cat_printf(TmpString, "\e#%s\n", "Read:"); + furi_string_cat_printf( + TmpString, + " The read menu has the following options:\n" + "- Press Ok to start/stop reading\n" + "- Press Up to save the selected EPC\n" + "- Press Down to see TID, EPC, User, and Reserved Memory\n" + "- Press Left/Right to cycle through tags read (M6E & M7E Only)\n\n"); + + //Write screen information + furi_string_cat_printf(TmpString, "\e#%s\n", "Write:"); + furi_string_cat_printf( + TmpString, + " The write menu has the following options:\n" + "- Press Ok to start/stop writing\n" + "- Press Left to modify the EPC value\n" + "- Press Right to modify Reserved Memory (First 4 bytes = kill password, last 4 bytes = access password)\n" + "- Press Up to modify the User Memory Bank\n" + "- Press Down to modify the TID (Supported but usually locked by manufacturer)\n\n"); + + furi_string_cat_printf(TmpString, "\e#%s\n", "Lock:"); + furi_string_cat_printf( + TmpString, + " The Lock menu has the following options:\n" + "- Set access password (Can alternatively be set through the write menu)\n" + "- Pick the Memory Bank or Password to Lock/Unlock, or Permanently Lock/Unlock (In the Open or Secured State)\n" + "- Select the Lock Mode\n" + "- Press Execute to perform the lock action\n" + "- To perform a lock action, first ensure the AP has been set in the config menu, and then select a lock action\n" + "- If the password is wrong, an error sequence will beep\n\n"); + furi_string_cat_printf(TmpString, "\e#%s\n", "Kill:"); + furi_string_cat_printf( + TmpString, + " The Kill menu has the following options:\n" + "- Set kill password (Can alternatively be set through the write menu)\n" + "- Press Kill Tag and confirm the kill password to permanently inactivate the tag!\n" + "- If the password is wrong, an error sequence will beep\n\n"); + + //Adding the widget to the view dispatcher + widget_add_text_scroll_element( + App->WidgetAbout, 0, 16, 128, 50, furi_string_get_cstr(TmpString)); + furi_string_free(TmpString); + view_set_previous_callback( + widget_get_view(App->WidgetAbout), uhf_reader_navigation_about_submenu_callback); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewAbout, widget_get_view(App->WidgetAbout)); +} + +/** + * @brief Frees the about view + * @details Frees all variables associated with the about widget. + * @param app The UHFReaderApp - used to free the widget. +*/ +void view_about_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewAbout); + widget_free(App->WidgetAbout); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_about.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_about.h new file mode 100644 index 00000000..7bab3b9c --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_about.h @@ -0,0 +1,9 @@ +#pragma once +#include "../app.h" + +//Function declarations +uint32_t uhf_reader_navigation_about_submenu_callback(void* context); + +void view_about_alloc(UHFReaderApp* App); + +void view_about_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_config.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_config.c new file mode 100644 index 00000000..f245a0bd --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_config.c @@ -0,0 +1,675 @@ +#include "view_config.h" + +/** + * @brief Callback for returning to submenu. + * @details This function is called when user press back button. + * @param context The context - unused + * @return next view id +*/ +uint32_t uhf_reader_navigation_config_submenu_callback(void* context) { + UNUSED(context); + return UHFReaderViewSubmenu; +} + +/** + * @brief Allocates the configuration menu + * @details This function allocates all views and variables related to the configuration menu. + * @param app The UHFReaderApp - used to allocate app variables and views. +*/ +void view_config_alloc(UHFReaderApp* App) { + //Allocate the power input menu + power_menu_alloc(App); + ap_menu_alloc(App); + + //Creating the variable item list + App->VariableItemListConfig = variable_item_list_alloc(); + variable_item_list_reset(App->VariableItemListConfig); + + //Initializing configuration setting variables + App->Setting1Values[0] = 1; + App->Setting1Values[1] = 2; + App->Setting1Names[0] = "Connect"; + App->Setting1Names[1] = "Disconnect"; + App->ReaderConnected = false; + App->Setting1ConfigLabel = "Connection"; + App->Setting2ConfigLabel = "Power Level"; + App->Setting2EntryText = "Enter Value In Range 0-2700"; + App->Setting2DefaultValue = "1500"; + App->Setting3Values[0] = 1; + App->Setting3Values[1] = 2; + App->Setting3Names[0] = "Internal"; + App->Setting3Names[1] = "External"; + App->Setting3ConfigLabel = "Antenna"; + + //Baud rate values (just for YRM100 Module for now!) + App->SettingBaudValues[0] = 1; + App->SettingBaudValues[1] = 2; + App->SettingBaudValues[2] = 3; + App->SettingBaudNames[0] = "9600"; + App->SettingBaudNames[2] = "384000"; + App->SettingBaudNames[1] = "115200"; + App->SettingBaudConfigLabel = "Baud Rate"; + App->UHFBaudRate = 115200; + + //Setting the module values for the config menu + App->SettingModuleValues[0] = 1; + App->SettingModuleValues[1] = 2; + App->SettingModuleValues[2] = 3; + App->SettingModuleNames[0] = "M6e"; + App->SettingModuleNames[1] = "YRM100"; + App->SettingModuleNames[2] = "M7e"; + App->SettingModuleConfigLabel = "UHF Module"; + App->UHFModuleType = M6E_NANO_MODULE; + + App->SettingSavingValues[0] = 1; + App->SettingSavingValues[1] = 2; + App->SettingSavingNames[0] = "No"; + App->SettingSavingNames[1] = "Yes"; + App->SettingSavingConfigLabel = "Save on Write"; + App->UHFSaveType = NO_SAVE_ON_WRITE; + + //Setting the available regions + App->SettingRegionValues[0] = 1; + App->SettingRegionValues[1] = 2; + App->SettingRegionValues[2] = 3; + App->SettingRegionValues[3] = 4; + App->SettingRegionValues[4] = 5; + App->SettingRegionNames[0] = "USA"; + App->SettingRegionNames[1] = "EU"; + App->SettingRegionNames[2] = "Korea"; + App->SettingRegionNames[3] = "China 800"; + App->SettingRegionNames[4] = "China 900"; + App->SettingRegionConfigLabel = "Region"; + App->UHFRegionType = USA_REGION; + + //Setting the config menu labels for the default read access password + App->ReadAccessPasswordLabel = strdup("Default AP"); + App->AccessPasswordPlaceHolder = strdup("Enter Access Password!"); + App->DefaultAccessPassword = strdup("00000000"); + + // Add setting 1 to variable item list + VariableItem* Item = variable_item_list_add( + App->VariableItemListConfig, + App->Setting1ConfigLabel, + COUNT_OF(App->Setting1Values), + uhf_reader_setting_1_change, + App); + + //Creating the default index for setting one which is the connection status + App->Setting1Index = 0; + variable_item_set_current_value_index(Item, App->Setting1Index); + variable_item_set_current_value_text(Item, App->Setting1Names[App->Setting1Index]); + + //Moving the module selection up + VariableItem* ModuleSelection = variable_item_list_add( + App->VariableItemListConfig, + App->SettingModuleConfigLabel, + COUNT_OF(App->SettingModuleValues), + uhf_reader_module_setting_change, + App); + + //Default index for the module selection option + App->SettingModuleIndex = 0; + variable_item_set_current_value_index(ModuleSelection, App->SettingModuleIndex); + variable_item_set_current_value_text( + ModuleSelection, App->SettingModuleNames[App->SettingModuleIndex]); + + //Creating the default power value + App->Setting2PowerStr = furi_string_alloc_set(App->Setting2DefaultValue); + App->Setting2Item = variable_item_list_add( + App->VariableItemListConfig, App->Setting2ConfigLabel, 1, NULL, NULL); + variable_item_set_current_value_text( + App->Setting2Item, furi_string_get_cstr(App->Setting2PowerStr)); + variable_item_list_set_enter_callback( + App->VariableItemListConfig, uhf_reader_setting_item_clicked, App); + + VariableItem* BaudSelection = variable_item_list_add( + App->VariableItemListConfig, + App->SettingBaudConfigLabel, + COUNT_OF(App->SettingBaudValues), + uhf_reader_baud_setting_change, + App); + + //Default index for the baud selection option + App->SettingBaudIndex = 1; + variable_item_set_current_value_index(BaudSelection, App->SettingBaudIndex); + variable_item_set_current_value_text( + BaudSelection, App->SettingBaudNames[App->SettingBaudIndex]); + + VariableItem* RegionSelection = variable_item_list_add( + App->VariableItemListConfig, + App->SettingRegionConfigLabel, + COUNT_OF(App->SettingRegionValues), + uhf_reader_region_setting_change, + App); + + //Default index for the baud selection option + App->SettingRegionIndex = 0; + variable_item_set_current_value_index(RegionSelection, App->SettingRegionIndex); + variable_item_set_current_value_text( + RegionSelection, App->SettingRegionNames[App->SettingRegionIndex]); + + //Default access password input for reading and writing to the tag, or locking + App->DefaultAccessPwdStr = furi_string_alloc_set(App->DefaultAccessPassword); + App->SettingApPwdItem = variable_item_list_add( + App->VariableItemListConfig, App->ReadAccessPasswordLabel, 1, NULL, NULL); + variable_item_set_current_value_text( + App->SettingApPwdItem, furi_string_get_cstr(App->DefaultAccessPwdStr)); + variable_item_list_set_enter_callback( + App->VariableItemListConfig, uhf_reader_setting_item_clicked, App); + + //Moving the module selection up + VariableItem* SavingSelection = variable_item_list_add( + App->VariableItemListConfig, + App->SettingSavingConfigLabel, + COUNT_OF(App->SettingSavingValues), + uhf_reader_save_setting_change, + App); + + //Default index for the module selection option + App->SettingSavingIndex = 0; + variable_item_set_current_value_index(SavingSelection, App->SettingSavingIndex); + variable_item_set_current_value_text( + SavingSelection, App->SettingSavingNames[App->SettingSavingIndex]); + + // Add setting 3 to variable item list + VariableItem* AntennaSelection = variable_item_list_add( + App->VariableItemListConfig, + App->Setting3ConfigLabel, + COUNT_OF(App->Setting3Values), + uhf_reader_setting_3_change, + App); + + //Default index for the antenna selection option + App->Setting3Index = 0; + variable_item_set_current_value_index(AntennaSelection, App->Setting3Index); + variable_item_set_current_value_text(AntennaSelection, App->Setting3Names[App->Setting3Index]); + + //Setting previous callback + view_set_previous_callback( + variable_item_list_get_view(App->VariableItemListConfig), + uhf_reader_navigation_config_submenu_callback); + view_dispatcher_add_view( + App->ViewDispatcher, + UHFReaderViewConfigure, + variable_item_list_get_view(App->VariableItemListConfig)); +} + +/** + * @brief Callback for returning to configure screen. + * @details This function is called when user press back button. + * @param context The context - unused + * @return next view id +*/ +uint32_t uhf_reader_navigation_configure_callback(void* context) { + UNUSED(context); + return UHFReaderViewConfigure; +} + +/** + * @brief Handles the connection setting + * @details Attempts to connect/disconnect from the reader. + * @param item VariableItem - the current selection for connect values. +*/ +void uhf_reader_setting_1_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + + //Getting the index + uint8_t Index = variable_item_get_current_value_index(Item); + + //Will eventually do some sort of check to confirm successful connection + if(App->ReaderConnected == false) { + if(App->UHFModuleType != YRM100X_MODULE){ + uart_helper_send(App->UartHelper, "C\n", 2); + } + + App->ReaderConnected = true; + //TODO add ACK check to make sure that the connection was successful + } else { + if(App->UHFModuleType != YRM100X_MODULE){ + uart_helper_send(App->UartHelper, "D\n", 2); + } + App->ReaderConnected = false; + } + + //Setting the current setting value for both the read and write screens + variable_item_set_current_value_text(Item, App->Setting1Names[Index]); + UHFReaderConfigModel* ModelRead = view_get_model(App->ViewRead); + ModelRead->Setting1Index = Index; + furi_string_set(ModelRead->Setting1Value, App->Setting1Names[Index]); + UHFReaderWriteModel* ModelWrite = view_get_model(App->ViewWrite); + ModelWrite->Setting1Index = Index; + furi_string_set(ModelWrite->Setting1Value, App->Setting1Names[Index]); +} + +/** + * @brief Handles the power menu. + * @details This function handles the power value that is set from the configuration screen and sends it to the RPi Zero via UART. + * @param context The UHFReaderApp app - used to allocate app variables and views. +*/ +void uhf_reader_setting_2_text_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool Redraw = true; + + //Changing the read screen's power value to the one set in the configuration menu + if(App->UHFModuleType != YRM100X_MODULE) { + uint32_t power_value = (uint32_t)atoi(App->TempBuffer); + + // Validate power value is between 1 and 2700 inclusive + if(power_value >= 1 && power_value <= 2700) { + with_view_model( + App->ViewRead, + UHFReaderConfigModel * Model, + { + //Send the power command to the RPi Zero + uart_helper_send(App->UartHelper, "POWER\n", 6); + + //Set the current power determined by user + furi_string_set(Model->Setting2Power, App->TempBuffer); + + //Send the power value to the RPi Zero + uart_helper_send_string(App->UartHelper, Model->Setting2Power); + + //Update the power value in the configuration screen + variable_item_set_current_value_text( + App->Setting2Item, furi_string_get_cstr(Model->Setting2Power)); + }, + Redraw); + + } + } else { + //Set the power of the YRM100X Here!!!! + uint16_t power_value = (uint16_t)atoi(App->TempBuffer); + + // Validate power value is between 1 and 27 inclusive + if(power_value >= 1 && power_value <= 27) { + with_view_model( + App->ViewRead, + UHFReaderConfigModel * Model, + { + //Set the current power determined by user + furi_string_set(Model->Setting2Power, App->TempBuffer); + + //Send the power value to the YRM100X + if(m100_set_transmitting_power(App->YRM100XWorker->module, power_value)) { + //Update the power value in the configuration screen + variable_item_set_current_value_text( + App->Setting2Item, furi_string_get_cstr(Model->Setting2Power)); + } + }, + Redraw); + + } + } + + //Switch back to the configuration view + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewConfigure); +} + +/** + * @brief Handles setting the default access password + * @details This function handles setting the default access password for reading, writing, and locking actions + * @param context The UHFReaderApp app - used to allocate app variables and views. +*/ +void uhf_reader_setting_6_text_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool Redraw = true; + + // Temporary buffer to hold the converted string + char* tempBuffer = (char*)malloc(24); + snprintf(tempBuffer, 24, "%s", convert_to_hex_string(App->ApTempBuffer, 4)); + + + if(App->UHFModuleType != YRM100X_MODULE) { + + + //TODO: ADD SUPPORT FOR M6E and M7E + + with_view_model( + App->ViewRead, + UHFReaderConfigModel * Model, + { + //Send the set AP command to the RPi Zero + uart_helper_send(App->UartHelper, "SETPWD\n", 7); + + //Set the current AP determined by user + furi_string_set(Model->SettingReadAp, tempBuffer); + + //Send the AP value to the RPi Zero + uart_helper_send_string(App->UartHelper, Model->SettingReadAp); + + //Update the AP value in the configuration screen + variable_item_set_current_value_text( + App->SettingApPwdItem, furi_string_get_cstr(Model->SettingReadAp)); + }, + Redraw); + + } else { + + with_view_model( + App->ViewRead, + UHFReaderConfigModel * Model, + { + //Set the current AP determined by user + furi_string_set(Model->SettingReadAp, tempBuffer); + + //Send the AP value to the YRM100X + variable_item_set_current_value_text( + App->SettingApPwdItem, furi_string_get_cstr(Model->SettingReadAp)); + }, + Redraw); + + if(App->ReaderConnected){ + App->YRM100XWorker->DefaultAP = bytes_to_uint32(App->ApTempBuffer, 4); + } + + } + free(tempBuffer); + //Switch back to the configuration view + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewConfigure); +} + + +/** + * @brief Handles the Antenna Selection + * @details This function is a place holder for future functionality. + * @param item VariableItem - the current selection for antenna values. +*/ +void uhf_reader_setting_3_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + uint8_t Index = variable_item_get_current_value_index(Item); + + if(Index == 1) { + uart_helper_send(App->UartHelper, "External\n", 9); + //TODO: ADD SUPPORT FOR DIFFERENT ANTENNA TYPES AFTER HARDWARE DEVELOPED! + } else { + uart_helper_send(App->UartHelper, "Internal\n", 9); + } + + //TODO: WAIT FOR ACK AND THEN SET TEXT VALUE + variable_item_set_current_value_text(Item, App->Setting3Names[Index]); + + //Updating the antenna value for the read screen + UHFReaderConfigModel* ModelRead = view_get_model(App->ViewRead); + ModelRead->Setting3Index = Index; + furi_string_set(ModelRead->Setting3Value, App->Setting3Names[Index]); + + //Updating the value of the antenna mode for the write screen + UHFReaderWriteModel* ModelWrite = view_get_model(App->ViewWrite); + ModelWrite->Setting3Index = Index; + furi_string_set(ModelWrite->Setting3Value, App->Setting3Names[Index]); +} + +/** + * @brief Handles the UHF Reader Module Selection + * @details This function handles switching between different supported UHF Readers like the YRM100X, M6e, and M7e Nano RFID modules. + * @param item VariableItem - the current selection for the UHF Module. +*/ +void uhf_reader_module_setting_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + uint8_t Index = variable_item_get_current_value_index(Item); + + variable_item_set_current_value_text(Item, App->SettingModuleNames[Index]); + + if(Index == 1) { + //Mark the YRM100X as being used + App->UHFModuleType = YRM100X_MODULE; + + //Free regular uart worker + uart_helper_free(App->UartHelper); + + //Create a UART worker for the YRM100X module + App->YRM100XWorker = uhf_worker_alloc(); + UHFTagWrapper* WorkerTagWrapper = uhf_tag_wrapper_alloc(); + App->YRM100XWorker->uhf_tag_wrapper = WorkerTagWrapper; + m100_disable_write_mask(App->YRM100XWorker->module, WRITE_EPC); + + } else if(Index == 2) { + //Freeing the YRM100X uart helper + if(App->UHFModuleType == YRM100X_MODULE) { + //Free Tag Wrapper + uhf_tag_wrapper_free(App->YRM100XWorker->uhf_tag_wrapper); + + //Freeing yrm100x worker + uhf_worker_stop(App->YRM100XWorker); + uhf_worker_free(App->YRM100XWorker); + } + //Mark the M7E as being used + App->UartHelper = uart_helper_alloc(); + uart_helper_set_delimiter(App->UartHelper, LINE_DELIMITER, INCLUDE_LINE_DELIMITER); + uart_helper_set_callback(App->UartHelper, uart_demo_process_line, App); + App->UHFModuleType = M7E_HECTO_MODULE; + } else { + //Freeing the YRM100X uart helper + if(App->UHFModuleType == YRM100X_MODULE) { + //Free Tag Wrapper + uhf_tag_wrapper_free(App->YRM100XWorker->uhf_tag_wrapper); + + //Freeing yrm100x worker + uhf_worker_stop(App->YRM100XWorker); + uhf_worker_free(App->YRM100XWorker); + } + + //Mark the M6E as being used + App->UartHelper = uart_helper_alloc(); + uart_helper_set_delimiter(App->UartHelper, LINE_DELIMITER, INCLUDE_LINE_DELIMITER); + uart_helper_set_callback(App->UartHelper, uart_demo_process_line, App); + App->UHFModuleType = M6E_NANO_MODULE; + } +} +/** + * @brief Handles the App save settings + * @details This function toggles the saving mode after a write operation. + * @param item VariableItem - the current selection for setting. +*/ +void uhf_reader_save_setting_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + uint8_t Index = variable_item_get_current_value_index(Item); + + variable_item_set_current_value_text(Item, App->SettingSavingNames[Index]); + + if(Index == 1) { + App->UHFSaveType = YES_SAVE_ON_WRITE; + } + else { + App->UHFSaveType = NO_SAVE_ON_WRITE; + } +} + +/** + * @brief Handles the UHF Reader Baud Rate Selection + * @details This function handles switching between different supported baud rates for the UHF Readers like the YRM100X, M6e, and M7e Nano RFID modules. + * @param item VariableItem - the current selection for the UHF baud rate. +*/ +void uhf_reader_baud_setting_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + uint8_t Index = variable_item_get_current_value_index(Item); + + variable_item_set_current_value_text(Item, App->SettingBaudNames[Index]); + + if(Index == 1) { + //Use 115200 as baud rate + App->UHFBaudRate = 115200; + } else if(Index == 2) { + //Use 19200 as baud rate + App->UHFBaudRate = 384000; + } else { + //Use 9600 as baud rate + App->UHFBaudRate = 9600; + } + + //Setting the baudrate for each module + if(App->UHFModuleType == YRM100X_MODULE && App->ReaderConnected) { + m100_set_baudrate(App->YRM100XWorker->module, App->UHFBaudRate); + } else { + uart_helper_set_baud_rate(App->UartHelper, App->UHFBaudRate); + } +} + +/** + * @brief Handles the UHF Reader Region Selection + * @details This function handles switching between different supported regions for the selected UHF RFID Reader. + * @param item VariableItem - the current selection for the Region. +*/ +void uhf_reader_region_setting_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + uint8_t Index = variable_item_get_current_value_index(Item); + + if(App->ReaderConnected) { + if(Index == 1) { + //Mark EU as being used + App->UHFRegionType = EU_REGION; + } else if(Index == 2) { + //Mark Korea as being used + App->UHFRegionType = KOREA_REGION; + } else if(Index == 3) { + //Mark Korea as being used + App->UHFRegionType = CHINA_800_REGION; + } else if(Index == 4) { + //Mark Korea as being used + App->UHFRegionType = CHINA_900_REGION; + } else { + //Mark the M6E as being used + App->UHFRegionType = USA_REGION; + } + + if(App->UHFModuleType == YRM100X_MODULE) { + WorkingRegion region = WORKING_REGIONS[Index]; + if(m100_set_working_region(App->YRM100XWorker->module, region)) { + variable_item_set_current_value_text(Item, App->SettingRegionNames[Index]); + } + + } else { + //PLACE COMMAND HERE TO CHANGE THE REGION FOR M6E and M7E + uart_helper_send(App->UartHelper, "Region\n", 7); + variable_item_set_current_value_text(Item, App->SettingRegionNames[Index]); + } + } +} +/** + * @brief Allocates the power text screen + * @details Allocates the text input object for the power screen. + * @param app The UHFReaderApp - used for allocating variables and text input. +*/ +void power_menu_alloc(UHFReaderApp* App) { + //TODO: Add checks for reader connection and change power levels based on module used. + App->TextInput = text_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSetPower, text_input_get_view(App->TextInput)); + App->TempBufferSize = 5; + App->TempBuffer = (char*)malloc(App->TempBufferSize); + +} + +/** + * @brief Allocates the AP text screen + * @details Allocates the text input object for the AP screen. + * @param app The UHFReaderApp - used for allocating variables and text input. +*/ +void ap_menu_alloc(UHFReaderApp* App) { + App->ApInput = byte_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSetReadAp, byte_input_get_view(App->ApInput)); + App->ApInputBufferSize = 4; + App->ApTempBuffer = (uint8_t*)malloc(App->ApInputBufferSize); +} + + +/** + * @brief Handles the setting items clicked + * @details Handles the power value input by the user. + * @param context, index - context used for UHFReaderApp, index used for state check. +*/ +void uhf_reader_setting_item_clicked(void* context, uint32_t index) { + UHFReaderApp* App = (UHFReaderApp*)context; + index++; + + //Check if the power menu is being selected + if(index == 3) { + // Header to display on the power value input screen. + text_input_set_header_text(App->TextInput, App->Setting2EntryText); + + //Modify the value of the power for the read and write models + bool Redraw = false; + with_view_model( + App->ViewRead, + UHFReaderConfigModel * Model, + { + strncpy( + App->TempBuffer, + furi_string_get_cstr(Model->Setting2Power), + App->TempBufferSize); + }, + Redraw); + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + strncpy( + App->TempBuffer, + furi_string_get_cstr(Model->Setting2Power), + App->TempBufferSize); + }, + Redraw); + + //Setting the power text input callback function + bool ClearPreviousText = false; + text_input_set_result_callback( + App->TextInput, + uhf_reader_setting_2_text_updated, + App, + App->TempBuffer, + App->TempBufferSize, + ClearPreviousText); + view_set_previous_callback( + text_input_get_view(App->TextInput), uhf_reader_navigation_configure_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSetPower); + } + else if(index == 6) { + // Header to display on the AP value input screen. + byte_input_set_header_text(App->ApInput, App->AccessPasswordPlaceHolder); + + //Modify the value of the AP for the read and write models + bool Redraw = false; + with_view_model( + App->ViewRead, + UHFReaderConfigModel * Model, + { + strncpy( + convert_to_hex_string(App->ApTempBuffer, 4), + furi_string_get_cstr(Model->SettingReadAp), + App->ApInputBufferSize); + }, + Redraw); + + + //Setting the AP text input callback function + byte_input_set_result_callback( + App->ApInput, + uhf_reader_setting_6_text_updated, + NULL, + App, + App->ApTempBuffer, + App->ApInputBufferSize); + view_set_previous_callback( + byte_input_get_view(App->ApInput), uhf_reader_navigation_configure_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSetReadAp); + } + +} + +/** + * @brief Frees the configure screen. + * @details Frees all variables and views for the configure screen. + * @param app The UHFReaderApp - used for freeing variables and text input. +*/ +void view_config_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSetPower); + text_input_free(App->TextInput); + free(App->TempBuffer); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSetReadAp); + byte_input_free(App->ApInput); + free(App->ApTempBuffer); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewConfigure); + variable_item_list_free(App->VariableItemListConfig); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_config.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_config.h new file mode 100644 index 00000000..25c52d66 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_config.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../app.h" + +//Function Declarations +uint32_t uhf_reader_navigation_configure_callback(void* context); + +void uhf_reader_setting_1_change(VariableItem* Item); + +void uhf_reader_setting_2_text_updated(void* context); + +uint32_t uhf_reader_navigation_config_submenu_callback(void* context); + +void uhf_reader_setting_3_change(VariableItem* Item); + +void uhf_reader_module_setting_change(VariableItem* Item); + +void uhf_reader_baud_setting_change(VariableItem* Item); + +void uhf_reader_region_setting_change(VariableItem* Item); + +void uhf_reader_setting_item_clicked(void* context, uint32_t index); + +void view_config_alloc(UHFReaderApp* App); + +void view_config_free(UHFReaderApp* App); + +void power_menu_alloc(UHFReaderApp* App); + +void ap_menu_alloc(UHFReaderApp* App); + +void uhf_reader_save_setting_change(VariableItem* Item); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_delete.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete.c new file mode 100644 index 00000000..927c73ad --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete.c @@ -0,0 +1,225 @@ +#include "view_delete.h" + +/** + * @brief Delete Draw Callback. + * @details This function is called when the user selects delete for a saved UHF tag. + * @param canvas The canvas - Canvas object for drawing the screen. + * @param model The view model - model for the view with variables required for drawing. +*/ +void uhf_reader_view_delete_draw_callback(Canvas* canvas, void* model) { + UHFReaderDeleteModel* MyModel = (UHFReaderDeleteModel*)model; + FuriString* XStr = furi_string_alloc(); + + //Clearing the canvas, setting the color, font and content displayed. + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, " Confirm EPC Deletion!"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 4, 22, "Name:"); + + //Displaying the name of the saved UHF Tag to delete. + canvas_draw_str(canvas, 32, 22, furi_string_get_cstr(MyModel->SelectedTagName)); + + //Displaying the index of the saved tag as shown in the Index file. + furi_string_printf(XStr, "%ld", MyModel->SelectedTagIndex); + canvas_draw_str(canvas, 4, 33, "EPC Index:"); + canvas_draw_str(canvas, 53, 33, furi_string_get_cstr(XStr)); + + //Displaying the EPC scrolling across the screen + canvas_draw_str(canvas, 4, 44, "EPC:"); + MyModel->ScrollingText = (char*)furi_string_get_cstr(MyModel->SelectedTagEpc); + + //Setting the width of the screen for the sliding window + uint32_t ScreenWidthChars = 24; + + // Calculate the start and end indices of the substring to draw + uint32_t StartPos = MyModel->ScrollOffset; + + //Calculate the length of the scrolling text + uint32_t Len = strlen(MyModel->ScrollingText); + + //I am sure there is a better way to do this that involves slightly safer memory management (i.e. malloc...) + char VisiblePart[ScreenWidthChars + 2]; + memset(VisiblePart, ' ', ScreenWidthChars); + VisiblePart[ScreenWidthChars] = '\0'; + + //Fill the array up with the epc values + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndex = StartPos + i; + if(CharIndex <= Len) { + VisiblePart[i] = MyModel->ScrollingText[CharIndex]; + } + } + + //Draw the visible part of the epc + canvas_draw_str(canvas, 28, 44, VisiblePart); + + //Cancel and Confirm buttons + elements_button_left(canvas, "Cancel"); + elements_button_right(canvas, "Confirm"); + furi_string_free(XStr); +} + +/** + * @brief Callback for delete input. + * @details This function is called when the user presses a button while on the delete screen. + * @param event The event - InputEvent object. + * @param context The context - UHFReaderApp object. + * @return true if the event was handled, false otherwise. +*/ +bool uhf_reader_view_delete_input_callback(InputEvent* event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Switch to the tag action menu if the left button pressed or the delete success screen if the right button pressed + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewTagAction); + return true; + } else if(event->key == InputKeyRight) { + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewDeleteSuccess); + return true; + } + } + + return false; +} + +/** + * @brief Callback when the user exits the delete screen. + * @details This function is called when the user exits the delete screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_delete_exit_callback(void* context) { + UNUSED(context); + return UHFReaderViewTagAction; +} + +/** + * @brief Callback when the user exits the delete screen. + * @details This function is called when the user exits the delete screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_delete_exit_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + furi_timer_stop(App->Timer); + furi_timer_free(App->Timer); + App->Timer = NULL; +} + +/** + * @brief Delete enter callback function. + * @details This function is called when the view transitions to the delete screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_delete_enter_callback(void* context) { + //Grab the period for the timer + int32_t Period = furi_ms_to_ticks(200); + UHFReaderApp* App = (UHFReaderApp*)context; + + //Create FuriStrings for storing saved UHF tag values + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + FuriString* TempEpcName = furi_string_alloc(); + FuriString* TempEpcStr = furi_string_alloc(); + + //Open the saved epcs text file + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open Saved file"); + flipper_format_file_close(App->EpcFile); + + } else { + //Read from the file selecting the current tag the user picked + furi_string_printf(TempStr, "Tag%ld", App->SelectedTagIndex); + if(!flipper_format_read_string(App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", App->SelectedTagIndex); + } else { + //Grabbing the extracted name and epc from the file to display to the user + const char* InputString = furi_string_get_cstr(TempTag); + char* ExtractedEpc = extract_epc(InputString); + char* ExtractedName = extract_name(InputString); + furi_string_set_str(TempEpcName, ExtractedName); + furi_string_set_str(TempEpcStr, ExtractedEpc); + App->EpcDelete = TempEpcStr; + App->EpcNameDelete = TempEpcName; + + //Set the view models for both this view and the delete success view + bool Redraw = true; + with_view_model( + App->ViewDelete, + UHFReaderDeleteModel * model, + { + furi_string_set(model->SelectedTagEpc, App->EpcDelete); + model->SelectedTagIndex = App->SelectedTagIndex; + furi_string_set(model->SelectedTagName, App->EpcNameDelete); + }, + Redraw); + with_view_model( + App->ViewDeleteSuccess, + UHFReaderDeleteModel * model, + { + furi_string_set(model->SelectedTagEpc, App->EpcDelete); + model->SelectedTagIndex = App->SelectedTagIndex; + furi_string_set(model->SelectedTagName, App->EpcNameDelete); + }, + Redraw); + //Close the file + flipper_format_file_close(App->EpcFile); + } + } + + //Freeing all FuriStrings used + furi_string_free(TempTag); + furi_string_free(TempStr); + furi_string_free(TempEpcName); + furi_string_free(TempEpcStr); + + //Start the timer + furi_assert(App->Timer == NULL); + App->Timer = + furi_timer_alloc(uhf_reader_view_epc_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(App->Timer, Period); +} + +/** + * @brief Allocates the delete view. + * @details This function allocates all variables for the delete view. + * @param context The context - UHFReaderApp object. +*/ +void view_delete_alloc(UHFReaderApp* App) { + //Allocating the view and setting all callback functions + App->ViewDelete = view_alloc(); + view_set_draw_callback(App->ViewDelete, uhf_reader_view_delete_draw_callback); + view_set_input_callback(App->ViewDelete, uhf_reader_view_delete_input_callback); + view_set_previous_callback(App->ViewDelete, uhf_reader_navigation_delete_exit_callback); + view_set_enter_callback(App->ViewDelete, uhf_reader_view_delete_enter_callback); + view_set_exit_callback(App->ViewDelete, uhf_reader_view_delete_exit_callback); + view_set_context(App->ViewDelete, App); + + //Allocating the view model + view_allocate_model(App->ViewDelete, ViewModelTypeLockFree, sizeof(UHFReaderDeleteModel)); + FuriString* DefaultEpcName = furi_string_alloc(); + furi_string_set_str(DefaultEpcName, "Default Name"); + UHFReaderDeleteModel* ModelDelete = view_get_model(App->ViewDelete); + + //Setting default values for the view model + ModelDelete->SelectedTagEpc = furi_string_alloc_set("ABCDEF12"); + ModelDelete->SelectedTagIndex = 1; + ModelDelete->SelectedTagName = DefaultEpcName; + ModelDelete->ScrollOffset = 0; + ModelDelete->ScrollingText = "Press Delete"; + App->EpcDelete = furi_string_alloc_set("Enter Name"); + App->EpcNameDelete = furi_string_alloc_set("Enter Name"); + view_dispatcher_add_view(App->ViewDispatcher, UHFReaderViewDelete, App->ViewDelete); +} + +/** + * @brief Frees the delete view. + * @details This function frees all variables for the delete view. + * @param context The context - UHFReaderApp object. +*/ +void view_delete_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewDelete); + view_free(App->ViewDelete); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_delete.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete.h new file mode 100644 index 00000000..3dc3fbd7 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../app.h" + +//Function Declarations +void uhf_reader_view_delete_draw_callback(Canvas* canvas, void* model); + +bool uhf_reader_view_delete_input_callback(InputEvent* event, void* context); + +uint32_t uhf_reader_navigation_delete_exit_callback(void* context); + +void uhf_reader_view_delete_enter_callback(void* context); + +void uhf_reader_view_delete_exit_callback(void* context); + +void view_delete_alloc(UHFReaderApp* App); + +void view_delete_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_delete_success.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete_success.c new file mode 100644 index 00000000..b7ed1fff --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete_success.c @@ -0,0 +1,161 @@ +#include "view_delete_success.h" + +/** + * @brief Delete Success Draw Callback. + * @details This function is called when the user confirmed deleting a saved UHF tag. + * @param canvas The canvas - Canvas object for drawing the screen. + * @param model The view model - model for the view with variables required for drawing. +*/ +void uhf_reader_view_delete_success_draw_callback(Canvas* canvas, void* model) { + UHFReaderDeleteModel* MyModel = (UHFReaderDeleteModel*)model; + FuriString* XStr = furi_string_alloc(); + + //Clearing the canvas, setting the color, font and content displayed. + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, " Successfully Deleted!"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 4, 22, "Name:"); + + //Displaying the name of the saved UHF Tag deleted. + canvas_draw_str(canvas, 32, 22, furi_string_get_cstr(MyModel->SelectedTagName)); + + //Displaying the index of the saved tag as shown in the Index file. + furi_string_printf(XStr, "%ld", MyModel->SelectedTagIndex); + canvas_draw_str(canvas, 4, 33, "EPC Index:"); + canvas_draw_str(canvas, 53, 33, furi_string_get_cstr(XStr)); + + //Displaying the EPC scrolling across the screen + canvas_draw_str(canvas, 4, 44, "EPC:"); + MyModel->ScrollingText = (char*)furi_string_get_cstr(MyModel->SelectedTagEpc); + + //Setting the width of the screen for the sliding window + uint32_t ScreenWidthChars = 24; + + // Calculate the start and end indices of the substring to draw + uint32_t StartPos = MyModel->ScrollOffset; + + //Calculate the length of the scrolling text + uint32_t Len = strlen(MyModel->ScrollingText); + + //I am sure there is a better way to do this that involves slightly safer memory management... + char VisiblePart[ScreenWidthChars + 2]; + memset(VisiblePart, ' ', ScreenWidthChars); + VisiblePart[ScreenWidthChars] = '\0'; + + //Fill the array up with the epc values + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndex = StartPos + i; + if(CharIndex <= Len) { + VisiblePart[i] = MyModel->ScrollingText[CharIndex]; + } + } + + //Draw the visible part of the epc + canvas_draw_str(canvas, 28, 44, VisiblePart); + + //Exit button + elements_button_center(canvas, "Exit"); + furi_string_free(XStr); +} + +/** + * @brief Callback for delete success input. + * @details This function is called when the user presses a button while on the delete success screen. + * @param event The event - InputEvent object. + * @param context The context - UHFReaderApp object. + * @return true if the event was handled, false otherwise. +*/ +bool uhf_reader_view_delete_success_input_callback(InputEvent* event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Return to the saved menu after deleting the saved epc + if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSaved); + return true; + } + } + + return false; +} + +/** + * @brief Delete Success enter callback function. + * @details This function is called when the view transitions to the delete success screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_delete_success_enter_callback(void* context) { + //Grab the period for the timer + int32_t Period = furi_ms_to_ticks(200); + UHFReaderApp* App = (UHFReaderApp*)context; + + //Call the helper functions below to update the saved UHF tag submenu + delete_and_update_entry(context, App->SelectedTagIndex); + update_dictionary_keys(context); + dolphin_deed(DolphinDeedRfidReadSuccess); + notification_message(App->Notifications, &sequence_success); + //Start the timer for the delete success screen + furi_assert(App->Timer == NULL); + App->Timer = + furi_timer_alloc(uhf_reader_view_epc_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(App->Timer, Period); +} + +/** + * @brief Callback when the user exits the delete success screen. + * @details This function is called when the user exits the delete success screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_delete_success_exit_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Stop and free the timer + furi_timer_stop(App->Timer); + furi_timer_free(App->Timer); + App->Timer = NULL; +} + +/** + * @brief Allocates the delete success view. + * @details This function allocates all variables for the delete success view. + * @param context The context - UHFReaderApp object. +*/ +void view_delete_success_alloc(UHFReaderApp* App) { + //Allocating the view and setting all callback functions + App->ViewDeleteSuccess = view_alloc(); + view_set_draw_callback(App->ViewDeleteSuccess, uhf_reader_view_delete_success_draw_callback); + view_set_input_callback( + App->ViewDeleteSuccess, uhf_reader_view_delete_success_input_callback); + view_set_previous_callback( + App->ViewDeleteSuccess, uhf_reader_navigation_saved_exit_callback); + view_set_enter_callback( + App->ViewDeleteSuccess, uhf_reader_view_delete_success_enter_callback); + view_set_exit_callback(App->ViewDeleteSuccess, uhf_reader_view_delete_success_exit_callback); + view_set_context(App->ViewDeleteSuccess, App); + + //Allocating the view model + view_allocate_model( + App->ViewDeleteSuccess, ViewModelTypeLockFree, sizeof(UHFReaderDeleteModel)); + UHFReaderDeleteModel* ModelDeleteSuccess = view_get_model(App->ViewDeleteSuccess); + + //Setting default values for the view model + ModelDeleteSuccess->SelectedTagEpc = furi_string_alloc_set("ABCDEF12"); + ModelDeleteSuccess->SelectedTagIndex = 1; + ModelDeleteSuccess->SelectedTagName = furi_string_alloc_set("Default Name"); + ModelDeleteSuccess->ScrollOffset = 0; + ModelDeleteSuccess->ScrollingText = "Press Delete"; + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewDeleteSuccess, App->ViewDeleteSuccess); +} + +/** + * @brief Frees the delete success view. + * @details This function frees all variables for the delete success view. + * @param context The context - UHFReaderApp object. +*/ +void view_delete_success_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewDeleteSuccess); + view_free(App->ViewDeleteSuccess); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_delete_success.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete_success.h new file mode 100644 index 00000000..0a7234ff --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_delete_success.h @@ -0,0 +1,15 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void uhf_reader_view_delete_success_draw_callback(Canvas* canvas, void* model); + +bool uhf_reader_view_delete_success_input_callback(InputEvent* event, void* context); + +void uhf_reader_view_delete_success_enter_callback(void* context); + +void uhf_reader_view_delete_success_exit_callback(void* context); + +void view_delete_success_alloc(UHFReaderApp* App); + +void view_delete_success_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_epc.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc.c new file mode 100644 index 00000000..5f7c4a88 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc.c @@ -0,0 +1,287 @@ +#include "view_epc.h" + +/** + * @brief Callback for the timer used for the epc info screen + * @details This function is called for timer events. + * @param context The UHFReaderApp - Used to change app variables. +*/ +void uhf_reader_view_epc_timer_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //TODO: Make this function usable by Tag Info Screen + //Update the offset for each value used in the draw callback + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * model, + { + uint32_t Len = strlen(model->ScrollingTextEpc); + uint32_t LenTid = strlen(model->ScrollingTextTid); + uint32_t LenRes = strlen(model->ScrollingTextRes); + uint32_t LenMem = strlen(model->ScrollingTextMem); + + //Incrementing each offset + model->ScrollOffsetEpc++; + model->ScrollOffsetTid++; + model->ScrollOffsetRes++; + model->ScrollOffsetMem++; + + //Check the bounds of the offset and reset if necessary + if(model->ScrollOffsetEpc >= Len) { + model->ScrollOffsetEpc = 0; + } + if(model->ScrollOffsetTid >= LenTid) { + model->ScrollOffsetTid = 0; + } + if(model->ScrollOffsetRes >= LenRes) { + model->ScrollOffsetRes = 0; + } + if(model->ScrollOffsetMem >= LenMem) { + model->ScrollOffsetMem = 0; + } + }, + true); + + //TODO: Make a timer function for delete screen and move this to it + with_view_model( + App->ViewDelete, + UHFReaderDeleteModel * model, + { + uint32_t LenEpc = strlen(model->ScrollingText); + model->ScrollOffset++; + if(model->ScrollOffset >= LenEpc) { + model->ScrollOffset = 0; + } + }, + true); + with_view_model( + App->ViewDeleteSuccess, + UHFReaderDeleteModel * model, + { + uint32_t LenEpc = strlen(model->ScrollingText); + model->ScrollOffset++; + if(model->ScrollOffset >= LenEpc) { + model->ScrollOffset = 0; + } + }, + true); + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFReaderEventIdRedrawScreen); +} + +/** + * @brief View EPC Draw Callback. + * @details This function is called when the user presses the down button on the read screen to show the tag details. + * @param canvas The canvas - Canvas object for drawing the screen. + * @param model The view model - model for the view with variables required for drawing. +*/ +void uhf_reader_view_epc_draw_callback(Canvas* canvas, void* model) { + UHFRFIDTagModel* MyModel = (UHFRFIDTagModel*)model; + FuriString* XStr = furi_string_alloc(); + + //Clearing the canvas, setting the color, font and content displayed. + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, " EPC Info:"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 4, 22, "EPC: "); + canvas_draw_str(canvas, 4, 44, "Reserved: "); + canvas_draw_str(canvas, 4, 33, "TID: "); + canvas_draw_str(canvas, 4, 55, "User Mem: "); + + //Displaying the EPC, TID, Reserved, and User memory in a scrolling fashion + MyModel->ScrollingTextEpc = (char*)furi_string_get_cstr(MyModel->Epc); + MyModel->ScrollingTextTid = (char*)furi_string_get_cstr(MyModel->Tid); + MyModel->ScrollingTextRes = (char*)furi_string_get_cstr(MyModel->Reserved); + MyModel->ScrollingTextMem = (char*)furi_string_get_cstr(MyModel->User); + + //Setting the width of the screen for the sliding window + uint32_t ScreenWidthChars = 24; + + // Calculate the start and end indices of the substring to draw + uint32_t StartPos = MyModel->ScrollOffsetEpc; + + //Calculate the length of the scrolling text + uint32_t Len = strlen(MyModel->ScrollingTextEpc); + + //I am sure there is a better way to do this that involves slightly safer memory management... + char VisiblePart[ScreenWidthChars + 2]; + memset(VisiblePart, ' ', ScreenWidthChars); + VisiblePart[ScreenWidthChars] = '\0'; + + //Fill the array up with the epc values + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndex = StartPos + i; + if(CharIndex <= Len) { + VisiblePart[i] = MyModel->ScrollingTextEpc[CharIndex]; + } + } + + //This is just the same code for the TID, Reserved, and User memory + //TODO: Make this into some sort of function to reduce code duplication + canvas_draw_str(canvas, 28, 22, VisiblePart); + uint32_t StartPosTid = MyModel->ScrollOffsetTid; + uint32_t LenTid = strlen(MyModel->ScrollingTextTid); + + char VisiblePartTid[ScreenWidthChars + 2]; + memset(VisiblePartTid, ' ', ScreenWidthChars); + VisiblePartTid[ScreenWidthChars] = '\0'; + + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndexTid = StartPosTid + i; + if(CharIndexTid <= LenTid) { + VisiblePartTid[i] = MyModel->ScrollingTextTid[CharIndexTid]; + } + } + + canvas_draw_str(canvas, 28, 33, VisiblePartTid); + uint32_t StartPosRes = MyModel->ScrollOffsetRes; + uint32_t LenRes = strlen(MyModel->ScrollingTextRes); + + char VisiblePartRes[ScreenWidthChars + 2]; + memset(VisiblePartRes, ' ', ScreenWidthChars); + VisiblePartRes[ScreenWidthChars] = '\0'; + + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndexRes = StartPosRes + i; + if(CharIndexRes <= LenRes) { + VisiblePartRes[i] = MyModel->ScrollingTextRes[CharIndexRes]; + } + } + + canvas_draw_str(canvas, 46, 44, VisiblePartRes); + uint32_t StartPosMem = MyModel->ScrollOffsetMem; + uint32_t LenMem = strlen(MyModel->ScrollingTextMem); + + char VisiblePartMem[ScreenWidthChars + 2]; + memset(VisiblePartMem, ' ', ScreenWidthChars); + VisiblePartMem[ScreenWidthChars] = '\0'; + + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndexMem = StartPosMem + i; + if(CharIndexMem <= LenMem) { + VisiblePartMem[i] = MyModel->ScrollingTextMem[CharIndexMem]; + } + } + + canvas_draw_str(canvas, 48, 55, VisiblePartMem); + furi_string_free(XStr); +} + +/** + * @brief Callback when the user exits the view epc info screen. + * @details This function is called when the user exits the epc info screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_exit_view_epc_callback(void* context) { + UNUSED(context); + return UHFReaderViewRead; +} + +/** + * @brief Tag Info enter callback function. + * @details This function is called when the view transitions to the Tag Info screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_epc_enter_callback(void* context) { + //Grab the period for the timer + uint32_t Period = furi_ms_to_ticks(300); + UHFReaderApp* App = (UHFReaderApp*)context; + + //Grab the current index for each memory bank + uint32_t CurTidIndex = App->CurTidIndex; + uint32_t CurResIndex = App->CurResIndex; + uint32_t CurMemIndex = App->CurMemIndex; + + //Display the read values for each memory bank + bool Redraw = true; + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * model, + { + if(App->NumberOfTidsToRead > 0 && CurTidIndex < App->NumberOfTidsToRead) { + furi_string_set_str(model->Tid, App->TidValues[CurTidIndex * 41]); + } + if(App->NumberOfResToRead > 0 && CurResIndex < App->NumberOfResToRead) { + furi_string_set_str(model->Reserved, App->ResValues[CurResIndex * 17]); + } + if(App->NumberOfMemToRead > 0 && CurMemIndex < App->NumberOfMemToRead) { + furi_string_set_str(model->User, App->MemValues[CurMemIndex * 33]); + } + }, + Redraw); + + //Start the timer + furi_assert(App->Timer == NULL); + App->Timer = + furi_timer_alloc(uhf_reader_view_epc_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(App->Timer, Period); +} + +/** + * @brief Callback when the user exits the read epc info screen. + * @details This function is called when the user exits the read epc info screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_epc_exit_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + furi_timer_stop(App->Timer); + furi_timer_free(App->Timer); + App->Timer = NULL; +} + +/** + * @brief Allocates the epc info view on the read screen. + * @details This function allocates all variables for the epc info view. + * @param context The context - UHFReaderApp object. +*/ +void view_epc_alloc(UHFReaderApp* App) { + //Allocating the view and setting all callback functions + App->ViewEpc = view_alloc(); + view_set_draw_callback(App->ViewEpc, uhf_reader_view_epc_draw_callback); + view_set_previous_callback(App->ViewEpc, uhf_reader_navigation_exit_view_epc_callback); + view_set_enter_callback(App->ViewEpc, uhf_reader_view_epc_enter_callback); + view_set_exit_callback(App->ViewEpc, uhf_reader_view_epc_exit_callback); + view_set_context(App->ViewEpc, App); + + //Allocating the view model + view_allocate_model(App->ViewEpc, ViewModelTypeLockFree, sizeof(UHFRFIDTagModel)); + UHFRFIDTagModel* ModelEpc = view_get_model(App->ViewEpc); + FuriString* UserMem = furi_string_alloc(); + furi_string_set_str(UserMem, "Press Read!"); + FuriString* TidMem = furi_string_alloc(); + furi_string_set_str(TidMem, "Press Read!"); + FuriString* Epc = furi_string_alloc(); + furi_string_set_str(Epc, "Press Read!"); + FuriString* ReservedMem = furi_string_alloc(); + furi_string_set_str(ReservedMem, "Press Read!"); + + + + //Setting default values for the view model + ModelEpc->User = UserMem; + ModelEpc->Epc = Epc; + ModelEpc->Tid = TidMem; + ModelEpc->Reserved = ReservedMem; + ModelEpc->ScrollOffsetEpc = 0; + ModelEpc->ScrollingTextEpc = "EPC VALUE HERE"; + ModelEpc->ScrollOffsetTid = 0; + ModelEpc->ScrollingTextTid = "TID VALUE HERE"; + ModelEpc->ScrollOffsetRes = 0; + ModelEpc->ScrollingTextRes = "RES VALUE HERE"; + ModelEpc->ScrollOffsetMem = 0; + ModelEpc->ScrollingTextMem = "MEM VALUE HERE"; + ModelEpc->Crc = furi_string_alloc_set("XXXX"); + ModelEpc->Pc = furi_string_alloc_set("XXXX"); + view_dispatcher_add_view(App->ViewDispatcher, UHFReaderViewEpcDump, App->ViewEpc); +} + +/** + * @brief Frees the epc info view. + * @details This function frees all variables for the epc info view. + * @param context The context - UHFReaderApp object. +*/ +void view_epc_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewEpcDump); + view_free(App->ViewEpc); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_epc.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc.h new file mode 100644 index 00000000..32c499d3 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc.h @@ -0,0 +1,17 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void uhf_reader_view_epc_timer_callback(void* context); + +void uhf_reader_view_epc_draw_callback(Canvas* canvas, void* model); + +uint32_t uhf_reader_navigation_exit_view_epc_callback(void* context); + +void uhf_reader_view_epc_enter_callback(void* context); + +void uhf_reader_view_epc_exit_callback(void* context); + +void view_epc_alloc(UHFReaderApp* App); + +void view_epc_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_epc_info.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc_info.c new file mode 100644 index 00000000..f3a09bc1 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc_info.c @@ -0,0 +1,272 @@ +#include "view_epc_info.h" + +/** + * @brief EPC Info Draw Callback. + * @details This function is called when the user selects tag info on the tag actions submenu. + * @param canvas The canvas - Canvas object for drawing the screen. + * @param model The view model - model for the view with variables required for drawing. +*/ +void uhf_reader_view_epc_info_draw_callback(Canvas* canvas, void* model) { + UHFRFIDTagModel* MyModel = (UHFRFIDTagModel*)model; + FuriString* XStr = furi_string_alloc(); + + //Clearing the canvas, setting the color, font and content displayed. + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, " EPC Info:"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 4, 22, "EPC: "); + canvas_draw_str(canvas, 4, 44, "Reserved: "); + canvas_draw_str(canvas, 4, 33, "TID: "); + canvas_draw_str(canvas, 4, 55, "User Mem: "); + + //Displaying the EPC, TID, Reserved, and User memory in a scrolling fashion + MyModel->ScrollingTextEpc = (char*)furi_string_get_cstr(MyModel->Epc); + MyModel->ScrollingTextTid = (char*)furi_string_get_cstr(MyModel->Tid); + MyModel->ScrollingTextRes = (char*)furi_string_get_cstr(MyModel->Reserved); + MyModel->ScrollingTextMem = (char*)furi_string_get_cstr(MyModel->User); + + //Setting the width of the screen for the sliding window + uint32_t ScreenWidthChars = 24; + + // Calculate the start and end indices of the substring to draw + uint32_t StartPos = MyModel->ScrollOffsetEpc; + + //Calculate the length of the scrolling text + uint32_t Len = strlen(MyModel->ScrollingTextEpc); + + //I am sure there is a better way to do this that involves slightly safer memory management... + char VisiblePart[ScreenWidthChars + 2]; + memset(VisiblePart, ' ', ScreenWidthChars); + VisiblePart[ScreenWidthChars] = '\0'; + + //Fill the array up with the epc values + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndex = StartPos + i; + if(CharIndex <= Len) { + VisiblePart[i] = MyModel->ScrollingTextEpc[CharIndex]; + } + } + + //This is just the same code for the TID, Reserved, and User memory + //TODO: Make this into some sort of function to reduce code duplication + canvas_draw_str(canvas, 28, 22, VisiblePart); + uint32_t StartPosTid = MyModel->ScrollOffsetTid; + uint32_t LenTid = strlen(MyModel->ScrollingTextTid); + + char VisiblePartTid[ScreenWidthChars + 2]; + memset(VisiblePartTid, ' ', ScreenWidthChars); + VisiblePartTid[ScreenWidthChars] = '\0'; + + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndexTid = StartPosTid + i; + if(CharIndexTid <= LenTid) { + VisiblePartTid[i] = MyModel->ScrollingTextTid[CharIndexTid]; + } + } + + canvas_draw_str(canvas, 28, 33, VisiblePartTid); + uint32_t StartPosRes = MyModel->ScrollOffsetRes; + uint32_t LenRes = strlen(MyModel->ScrollingTextRes); + + char VisiblePartRes[ScreenWidthChars + 2]; + memset(VisiblePartRes, ' ', ScreenWidthChars); + VisiblePartRes[ScreenWidthChars] = '\0'; + + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndexRes = StartPosRes + i; + if(CharIndexRes <= LenRes) { + VisiblePartRes[i] = MyModel->ScrollingTextRes[CharIndexRes]; + } + } + + canvas_draw_str(canvas, 46, 44, VisiblePartRes); + uint32_t StartPosMem = MyModel->ScrollOffsetMem; + uint32_t LenMem = strlen(MyModel->ScrollingTextMem); + + char VisiblePartMem[ScreenWidthChars + 2]; + memset(VisiblePartMem, ' ', ScreenWidthChars); + VisiblePartMem[ScreenWidthChars] = '\0'; + + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndexMem = StartPosMem + i; + if(CharIndexMem <= LenMem) { + VisiblePartMem[i] = MyModel->ScrollingTextMem[CharIndexMem]; + } + } + + canvas_draw_str(canvas, 48, 55, VisiblePartMem); + furi_string_free(XStr); +} + +/** + * @brief Callback when the user exits the tag info screen. + * @details This function is called when the user exits the tag info screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_exit_epc_info_callback(void* context) { + UNUSED(context); + return UHFReaderViewTagAction; +} + +/** + * @brief Callback for the timer used for the tag info screen + * @details This function is called for timer events. + * @param context The UHFReaderApp - Used to change app variables. +*/ +void uhf_reader_view_epc_info_timer_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Update the offset for each value used in the draw callback + with_view_model( + App->ViewEpcInfo, + UHFRFIDTagModel * model, + { + uint32_t Len = strlen(model->ScrollingTextEpc); + uint32_t LenTid = strlen(model->ScrollingTextTid); + uint32_t LenRes = strlen(model->ScrollingTextRes); + uint32_t LenMem = strlen(model->ScrollingTextMem); + + //Incrementing each offset + model->ScrollOffsetEpc++; + model->ScrollOffsetTid++; + model->ScrollOffsetRes++; + model->ScrollOffsetMem++; + + //Check the bounds of the offset and reset if necessary + if(model->ScrollOffsetEpc >= Len) { + model->ScrollOffsetEpc = 0; + } + if(model->ScrollOffsetTid >= LenTid) { + model->ScrollOffsetTid = 0; + } + if(model->ScrollOffsetRes >= LenRes) { + model->ScrollOffsetRes = 0; + } + if(model->ScrollOffsetMem >= LenMem) { + model->ScrollOffsetMem = 0; + } + }, + true); + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFReaderEventIdRedrawScreen); +} + +/** + * @brief Tag Info enter callback function. + * @details This function is called when the view transitions to the Tag Info screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_epc_info_enter_callback(void* context) { + //Grab the period for the timer + uint32_t Period = furi_ms_to_ticks(200); + UHFReaderApp* App = (UHFReaderApp*)context; + + //Create FuriStrings for storing saved UHF tag values + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + + //Open the saved epcs text file + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open Saved file"); + flipper_format_file_close(App->EpcFile); + + } else { + //Read from the file selecting the current tag the user picked + furi_string_printf(TempStr, "Tag%ld", App->SelectedTagIndex); + if(!flipper_format_read_string(App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", App->SelectedTagIndex); + } else { + //Grabbing the extracted tid, reserved memory, user memory, and epc from the file to display to the user + const char* InputString = furi_string_get_cstr(TempTag); + bool Redraw = true; + with_view_model( + App->ViewEpcInfo, + UHFRFIDTagModel * model, + { + furi_string_set(model->Epc, extract_epc(InputString)); + furi_string_set(model->Tid, extract_tid(InputString)); + furi_string_set(model->Reserved, extract_res(InputString)); + furi_string_set(model->User, extract_mem(InputString)); + }, + Redraw); + //Close the file + flipper_format_file_close(App->EpcFile); + } + } + + //Freeing all FuriStrings used + furi_string_free(TempTag); + furi_string_free(TempStr); + + //Start the timer + furi_assert(App->Timer == NULL); + App->Timer = + furi_timer_alloc(uhf_reader_view_epc_info_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(App->Timer, Period); +} + +/** + * @brief Callback when the user exits the tag info screen. + * @details This function is called when the user exits the tag info screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_epc_info_exit_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + furi_timer_stop(App->Timer); + furi_timer_free(App->Timer); + App->Timer = NULL; +} + +/** + * @brief Allocates the tag info view. + * @details This function allocates all variables for the tag info view. + * @param context The context - UHFReaderApp object. +*/ +void view_epc_info_alloc(UHFReaderApp* App) { + //Allocating the view and setting all callback functions + App->ViewEpcInfo = view_alloc(); + view_set_draw_callback(App->ViewEpcInfo, uhf_reader_view_epc_info_draw_callback); + view_set_previous_callback(App->ViewEpcInfo, uhf_reader_navigation_exit_epc_info_callback); + view_set_enter_callback(App->ViewEpcInfo, uhf_reader_view_epc_info_enter_callback); + view_set_exit_callback(App->ViewEpcInfo, uhf_reader_view_epc_info_exit_callback); + view_set_context(App->ViewEpcInfo, App); + + //Allocating the view model + view_allocate_model(App->ViewEpcInfo, ViewModelTypeLockFree, sizeof(UHFRFIDTagModel)); + UHFRFIDTagModel* ModelEpcInfo = view_get_model(App->ViewEpcInfo); + FuriString* UserMemEpcInfo = furi_string_alloc(); + furi_string_set_str(UserMemEpcInfo, "000000..."); + FuriString* TidMemEpcInfo = furi_string_alloc(); + furi_string_set_str(TidMemEpcInfo, "E2801105200...."); + FuriString* EpcInfo = furi_string_alloc(); + furi_string_set_str(EpcInfo, "ABCDEF12"); + FuriString* ReservedMemEpcInfo = furi_string_alloc(); + furi_string_set_str(ReservedMemEpcInfo, "131313..."); + + //Setting default values for the view model + ModelEpcInfo->User = UserMemEpcInfo; + ModelEpcInfo->Epc = EpcInfo; + ModelEpcInfo->Tid = TidMemEpcInfo; + ModelEpcInfo->Reserved = ReservedMemEpcInfo; + ModelEpcInfo->ScrollOffsetEpc = 0; + ModelEpcInfo->ScrollingTextEpc = "EPC VALUE HERE"; + ModelEpcInfo->ScrollOffsetTid = 0; + ModelEpcInfo->ScrollingTextTid = "TID VALUE HERE"; + ModelEpcInfo->ScrollOffsetRes = 0; + ModelEpcInfo->ScrollingTextRes = "RES VALUE HERE"; + ModelEpcInfo->ScrollOffsetMem = 0; + ModelEpcInfo->ScrollingTextMem = "MEM VALUE HERE"; + view_dispatcher_add_view(App->ViewDispatcher, UHFReaderViewEpcInfo, App->ViewEpcInfo); +} + +/** + * @brief Frees the tag info view. + * @details This function frees all variables for the tag info view. + * @param context The context - UHFReaderApp object. +*/ +void view_epc_info_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewEpcInfo); + view_free(App->ViewEpcInfo); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_epc_info.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc_info.h new file mode 100644 index 00000000..bda43eb0 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_epc_info.h @@ -0,0 +1,17 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void uhf_reader_view_epc_info_draw_callback(Canvas* canvas, void* model); + +uint32_t uhf_reader_navigation_exit_epc_info_callback(void* context); + +void uhf_reader_view_epc_info_enter_callback(void* context); + +void uhf_reader_view_epc_info_exit_callback(void* context); + +void uhf_reader_view_epc_info_timer_callback(void* context); + +void view_epc_info_alloc(UHFReaderApp* App); + +void view_epc_info_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_kill.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_kill.c new file mode 100644 index 00000000..06265eae --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_kill.c @@ -0,0 +1,434 @@ +#include "view_kill.h" +#include "../helpers/yrm100x_module.h" +/** + * @brief Callback for navigation on kill screen. + * @details This function is called when the user exits a screen on the kill screen + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_kill_callback(void* context) { + UNUSED(context); + return UHFReaderViewKill; +} + +/** + * @brief Fetch selected tag's info + * @details This function populates the app's tag variables based on the selected saved tag + * @param context The context - The App +*/ +void uhf_reader_fetch_selected_tag(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Allocate space for the FuriStrings used + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + + //Open the saved epcs file to extract the uhf tag info + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open Saved file"); + flipper_format_file_close(App->EpcFile); + } else { + furi_string_printf(TempStr, "Tag%ld", App->SelectedTagIndex); + if(!flipper_format_read_string(App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", App->SelectedTagIndex); + } else { + //Grab the saved uhf tag info from the saved epcs file + const char* InputString = furi_string_get_cstr(TempTag); + furi_string_set(App->EpcToWrite, extract_epc(InputString)); + furi_string_set(App->EpcName, extract_name(InputString)); + + //Set the write model uhf tag values accordingly + bool redraw = true; + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(Model->EpcValue, extract_epc(InputString)); + furi_string_set(Model->TidValue, extract_tid(InputString)); + furi_string_set(Model->ResValue, extract_res(InputString)); + furi_string_set(Model->MemValue, extract_mem(InputString)); + furi_string_set(Model->Pc, extract_pc(InputString)); + furi_string_set(Model->Crc, extract_crc(InputString)); + }, + redraw); + //Close the file + flipper_format_file_close(App->EpcFile); + } + } + + furi_string_free(TempTag); + furi_string_free(TempStr); +} + +/** + * @brief Callback for the uhf worker for writing. + * @details This function is called when the uhf worker is started for writing. + * @param event The UHFWorkerEvent - UHFReaderApp object. + * @param context The context - UHFReaderApp object. +*/ +void uhf_kill_tag_worker_callback(UHFWorkerEvent event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + if(event == UHFWorkerEventSuccess) { + dolphin_deed(DolphinDeedNfcReadSuccess); + + //Stop blinking the led + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_success); + + //Set the boolean false that tracks which password is being used + App->YRM100XWorker->KillPwd = false; + + //Reset the popup + popup_reset(App->LockPopup); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewKill); + } else if(event == UHFWorkerEventAborted) { + //Set the boolean false that tracks which password is being used + App->YRM100XWorker->KillPwd = false; + //Stop blinking the led + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_error); + + //Reset the popup + popup_reset(App->LockPopup); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewKill); + } +} +/** + * @brief Handles the kill menu. + * @details This function handles the kill password that is set from the kill screen + * @param context The UHFReaderApp app - used to allocate app variables and views. +*/ +void uhf_reader_kill_password_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool Redraw = true; + // Temporary buffer to hold the converted string + char* tempBuffer = (char*)malloc(12); + + snprintf(tempBuffer, 12, "%s", convert_to_hex_string(App->KillPwdTempBuffer, 4)); + + //Changing the read screen's power value to the one set in the configuration menu + if(App->UHFModuleType != YRM100X_MODULE) { + //TODO: ADD SUPPORT FOR M6E and M7E + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + //Send the power command to the RPi Zero + uart_helper_send(App->UartHelper, "SETKILLPWD\n", 11); + + //Set the current power determined by user + furi_string_set(Model->SettingKillPwd, tempBuffer); + + //Send the power value to the RPi Zero + uart_helper_send_string(App->UartHelper, Model->SettingKillPwd); + }, + Redraw); + + } else { + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + //Set the current power determined by user + furi_string_set(Model->SettingKillPwd, tempBuffer); + + if(App->ReaderConnected) { + uhf_reader_fetch_selected_tag(App); + memset(App->ResBytes, 0, 8 * sizeof(uint8_t)); + memset(App->PcBytes, 0, 2 * sizeof(uint16_t)); + memset(App->CrcBytes, 0, 2 * sizeof(uint16_t)); + + // Resetting the size_t variables to zero + App->ResBytesLen = 0; + App->PcBytesLen = 0; + App->CrcBytesLen = 0; + //uint16_t PcBytes[4]; + //size_t PcBytesLen; + // uint16_t CrcBytes[4]; + //size_t CrcBytesLen; + //uint8_t ResBytes + // [4]; //Technically can be up to 96 bits in length for epc gen 2. We only care about the first 8.... + //size_t ResBytesLen; + + //uhf_reader_fetch_selected_tag(App); + UHFTag* TempTag = App->YRM100XWorker->NewTag; + + uhf_tag_reset(TempTag); + + hex_string_to_uint16( + furi_string_get_cstr(Model->Pc), App->PcBytes, &App->PcBytesLen); + hex_string_to_uint16( + furi_string_get_cstr(Model->Crc), App->CrcBytes, &App->CrcBytesLen); + + uint16_t combinedPc = 0; + uint16_t combinedCrc = 0; + + for(size_t i = 0; i < 4; i++) { + combinedPc |= App->PcBytes[i]; + } + + for(size_t i = 0; i < 4; i++) { + combinedCrc |= App->CrcBytes[i]; + } + hex_string_to_bytes(tempBuffer, App->ResBytes, &App->ResBytesLen); + uhf_tag_set_kill_pwd(TempTag, App->KillPwdTempBuffer, 4); + uhf_tag_set_epc_pc(TempTag, combinedPc); + uhf_tag_set_epc_crc(TempTag, combinedCrc); + m100_enable_write_mask(App->YRM100XWorker->module, WRITE_RFU); + App->YRM100XWorker->KillPwd = true; + + uhf_worker_start( + App->YRM100XWorker, + UHFWorkerStateWriteSingle, + uhf_kill_tag_worker_callback, + App); + notification_message(App->Notifications, &uhf_sequence_blink_start_cyan); + } + }, + Redraw); + } + Popup* PopupLock = App->LockPopup; + popup_set_header(PopupLock, "Setting\nKill\nPassword", 68, 30, AlignLeft, AlignTop); + popup_set_icon(PopupLock, 0, 3, &I_RFIDDolphinReceive_97x61); + + free(tempBuffer); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLockPopup); +} + +/** + * @brief Handles the kill confirm menu. + * @details This function handles the password confirmation screen + * @param context The UHFReaderApp app - used to allocate app variables and views. +*/ +void uhf_reader_kill_confirm_password_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool Redraw = true; + Popup* PopupLock = App->LockPopup; + popup_reset(PopupLock); + popup_set_header(PopupLock, "Killing\nUHF\nTag!", 68, 30, AlignLeft, AlignTop); + popup_set_icon(PopupLock, 0, 3, &I_RFIDDolphinReceive_97x61); + notification_message(App->Notifications, &uhf_sequence_blink_start_cyan); + // Temporary buffer to hold the converted string + char* tempBuffer = (char*)malloc(9); + + snprintf(tempBuffer, 9, "%s", convert_to_hex_string(App->KillConfirmPwdTempBuffer, 4)); + + //Changing the read screen's power value to the one set in the configuration menu + if(App->UHFModuleType != YRM100X_MODULE) { + //TODO: ADD SUPPORT FOR M6E and M7E + uart_helper_send(App->UartHelper, "KILLTAG\n", 11); + // view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLockPopup); + + } else { + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(Model->ResValue, tempBuffer); + + if(App->ReaderConnected) { + //This is where we should kill the tag. + uint32_t returnResponse = 0; + // uint16_t PcBytes[4]; + // size_t PcBytesLen; + // uint16_t CrcBytes[4]; + // size_t CrcBytesLen; + // uint8_t ResBytes + // [4]; //Technically can be up to 96 bits in length for epc gen 2. We only care about the first 8.... + // size_t ResBytesLen; + + //uhf_reader_fetch_selected_tag(App); + uhf_reader_fetch_selected_tag(App); + memset(App->ResBytes, 0, 8 * sizeof(uint8_t)); + memset(App->PcBytes, 0, 2 * sizeof(uint16_t)); + memset(App->CrcBytes, 0, 2 * sizeof(uint16_t)); + + // Resetting the size_t variables to zero + App->ResBytesLen = 0; + App->PcBytesLen = 0; + App->CrcBytesLen = 0; + UHFTag* TempTag = App->YRM100XWorker->NewTag; + + uhf_tag_reset(TempTag); + + hex_string_to_uint16( + furi_string_get_cstr(Model->Pc), App->PcBytes, &App->PcBytesLen); + hex_string_to_uint16( + furi_string_get_cstr(Model->Crc), App->CrcBytes, &App->CrcBytesLen); + + uint16_t combinedPc = 0; + uint16_t combinedCrc = 0; + + for(size_t i = 0; i < 4; i++) { + combinedPc |= App->PcBytes[i]; + } + + for(size_t i = 0; i < 4; i++) { + combinedCrc |= App->CrcBytes[i]; + } + hex_string_to_bytes(tempBuffer, App->ResBytes, &App->ResBytesLen); + + uhf_tag_set_kill_pwd(TempTag, App->ResBytes, 4); + uhf_tag_set_epc_pc(TempTag, combinedPc); + uhf_tag_set_epc_crc(TempTag, combinedCrc); + + // Start worker + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLockPopup); + while(true) { + returnResponse = m100_kill_tag( + App->YRM100XWorker->module, + bytes_to_uint32(App->KillConfirmPwdTempBuffer, 4)); + if(returnResponse == M100SuccessResponse) { + notification_message(App->Notifications, &sequence_success); + break; + } else if(returnResponse == M100APWrong) { + notification_message(App->Notifications, &sequence_error); + break; + } + continue; + } + notification_message(App->Notifications, &uhf_sequence_blink_stop); + popup_reset(PopupLock); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewKill); + } + }, + Redraw); + } + + //Switch back to the configuration view + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewKill); +} +/** + * @brief Kill Submenu Callback + * @details Handles the different submenu options for the kill tag menu. + * @param context The UHFReaderApp - the app for working with variables + * @param index The selected submenu index +*/ +void uhf_reader_submenu_kill_callback(void* context, uint32_t index) { + UHFReaderApp* App = (UHFReaderApp*)context; + + switch(index) { + //Case for setting the kill password + case UHFReaderSubmenuIndexSetKillPwd: + //Using a byte input type + byte_input_set_header_text(App->KillInput, App->KillPasswordPlaceHolder); + byte_input_set_result_callback( + App->KillInput, + uhf_reader_kill_password_updated, + NULL, + App, + App->KillPwdTempBuffer, + App->KillPwdInputBufferSize); + view_set_previous_callback( + byte_input_get_view(App->KillInput), uhf_reader_navigation_kill_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSetKillPwd); + break; + + //Case for the kill tag action + case UHFReaderSubmenuIndexKillTag: + + byte_input_set_header_text(App->KillConfirmInput, App->KillConfirmPasswordPlaceHolder); + byte_input_set_result_callback( + App->KillConfirmInput, + uhf_reader_kill_confirm_password_updated, + NULL, + App, + App->KillConfirmPwdTempBuffer, + App->KillPwdInputBufferSize); + view_set_previous_callback( + byte_input_get_view(App->KillConfirmInput), uhf_reader_navigation_kill_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewKillConfirm); + break; + default: + break; + } +} + +/** + * @brief Callback when the user exits the kill tag screen. + * @details This function is called when the user exits the kill tag screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_kill_exit_callback(void* context) { + UNUSED(context); + return UHFReaderViewTagAction; +} + +/** + * @brief Allocates kill input view + * @details This function allocates the kill byte input view + * @param context The context - The App +*/ +void kill_menu_alloc(UHFReaderApp* App) { + App->KillInput = byte_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSetKillPwd, byte_input_get_view(App->KillInput)); + App->KillPwdInputBufferSize = 4; + App->KillPwdTempBuffer = (uint8_t*)malloc(App->KillPwdInputBufferSize); +} + +/** + * @brief Allocates kill confirm input view + * @details This function allocates the kill confirm byte input view + * @param context The context - The App +*/ +void kill_confirm_menu_alloc(UHFReaderApp* App) { + App->KillConfirmInput = byte_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewKillConfirm, byte_input_get_view(App->KillConfirmInput)); + App->KillConfirmPwdTempBuffer = (uint8_t*)malloc(4); +} + +/** + * @brief Allocates the kill tag view. + * @details This function allocates all variables for the kill tag view. + * @param app The UHFReaderApp object. +*/ +void view_kill_alloc(UHFReaderApp* App) { + //Allocate the submenu for the kill menu + App->SubmenuKillActions = submenu_alloc(); + submenu_set_header(App->SubmenuKillActions, "Kill Tag Options: "); + + //Creating placeholders for the views used for the kill feature + App->KillPasswordPlaceHolder = strdup("Enter Kill Password!"); + App->KillConfirmPasswordPlaceHolder = strdup("Confirm Kill Password!"); + App->DefaultKillPassword = strdup("00000000"); + + //Allocate the kill input views + kill_menu_alloc(App); + kill_confirm_menu_alloc(App); + submenu_add_item( + App->SubmenuKillActions, + "Set Kill Password", + UHFReaderSubmenuIndexSetKillPwd, + uhf_reader_submenu_kill_callback, + App); + submenu_add_item( + App->SubmenuKillActions, + "Kill Tag (Permanent)", + UHFReaderSubmenuIndexKillTag, + uhf_reader_submenu_kill_callback, + App); + view_set_previous_callback( + submenu_get_view(App->SubmenuKillActions), uhf_reader_navigation_kill_exit_callback); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewKill, submenu_get_view(App->SubmenuKillActions)); +} + +/** + * @brief Frees the kill tag view. + * @details This function frees all variables for the kill tag view. + * @param context The context - UHFReaderApp object. +*/ +void view_kill_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSetKillPwd); + byte_input_free(App->KillInput); + free(App->KillPwdTempBuffer); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewKillConfirm); + byte_input_free(App->KillConfirmInput); + free(App->KillConfirmPwdTempBuffer); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewKill); + submenu_free(App->SubmenuKillActions); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_kill.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_kill.h new file mode 100644 index 00000000..775e8482 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_kill.h @@ -0,0 +1,15 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void uhf_reader_submenu_kill_callback(void* context, uint32_t index); + +uint32_t uhf_reader_navigation_kill_callback(void* context); + +uint32_t uhf_reader_navigation_kill_exit_callback(void* context); + +void view_kill_alloc(UHFReaderApp* App); + +void view_kill_free(UHFReaderApp* App); + +void uhf_reader_fetch_selected_tag(void* context); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_lock.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_lock.c new file mode 100644 index 00000000..ac20c8c8 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_lock.c @@ -0,0 +1,424 @@ +#include "view_lock.h" +#include "view_kill.h" +/** + * @brief Callback when the user exits the lock screen. + * @details This function is called when the user exits the lock screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_lock_callback(void* context) { + UNUSED(context); + return UHFReaderViewLock; +} + +/** + * @brief Callback for the uhf worker for locking. + * @details This function is called when the uhf worker is started for locking. + * @param event The UHFWorkerEvent - UHFReaderApp object. + * @param context The context - UHFReaderApp object. +*/ +void uhf_access_tag_worker_callback(UHFWorkerEvent event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + if(event == UHFWorkerEventSuccess) { + dolphin_deed(DolphinDeedNfcReadSuccess); + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_success); + App->YRM100XWorker->AccessPwd = false; + popup_reset(App->LockPopup); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLock); + } else if(event == UHFWorkerEventAborted) { + App->YRM100XWorker->AccessPwd = false; + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_error); + popup_reset(App->LockPopup); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLock); + } +} +/** + * @brief Allocates the access password input view + * @details Allocates the set access password input view + * @param context The UHFReaderApp app - used to allocate app variables and views. +*/ +void access_password_menu_alloc(UHFReaderApp* App) { + App->SetApInput = byte_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSetAccessPwd, byte_input_get_view(App->SetApInput)); + App->SetPwdTempBuffer = (uint8_t*)malloc(4); +} +/** + * @brief Handles the set access password view. + * @details Used when the access password is set through the GUI + * @param context The UHFReaderApp app - used to allocate app variables and views. +*/ +void uhf_reader_access_password_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool Redraw = true; + // Temporary buffer to hold the converted string + char* tempBuffer = (char*)malloc(8); + + snprintf(tempBuffer, 8, "%s", convert_to_hex_string(App->SetPwdTempBuffer, 4)); + + if(App->UHFModuleType != YRM100X_MODULE) { + //TODO: ADD SUPPORT FOR M6E and M7E + uart_helper_send(App->UartHelper, "SETPWD\n", 7); + + } else { + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(Model->ResValue, tempBuffer); + + if(App->ReaderConnected) { + //This is where we should write... + uhf_reader_fetch_selected_tag(App); + memset(App->ResBytes, 0, 8 * sizeof(uint8_t)); + memset(App->PcBytes, 0, 2 * sizeof(uint16_t)); + memset(App->CrcBytes, 0, 2 * sizeof(uint16_t)); + + // Resetting the size_t variables to zero + App->ResBytesLen = 0; + App->PcBytesLen = 0; + App->CrcBytesLen = 0; + + UHFTag* TempTag = App->YRM100XWorker->NewTag; + + uhf_tag_reset(TempTag); + + hex_string_to_uint16( + furi_string_get_cstr(Model->Pc), App->PcBytes, &App->PcBytesLen); + hex_string_to_uint16( + furi_string_get_cstr(Model->Crc), App->CrcBytes, &App->CrcBytesLen); + + uint16_t combinedPc = 0; + uint16_t combinedCrc = 0; + + for(size_t i = 0; i < 4; i++) { + combinedPc |= App->PcBytes[i]; + } + + for(size_t i = 0; i < 4; i++) { + combinedCrc |= App->CrcBytes[i]; + } + + //Set the reserved bank value + bool redraw = true; + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + hex_string_to_bytes( + furi_string_get_cstr(Model->ResValue), + App->ResBytes, + &App->ResBytesLen); + uhf_tag_set_kill_pwd(TempTag, App->ResBytes, 4); + }, + redraw); + + uhf_tag_set_access_pwd(TempTag, App->SetPwdTempBuffer, 4); + + m100_enable_write_mask(App->YRM100XWorker->module, WRITE_RFU); + + uhf_tag_set_epc_pc(TempTag, combinedPc); + uhf_tag_set_epc_crc(TempTag, combinedCrc); + App->YRM100XWorker->AccessPwd = true; + + uhf_worker_start( + App->YRM100XWorker, + UHFWorkerStateWriteSingle, + uhf_access_tag_worker_callback, + App); + notification_message(App->Notifications, &uhf_sequence_blink_start_cyan); + } + }, + Redraw); + } + //Switch to the popup + Popup* PopupLock = App->LockPopup; + popup_set_header(PopupLock, "Setting\nAccess\nPassword", 68, 30, AlignLeft, AlignTop); + popup_set_icon(PopupLock, 0, 3, &I_RFIDDolphinReceive_97x61); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLockPopup); +} + +/** + * @brief Callback when the user exits the lock screen. + * @details This function is called when the user exits the lock screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_lock_exit_callback(void* context) { + UNUSED(context); + return UHFReaderViewTagAction; +} + +/** + * @brief Maps lock type to string + * @details This function is used to change the lock type value for the lock popups. + * @param type LockType - The locktype + * @return the string representing the lock type action +*/ +const char* get_lock_bank_type_string(LockType type) { + switch(type) { + case Lock: + return "Locking"; + case Unlock: + return "Unlocking"; + case PermaUnlock: + return "Perma Unlocking"; + case PermaLock: + return "Perma Locking"; + default: + return "Unknown"; + } +} + +/** + * @brief Maps bank type to string + * @details This function is used to change the bank type value for the lock popups. + * @param bank BankType - The bank type + * @return the string representing the bank type +*/ +const char* get_memory_bank_string(BankType bank) { + switch(bank) { + case ReservedBank: + return "Reserved\nBank"; + case EPCBank: + return "EPC\nBank"; + case TIDBank: + return "TID\nBank"; + case UserBank: + return "User\nBank"; + case KillPwd: + return "Kill\nPassword"; + case AccessPwd: + return "Access\nPassword"; + case FileZero: + return "User\nBank"; + default: + return "Unknown\nBank"; + } +} +/** + * @brief Handles the lock items clicked + * @details Handles hanldes the lock actions + * @param context, index - context used for UHFReaderApp, index used for state check. +*/ +void uhf_reader_lock_item_clicked(void* context, uint32_t index) { + UHFReaderApp* App = (UHFReaderApp*)context; + Popup* PopupLock = App->LockPopup; + uint32_t returnResponse = 0; + char* header = malloc(68 * sizeof(char)); + index++; + + //Check if the set AP menu is being selected + if(index == 1) { + // Header to display on the access password input screen. + byte_input_set_header_text(App->SetApInput, App->SetAccessPasswordPlaceHolder); + byte_input_set_result_callback( + App->SetApInput, + uhf_reader_access_password_updated, + NULL, + App, + App->SetPwdTempBuffer, + App->KillPwdInputBufferSize); + view_set_previous_callback( + byte_input_get_view(App->SetApInput), uhf_reader_navigation_lock_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSetAccessPwd); + } else if(index == 4) { + // Create a dynamic string for the popup header + snprintf( + header, + 68, + "%s\n%s", + get_lock_bank_type_string(App->DefaultLockType), + get_memory_bank_string(App->DefaultLockBank)); + + // Set the popup header with the dynamic values and switch to it + popup_set_header(PopupLock, header, 68, 30, AlignLeft, AlignTop); + popup_set_icon(PopupLock, 0, 3, &I_RFIDDolphinReceive_97x61); + notification_message(App->Notifications, &uhf_sequence_blink_start_cyan); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLockPopup); + + while(true) { + returnResponse = m100_lock_label_data( + App->YRM100XWorker->module, + App->DefaultLockBank, + App->YRM100XWorker->DefaultAP, + App->DefaultLockType); + if(returnResponse == M100SuccessResponse) { + notification_message(App->Notifications, &sequence_success); + dolphin_deed(DolphinDeedNfcReadSuccess); + break; + } else if(returnResponse == M100APWrong) { + notification_message(App->Notifications, &sequence_error); + break; + } + continue; + } + notification_message(App->Notifications, &uhf_sequence_blink_stop); + popup_reset(App->LockPopup); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLock); + free(header); + } +} + +/** + * @brief Handles the UHF Bank Selection + * @details This function handles switching between different memory banks for locking the UHF RFID Tag + * @param item VariableItem - the current selection for the bank. +*/ +void uhf_reader_setting_lock_bank_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + uint8_t Index = variable_item_get_current_value_index(Item); + + if(Index == 1) { + App->DefaultLockBank = AccessPwd; + } else if(Index == 2) { + App->DefaultLockBank = EPCBank; + } else if(Index == 3) { + App->DefaultLockBank = TIDBank; + } else if(Index == 4) { + App->DefaultLockBank = FileZero; + } else { + App->DefaultLockBank = KillPwd; + } + variable_item_set_current_value_text(Item, App->SettingLockBankNames[Index]); +} + +/** + * @brief Handles the UHF Lock Action Selection + * @details This function handles switching between different supported lock actions for the selected UHF RFID Memory Bank. + * @param item VariableItem - the current selection for the lock action +*/ +void uhf_reader_setting_lock_action_change(VariableItem* Item) { + UHFReaderApp* App = variable_item_get_context(Item); + uint8_t Index = variable_item_get_current_value_index(Item); + if(Index == 1) { + App->DefaultLockType = PermaUnlock; + } else if(Index == 2) { + App->DefaultLockType = Lock; + } else if(Index == 3) { + App->DefaultLockType = PermaLock; + } else { + App->DefaultLockType = Unlock; + } + variable_item_set_current_value_text(Item, App->SettingLockActionNames[Index]); +} +/** + * @brief Allocates the lock view + * @details This function allocates all variables for the lock view. + * @param app The UHFReaderApp object. +*/ +void view_lock_alloc(UHFReaderApp* App) { + //Setting variables + App->SettingApLabel = "Set AP"; + App->SetAccessPasswordPlaceHolder = strdup("Enter Access Password!"); + App->SettingApDefaultPassword = strdup("00000000"); + + //Options for different banks to lock following the Gen2 Protocol Standard https://www.gs1.org/sites/default/files/docs/epc/Gen2_Protocol_Standard.pdf + App->SettingLockBankConfigLabel = "Memory Bank"; + App->SettingLockBankValues[0] = 1; + App->SettingLockBankValues[1] = 2; + App->SettingLockBankValues[2] = 3; + App->SettingLockBankValues[3] = 4; + App->SettingLockBankValues[4] = 5; + App->SettingLockBankNames[0] = "Kill"; + App->SettingLockBankNames[1] = "AP"; + App->SettingLockBankNames[2] = "EPC"; + App->SettingLockBankNames[3] = "TID"; + App->SettingLockBankNames[4] = "User"; + App->DefaultLockBank = KillPwd; + + //Options for the lock mode + App->SettingLockActionConfigLabel = "Lock Mode"; + App->SettingLockActionValues[0] = 1; + App->SettingLockActionValues[1] = 2; + App->SettingLockActionValues[2] = 3; + App->SettingLockActionValues[3] = 4; + App->SettingLockActionNames[0] = "Unlock"; + App->SettingLockActionNames[1] = "Perm-U"; + App->SettingLockActionNames[2] = "Lock"; + App->SettingLockActionNames[3] = "Perm-L"; + App->DefaultLockType = Unlock; + //The Button for executing the desired lock command and storing the output + App->SettingLockExecuteConfigLabel = "Execute"; + App->SettingLockExecuteResult = strdup("Press Me!"); + + //Allocating the set access password menu + access_password_menu_alloc(App); + + //Allocating the popup shown when locking the tags + App->LockPopup = popup_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewLockPopup, popup_get_view(App->LockPopup)); + + //Creating the variable item list for the lock menu options + App->VariableItemListLock = variable_item_list_alloc(); + variable_item_list_reset(App->VariableItemListLock); + + //The set access password menu + App->DefaultLockAccessPwdStr = furi_string_alloc_set(App->SettingApDefaultPassword); + App->SettingLockApPwdItem = + variable_item_list_add(App->VariableItemListLock, App->SettingApLabel, 1, NULL, NULL); + variable_item_set_current_value_text( + App->SettingLockApPwdItem, furi_string_get_cstr(App->DefaultLockAccessPwdStr)); + variable_item_list_set_enter_callback( + App->VariableItemListLock, uhf_reader_lock_item_clicked, App); + + VariableItem* Item = variable_item_list_add( + App->VariableItemListLock, + App->SettingLockBankConfigLabel, + COUNT_OF(App->SettingLockBankValues), + uhf_reader_setting_lock_bank_change, + App); + + //Creating the default index for setting one which is the connection status + App->SettingLockBankIndex = 0; + variable_item_set_current_value_index(Item, App->SettingLockBankIndex); + variable_item_set_current_value_text( + Item, App->SettingLockBankNames[App->SettingLockBankIndex]); + + VariableItem* ItemLock = variable_item_list_add( + App->VariableItemListLock, + App->SettingLockActionConfigLabel, + COUNT_OF(App->SettingLockActionValues), + uhf_reader_setting_lock_action_change, + App); + + //Creating the default index for setting one which is the connection status + App->SettingLockActionIndex = 0; + variable_item_set_current_value_index(ItemLock, App->SettingLockActionIndex); + variable_item_set_current_value_text( + ItemLock, App->SettingLockActionNames[App->SettingLockActionIndex]); + + //The execute button and result + App->DefaultLockResultStr = furi_string_alloc_set(App->SettingLockExecuteResult); + App->SettingLockResultItem = variable_item_list_add( + App->VariableItemListLock, App->SettingLockExecuteConfigLabel, 1, NULL, NULL); + variable_item_set_current_value_text( + App->SettingLockResultItem, furi_string_get_cstr(App->DefaultLockResultStr)); + variable_item_list_set_enter_callback( + App->VariableItemListLock, uhf_reader_lock_item_clicked, App); + view_set_previous_callback( + variable_item_list_get_view(App->VariableItemListLock), + uhf_reader_navigation_lock_exit_callback); + view_dispatcher_add_view( + App->ViewDispatcher, + UHFReaderViewLock, + variable_item_list_get_view(App->VariableItemListLock)); +} + +/** + * @brief Frees the tag action view. + * @details This function frees all variables for the tag actions view. + * @param context The context - UHFReaderApp object. +*/ +void view_lock_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewLockPopup); + popup_free(App->LockPopup); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSetAccessPwd); + byte_input_free(App->SetApInput); + free(App->SetPwdTempBuffer); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewLock); + variable_item_list_free(App->VariableItemListLock); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_lock.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_lock.h new file mode 100644 index 00000000..d70bf768 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_lock.h @@ -0,0 +1,13 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void uhf_reader_submenu_lock_callback(void* context, uint32_t index); + +uint32_t uhf_reader_navigation_lock_callback(void* context); + +uint32_t uhf_reader_navigation_lock_exit_callback(void* context); + +void view_lock_alloc(UHFReaderApp* App); + +void view_lock_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_read.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_read.c new file mode 100644 index 00000000..97c93eab --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_read.c @@ -0,0 +1,707 @@ +#include "view_read.h" +#include +#include +#include + +/** + * @brief Converts hex bytearray to string + * @details This function is used to convert a bytearray to a char* for use with views. + * @param array The bytearray -Byte array of hexadecimal values + * @param length The length of the array - size_t length value for the byte array + * @return char* - the string representation of the hexadecimal array. +*/ +char* convert_to_hex_string(uint8_t* array, size_t length) { + if(array == NULL || length == 0) { + return " "; + } + FuriString* temp_str = furi_string_alloc(); + + for(size_t i = 0; i < length; i++) { + furi_string_cat_printf(temp_str, "%02X ", array[i]); + } + const char* furi_str = furi_string_get_cstr(temp_str); + + size_t str_len = strlen(furi_str); + char* str = (char*)malloc(sizeof(char) * str_len); + + memcpy(str, furi_str, str_len); + furi_string_free(temp_str); + return str; +} + +/** + * @brief Read Draw Callback. + * @details This function is called when the user selects read on the main submenu. + * @param canvas The canvas - Canvas object for drawing the screen. + * @param model The view model - model for the view with variables required for drawing. +*/ +void uhf_reader_view_read_draw_callback(Canvas* canvas, void* model) { + UHFReaderConfigModel* MyModel = (UHFReaderConfigModel*)model; + FuriString* XStr = furi_string_alloc(); + + //Clearing the canvas, setting the color, font and content displayed. + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, " Read Menu:"); + canvas_set_font(canvas, FontSecondary); + + //Displaying the current number of UHF Tags read + furi_string_printf(XStr, "%ld", MyModel->NumEpcsRead); + canvas_draw_str(canvas, 4, 33, "# EPCs:"); + canvas_draw_str(canvas, 45, 33, furi_string_get_cstr(XStr)); + + //Displaying the index of the current tag being viewed + furi_string_printf(XStr, "%ld", MyModel->CurEpcIndex); + canvas_draw_str(canvas, 70, 33, "Cur Tag:"); + canvas_draw_str(canvas, 115, 33, furi_string_get_cstr(XStr)); + + //Displaying the CRC + canvas_draw_str(canvas, 4, 22, "CRC: "); + canvas_draw_str(canvas, 28, 22, furi_string_get_cstr(MyModel->Crc)); + + //Displaying the PC + canvas_draw_str(canvas, 70, 22, "PC:"); + canvas_draw_str(canvas, 90, 22, furi_string_get_cstr(MyModel->Pc)); + + //Displaying the EPC in a scrolling fashion + MyModel->ScrollingText = (char*)furi_string_get_cstr(MyModel->EpcValue); + + //Setting the width of the screen for the sliding window + uint32_t ScreenWidthChars = 24; + + // Calculate the start and end indices of the substring to draw + uint32_t StartPos = MyModel->ScrollOffset; + + //Calculate the length of the scrolling text + uint32_t Len = strlen(MyModel->ScrollingText); + + //I am sure there is a better way to do this that involves slightly safer memory management... + char VisiblePart[ScreenWidthChars + 2]; + memset(VisiblePart, ' ', ScreenWidthChars); + VisiblePart[ScreenWidthChars] = '\0'; + + //Fill the array up with the epc values + for(uint32_t i = 0; i < ScreenWidthChars; i++) { + uint32_t CharIndex = StartPos + i; + if(CharIndex <= Len) { + VisiblePart[i] = MyModel->ScrollingText[CharIndex]; + } + } + + // Now draw the visible part of the string + canvas_draw_str(canvas, 0, 44, VisiblePart); + + if(!MyModel->IsReading) { + //Display the Prev and Next buttons if the app isn't reading + elements_button_left(canvas, "Prev"); + elements_button_center(canvas, "Start"); + elements_button_right(canvas, "Next"); + } else { + elements_button_center(canvas, "Stop"); + } + furi_string_free(XStr); +} + +/** + * @brief Callback when the user exits the read screen. + * @details This function is called when the user exits the read screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_read_callback(void* context) { + UNUSED(context); + return UHFReaderViewRead; +} + +/** + * @brief Callback for the timer used for the read screen + * @details This function is called for timer events. + * @param context The UHFReaderApp - Used to change app variables. +*/ +void uhf_reader_view_read_timer_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Update the offset for the epc in the read draw callback + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { + uint32_t Len = strlen(model->ScrollingText); + + //Incrementing each offset + model->ScrollOffset++; + + //Check the bounds of the offset and reset if necessary + if(model->ScrollOffset >= Len) { + model->ScrollOffset = 0; + } + }, + true); + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFReaderEventIdRedrawScreen); +} + +/** + * @brief Callback for the saved text input screen. + * @details This function saves the current tag selected with all its info + * @param context The UHFReaderApp - Used to change app variables. +*/ +void uhf_reader_save_text_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool Redraw = true; + + //Allocating FuriStrings to store each of the values associated with each UHF Tag + FuriString* Tid = furi_string_alloc(); + FuriString* Mem = furi_string_alloc(); + FuriString* Res = furi_string_alloc(); + FuriString* Pc = furi_string_alloc(); + FuriString* Crc = furi_string_alloc(); + + //Set the current EPC to save for the app based on the model's value + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { + furi_string_set(model->EpcName, App->TempSaveBuffer); + model->EpcName = App->EpcName; + App->EpcToSave = (char*)furi_string_get_cstr(model->EpcValue); + }, + Redraw); + + //Set the tid, User and Reserved memory values if any tags were read + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * model, + { + if(App->NumberOfEpcsToRead > 0) { + furi_string_set(Tid, model->Tid); + furi_string_set(Mem, model->User); + furi_string_set(Res, model->Reserved); + furi_string_set(Pc, model->Pc); + furi_string_set(Crc, model->Crc); + } else { + furi_string_free(Tid); + furi_string_free(Mem); + furi_string_free(Res); + furi_string_free(Pc); + furi_string_free(Crc); + return; + } + }, + Redraw); + + //Open the saved EPCS file if tags were read + if(!flipper_format_file_open_append(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } + + //Allocating some FuriStrings for use with the file + FuriString* NumEpcs = furi_string_alloc(); + FuriString* EpcAndName = furi_string_alloc(); + + //Increment the total number of saved tags and write this new tag with all its values to the epc_and_name FuriString + App->NumberOfSavedTags++; + furi_string_printf(NumEpcs, "Tag%ld", App->NumberOfSavedTags); + furi_string_printf( + EpcAndName, + "%s:%s:%s:%s:%s:%s:%s", + App->TempSaveBuffer, + App->EpcToSave, + furi_string_get_cstr(Tid), + furi_string_get_cstr(Res), + furi_string_get_cstr(Mem), + furi_string_get_cstr(Pc), + furi_string_get_cstr(Crc)); + + //Attempt to write the string using the given format + if(!flipper_format_write_string_cstr( + App->EpcFile, furi_string_get_cstr(NumEpcs), furi_string_get_cstr(EpcAndName))) { + FURI_LOG_E(TAG, "Failed to write to file"); + flipper_format_file_close(App->EpcFile); + } else { + //Add the new tag to the saved submenu + submenu_add_item( + App->SubmenuSaved, + App->TempSaveBuffer, + App->NumberOfSavedTags, + uhf_reader_submenu_saved_callback, + App); + flipper_format_file_close(App->EpcFile); + + //Update the Index_File to have the new total number of saved tags and write to the file + FuriString* NewNumEpcs = furi_string_alloc(); + furi_string_printf(NewNumEpcs, "%ld", App->NumberOfSavedTags); + if(!flipper_format_file_open_existing(App->EpcIndexFile, APP_DATA_PATH("Index_File.txt"))) { + FURI_LOG_E(TAG, "Failed to open index file"); + } else { + if(!flipper_format_write_string_cstr( + App->EpcIndexFile, "Number of Tags", furi_string_get_cstr(NewNumEpcs))) { + FURI_LOG_E(TAG, "Failed to write to file"); + flipper_format_file_close(App->EpcIndexFile); + } else { + flipper_format_file_close(App->EpcIndexFile); + } + } + furi_string_free(NewNumEpcs); + } + + //Freeing all the FuriStrings used + furi_string_free(EpcAndName); + furi_string_free(NumEpcs); + furi_string_free(Res); + furi_string_free(Tid); + furi_string_free(Mem); + furi_string_free(Pc); + furi_string_free(Crc); + dolphin_deed(DolphinDeedRfidSave); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewRead); +} + +/** + * @brief Callback for read screen input. + * @details This function is called when the user presses a button while on the read screen. + * @param event The event - InputEvent object. + * @param context The context - UHFReaderApp object. + * @return true if the event was handled, false otherwise. +*/ +bool uhf_reader_view_read_input_callback(InputEvent* event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Handles all short input types + if(event->type == InputTypeShort) { + //If the user presses the left button while the app is not reading + //TODO CHANGE THIS LOGIC WHEN ADD SUPPORT FOR MULTIPLE TAGS YRM100 + if(event->key == InputKeyLeft && !App->IsReading && App->UHFModuleType != YRM100X_MODULE) { + bool Redraw = true; + + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { + //Check if there are any epcs that were read and decrement the current epc index to show the previous epc + if(App->NumberOfEpcsToRead > 1 && model->CurEpcIndex > 1) { + dolphin_deed(DolphinDeedNfcRead); + //TODO: Add checks for number of tids, res, and user mem read in case of short reads + model->CurEpcIndex -= 1; + App->CurTidIndex -= 1; + App->CurResIndex -= 1; + App->CurMemIndex -= 1; + furi_string_set_str( + model->EpcValue, App->EpcValues[model->CurEpcIndex * 26]); + model->NumEpcsRead = App->NumberOfEpcsToRead; + } + }, + Redraw); + + //Updating the current TID, EPC, Reserved and User memory values to display on the view epc screen + //TODO Add logic for PC and CRC values for the M6E/M7E readers + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * model, + { + if(App->NumberOfEpcsToRead > 0) { + furi_string_set_str(model->Epc, App->EpcValues[App->CurTidIndex * 26]); + } + if(App->NumberOfTidsToRead > 0) { + furi_string_set_str(model->Tid, App->TidValues[App->CurTidIndex * 41]); + } + if(App->NumberOfResToRead > 0) { + furi_string_set_str( + model->Reserved, App->ResValues[App->CurResIndex * 17]); + } + if(App->NumberOfMemToRead > 0) { + furi_string_set_str(model->User, App->MemValues[App->CurMemIndex * 33]); + } + }, + Redraw); + + return true; + } + //Increment the current epc index if the app is not reading + else if( + event->key == InputKeyRight && !App->IsReading && + App->UHFModuleType != YRM100X_MODULE) { + bool Redraw = true; + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { + if(App->NumberOfEpcsToRead > 1 && + model->CurEpcIndex < App->NumberOfEpcsToRead) { + //TODO: Add better bounds checking to ensure no crashes with short reads + model->CurEpcIndex += 1; + App->CurTidIndex += 1; + App->CurResIndex += 1; + App->CurMemIndex += 1; + furi_string_set_str( + model->EpcValue, App->EpcValues[model->CurEpcIndex * 26]); + model->NumEpcsRead = App->NumberOfEpcsToRead; + } + }, + Redraw); + + //Updating the current TID, EPC, Reserved and User memory values to display on the view epc screen + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * model, + { + if(App->NumberOfEpcsToRead > 0) { + furi_string_set_str(model->Epc, App->EpcValues[App->CurTidIndex * 26]); + } + if(App->NumberOfTidsToRead > 0) { + furi_string_set_str(model->Tid, App->TidValues[App->CurTidIndex * 41]); + } + if(App->NumberOfResToRead > 0) { + furi_string_set_str( + model->Reserved, App->ResValues[App->CurResIndex * 17]); + } + if(App->NumberOfMemToRead > 0) { + furi_string_set_str(model->User, App->MemValues[App->CurMemIndex * 33]); + } + }, + Redraw); + + return true; + } + + //Handles the up key press that allows the user to save the currently selected UHF Tag + else if(event->key == InputKeyUp && !App->IsReading) { + //Setting the text input header + text_input_set_header_text(App->SaveInput, "Save EPC"); + bool Redraw = false; + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { + //Copy the name contents from the text input + strncpy( + App->TempSaveBuffer, + furi_string_get_cstr(model->EpcName), + App->TempBufferSaveSize); + }, + Redraw); + + //Set the text input result callback function + bool ClearPreviousText = false; + text_input_set_result_callback( + App->SaveInput, + uhf_reader_save_text_updated, + App, + App->TempSaveBuffer, + App->TempBufferSaveSize, + ClearPreviousText); + view_set_previous_callback( + text_input_get_view(App->SaveInput), uhf_reader_navigation_read_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSaveInput); + + return true; + } + //If the down button is pressed, then show the view epc screen + else if(event->key == InputKeyDown && !App->IsReading) { + view_set_previous_callback(App->ViewEpc, uhf_reader_navigation_read_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewEpcDump); + return true; + } + } + //Handles the ok button being pressed + else if(event->type == InputTypePress) { + if(event->key == InputKeyOk) { + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFReaderEventIdOkPressed); + + return true; + } + } + + return false; +} + +/** + * @brief Callback when the user exits the read screen. + * @details This function is called when the user exits the read screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_read_submenu_callback(void* context) { + UNUSED(context); + return UHFReaderViewSubmenu; +} + +/** + * @brief Callback when the user exits the read screen. + * @details This function is called when the user exits the read screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_read_exit_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + if(App->UHFModuleType == YRM100X_MODULE) { + uhf_worker_stop(App->YRM100XWorker); + } + + furi_timer_stop(App->Timer); + furi_timer_free(App->Timer); + App->Timer = NULL; +} + +/** + * @brief Read enter callback function. + * @details This function is called when the view transitions to the read screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_read_enter_callback(void* context) { + //Grab the period for the timer + uint32_t Period = furi_ms_to_ticks(300); + UHFReaderApp* App = (UHFReaderApp*)context; + dolphin_deed(DolphinDeedNfcRead); + + //Start the timer + furi_assert(App->Timer == NULL); + App->Timer = + furi_timer_alloc(uhf_reader_view_read_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(App->Timer, Period); + + //Setting default reading states + App->IsReading = false; + with_view_model( + App->ViewRead, UHFReaderConfigModel * model, { model->IsReading = false; }, true); +} + +/** + * @brief Callback for the YRM100 worker + * @details This function is called when there is a worker event + * @param event The worker event - UHFWorkerEvent event. + * @param ctx The context - UHFReaderApp object. +*/ +void uhf_read_tag_worker_callback(UHFWorkerEvent event, void* ctx) { + UHFReaderApp* App = (UHFReaderApp*)ctx; + + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { + uint32_t Len = strlen(model->ScrollingText); + + //Incrementing each offset + model->ScrollOffset++; + + //Check the bounds of the offset and reset if necessary + if(model->ScrollOffset >= Len) { + model->ScrollOffset = 0; + } + }, + true); + + if(event == UHFWorkerEventSuccess) { + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_success); + dolphin_deed(DolphinDeedNfcReadSuccess); + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFCustomEventWorkerExit); + } +} + +/** + * @brief Callback for custom read events. + * @details This function is called when a custom event is sent to the view dispatcher. + * @param event The event id - UHFReaderAppEventId value. + * @param context The context - UHFReaderApp object. + * @return true if the event was handled, false otherwise. +*/ +bool uhf_reader_view_read_custom_event_callback(uint32_t event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + switch(event) { + //Redraw the screen + case UHFReaderEventIdRedrawScreen: { + bool Redraw = true; + with_view_model(App->ViewRead, UHFReaderConfigModel * _model, { UNUSED(_model); }, Redraw); + return true; + } + + //Handles the worker exiting after a read + case UHFCustomEventWorkerExit: { + bool Redraw = true; + App->IsReading = false; + + //Stop the worker + uhf_worker_stop(App->YRM100XWorker); + + //Get the tag read + UHFTag* TestTag = App->YRM100XWorker->uhf_tag_wrapper->uhf_tag; + + //Parse all of the memory banks + char* TempEpc = convertToHexString(TestTag->epc->data, TestTag->epc->size); + char* TempTid = convertToHexString(TestTag->tid->data, TestTag->tid->size); + char* TempPass = convertToHexString(TestTag->reserved->access_password, 4); + char* TempKill = convertToHexString(TestTag->reserved->kill_password, 4); + char* TempRes = combineArrays(TempKill, TempPass); + char* TempUser = convertToHexString(TestTag->user->data, TestTag->user->size); + char* TempCrc = uint16_to_hex_string(TestTag->epc->crc); + char* TempPc = uint16_to_hex_string(TestTag->epc->pc); + + + //If any of the banks returned empty, create visual placeholders to indicate to the user that something failed to read. + if(strcmp(TempUser, " ") == 0 || TempUser == NULL) { + TempUser = "XXXXXXXX"; + } + if(strcmp(TempTid, " ") == 0 || TempTid == NULL) { + TempTid = "XXXXXXXX"; + } + if(strcmp(TempRes, " ") == 0 || TempRes == NULL) { + TempRes = "XXXXXXXX"; + } + App->CurEpcIndex = 26; + App->NumberOfEpcsToRead = 1; + + with_view_model( + App->ViewRead, + UHFReaderConfigModel * _model, + { + furi_string_set_str(_model->EpcValue, TempEpc); + _model->IsReading = App->IsReading; + _model->NumEpcsRead = App->NumberOfEpcsToRead; + _model->CurEpcIndex = 1; + furi_string_set_str(_model->Crc, TempCrc); + furi_string_set_str(_model->Pc, TempPc); + }, + Redraw); + with_view_model( + App->ViewEpc, + UHFRFIDTagModel * _model, + { + furi_string_set_str(_model->Epc, TempEpc); + furi_string_set_str(_model->Tid, TempTid); + furi_string_set_str(_model->User, TempUser); + furi_string_set_str(_model->Reserved, TempRes); + furi_string_set_str(_model->Crc, TempCrc); + furi_string_set_str(_model->Pc, TempPc); + }, + Redraw); + free(TempEpc); + free(TempTid); + free(TempUser); + free(TempCrc); + free(TempPc); + free(TempRes); + free(TempKill); + free(TempPass); + return true; + } + + //The ok button was pressed + case UHFReaderEventIdOkPressed: + + //Check if the app is reading + if(App->IsReading) { + //Stop reading + App->IsReading = false; + notification_message(App->Notifications, &uhf_sequence_blink_stop); + uhf_worker_stop(App->YRM100XWorker); + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { model->IsReading = App->IsReading; }, + true); + } else { + //Check if the reader is connected before sending a read command + if(App->ReaderConnected) { + App->IsReading = true; + notification_message(App->Notifications, &uhf_sequence_blink_start_cyan); + if(App->UHFModuleType == M6E_NANO_MODULE || + App->UHFModuleType == M7E_HECTO_MODULE) { + //Send the read command to the RPi Zero via UART + uart_helper_send(App->UartHelper, "READ\n", 5); + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { model->IsReading = App->IsReading; }, + true); + } else { + + with_view_model( + App->ViewRead, + UHFReaderConfigModel * model, + { model->IsReading = App->IsReading; }, + true); + uhf_worker_start( + App->YRM100XWorker, + UHFWorkerStateDetectSingle, + uhf_read_tag_worker_callback, + App); + } + } + } + return true; + default: + return false; + } +} + +/** + * @brief Allocates the saved input view. + * @details This function allocates all variables for the saved input view. + * @param context The context - UHFReaderApp object. +*/ +void saved_input_alloc(UHFReaderApp* App) { + //Allocate a new text input component + App->SaveInput = text_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSaveInput, text_input_get_view(App->SaveInput)); + + //Setting the max size of the buffer + App->TempBufferSaveSize = 150; + App->TempSaveBuffer = (char*)malloc(App->TempBufferSaveSize); +} + +/** + * @brief Allocates the read view. + * @details This function allocates all variables for the read view. + * @param context The context - UHFReaderApp object. +*/ +void view_read_alloc(UHFReaderApp* App) { + //Allocating the view and setting all callback functions + saved_input_alloc(App); + App->ViewRead = view_alloc(); + view_set_draw_callback(App->ViewRead, uhf_reader_view_read_draw_callback); + view_set_input_callback(App->ViewRead, uhf_reader_view_read_input_callback); + view_set_previous_callback(App->ViewRead, uhf_reader_navigation_read_submenu_callback); + view_set_enter_callback(App->ViewRead, uhf_reader_view_read_enter_callback); + view_set_exit_callback(App->ViewRead, uhf_reader_view_read_exit_callback); + view_set_context(App->ViewRead, App); + view_set_custom_callback(App->ViewRead, uhf_reader_view_read_custom_event_callback); + + //Allocating the view model + view_allocate_model(App->ViewRead, ViewModelTypeLockFree, sizeof(UHFReaderConfigModel)); + UHFReaderConfigModel* Model = view_get_model(App->ViewRead); + FuriString* EpcValueDefault = furi_string_alloc(); + furi_string_set_str(EpcValueDefault, "Press Read"); + + //Setting default values for the view model + Model->Setting1Index = App->Setting1Index; + Model->Setting2Power = App->Setting2PowerStr; + Model->SettingReadAp = App->DefaultAccessPwdStr; + Model->Setting3Index = App->Setting3Index; + Model->Setting1Value = furi_string_alloc_set(App->Setting1Names[App->Setting1Index]); + Model->Setting3Value = furi_string_alloc_set(App->Setting3Names[App->Setting3Index]); + Model->Pc = furi_string_alloc_set("XXXX"); + Model->Crc = furi_string_alloc_set("XXXX"); + Model->EpcName = furi_string_alloc_set("Enter name"); + Model->ScrollOffset = 0; + Model->ScrollingText = "Press Read"; + Model->EpcValue = EpcValueDefault; + Model->CurEpcIndex = 1; + Model->NumEpcsRead = 0; + view_dispatcher_add_view(App->ViewDispatcher, UHFReaderViewRead, App->ViewRead); +} + +/** + * @brief Frees the read view. + * @details This function frees all variables for the read view. + * @param context The context - UHFReaderApp object. +*/ +void view_read_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSaveInput); + text_input_free(App->SaveInput); + free(App->TempSaveBuffer); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewRead); + view_free(App->ViewRead); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_read.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_read.h new file mode 100644 index 00000000..54d5e89c --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_read.h @@ -0,0 +1,29 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void uhf_reader_view_read_draw_callback(Canvas* canvas, void* model); + +char* convert_to_hex_string(uint8_t* array, size_t length); + +bool uhf_reader_view_read_input_callback(InputEvent* event, void* context); + +uint32_t uhf_reader_navigation_read_submenu_callback(void* context); + +uint32_t uhf_reader_navigation_read_callback(void* context); + +void uhf_reader_view_read_enter_callback(void* context); + +void uhf_reader_view_read_exit_callback(void* context); + +bool uhf_reader_view_read_custom_event_callback(uint32_t event, void* context); + +void uhf_reader_view_read_timer_callback(void* context); + +void uhf_reader_save_text_updated(void* context); + +void view_read_alloc(UHFReaderApp* App); + +void view_read_free(UHFReaderApp* App); + +void saved_input_alloc(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_saved.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_saved.c new file mode 100644 index 00000000..5025c3da --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_saved.c @@ -0,0 +1,123 @@ +#include "view_saved.h" + +/** + * @brief Callback when the user exits the saved screen. + * @details This function is called when the user exits the saved screen. + * @param _context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_saved_exit_callback(void* context) { + UNUSED(context); + return UHFReaderViewSubmenu; +} + +/** + * @brief Callback for the saved tag selected + * @details This function is called when the user selects a saved tag from the submenu + * @param context The context - UHFReaderApp object + * @param index The index of the selected saved tag +*/ +void uhf_reader_submenu_saved_callback(void* context, uint32_t index) { + UHFReaderApp* App = (UHFReaderApp*)context; + switch(index) { + case UHFReaderSubmenuIndexTagAction: + break; + default: + App->SelectedTagIndex = index; + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewTagAction); + break; + } +} + +/** + * @brief Allocates the Saved menu menu + * @details This function allocates all variables for the saved menu + * @param context The context - UHFReaderApp object. +*/ +void view_saved_menu_alloc(UHFReaderApp* App){ + + //Allocate the saved submenu and a FuriString to store the number of saved tags + App->SubmenuSaved = submenu_alloc(); + submenu_set_header(App->SubmenuSaved, "Saved EPCs"); + FuriString* ExtractedNumTagsStr = furi_string_alloc(); + + //Try to open the Index_File or create a new one if it doesn't exist + if(!flipper_format_file_open_existing(App->EpcIndexFile, APP_DATA_PATH("Index_File.txt"))) { + FURI_LOG_E(TAG, "Creating new index file"); + flipper_format_file_close(App->EpcIndexFile); + if(!flipper_format_file_open_new(App->EpcIndexFile, APP_DATA_PATH("Index_File.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } else { + + //Sets the default format of the index file if none is present + if(!flipper_format_write_string_cstr(App->EpcIndexFile, "Number of Tags", "0")) { + FURI_LOG_E(TAG, "Failed to write to file"); + } else { + App->NumberOfSavedTags = 0; + } + } + } else { + //Extract the number of saved tags and set the app variable accordingly + if(!flipper_format_read_string( + App->EpcIndexFile, "Number of Tags", ExtractedNumTagsStr)) { + } else { + App->NumberOfSavedTags = + (uint32_t)atoi(furi_string_get_cstr(ExtractedNumTagsStr)); + } + } + + //Close the index file + flipper_format_file_close(App->EpcIndexFile); + + //Open the saved EPCs file + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open Saved file"); + flipper_format_file_close(App->EpcFile); + + } else { + + //Look through each index and extract the tag name + for(uint32_t i = 0; i < (uint32_t)App->NumberOfSavedTags; i++) { + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + furi_string_printf(TempStr, "Tag%ld", i + 1); + if(!flipper_format_read_string( + App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", i + 1); + } else { + + //Extract the name of the saved epc for this index + const char* InputString = furi_string_get_cstr(TempTag); + char* ExtractedName = extract_name(InputString); + + //Create a new submenu item if the name isn't NULL + if(ExtractedName != NULL) { + submenu_add_item( + App->SubmenuSaved, + ExtractedName, + (i + 1), + uhf_reader_submenu_saved_callback, + App); + free(ExtractedName); + } + } + furi_string_free(TempStr); + furi_string_free(TempTag); + } + flipper_format_file_close(App->EpcFile); + } + view_set_previous_callback( + submenu_get_view(App->SubmenuSaved), uhf_reader_navigation_saved_exit_callback); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSaved, submenu_get_view(App->SubmenuSaved)); +} + +/** + * @brief Frees the saved view. + * @details This function frees all variables for the saved view. + * @param context The context - UHFReaderApp object. +*/ +void view_saved_free(UHFReaderApp* App){ + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSaved); + submenu_free(App->SubmenuSaved); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_saved.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_saved.h new file mode 100644 index 00000000..505411ad --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_saved.h @@ -0,0 +1,11 @@ +#pragma once +#include "../app.h" + +//Function Declarations +uint32_t uhf_reader_navigation_saved_exit_callback(void* context); + +void uhf_reader_submenu_saved_callback(void* context, uint32_t index); + +void view_saved_menu_alloc(UHFReaderApp* App); + +void view_saved_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_tag_actions.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_tag_actions.c new file mode 100644 index 00000000..3ea6dcf4 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_tag_actions.c @@ -0,0 +1,319 @@ +#include "view_tag_actions.h" + +/** + * @brief Callback when the user exits the epc info screen. + * @details This function is called when the user exits the epc info screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_epc_info_callback(void* context) { + UNUSED(context); + return UHFReaderViewTagAction; +} + +/** + * @brief Callback for the rename text input screen. + * @details This function handles the renaming logic for saved UHF tags. + * @param context The UHFReaderApp - Used to change app variables. +*/ +void uhf_reader_rename_text_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Allocating FuriStrings to store each of the values associated with each UHF Tag + FuriString* TempTid = furi_string_alloc(); + FuriString* TempRes = furi_string_alloc(); + FuriString* TempMem = furi_string_alloc(); + FuriString* TempCrc = furi_string_alloc(); + FuriString* TempPc = furi_string_alloc(); + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + FuriString* TempEpcStr = furi_string_alloc(); + + //Open the saved EPCs file + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open Saved file"); + flipper_format_file_close(App->EpcFile); + + } else { + furi_string_printf(TempStr, "Tag%ld", App->SelectedTagIndex); + + //Get the current tag data for the tag being renamed + if(!flipper_format_read_string(App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", App->SelectedTagIndex); + } else { + //Get all the fields for the current tag being renamed + const char* InputString = furi_string_get_cstr(TempTag); + char* ExtractedEpc = extract_epc(InputString); + char* ExtractedTid = extract_tid(InputString); + char* ExtractedRes = extract_res(InputString); + char* ExtractedMem = extract_mem(InputString); + char* ExtractedCrc = extract_crc(InputString); + char* ExtractedPc = extract_pc(InputString); + furi_string_set_str(TempEpcStr, ExtractedEpc); + furi_string_set_str(TempTid, ExtractedTid); + furi_string_set_str(TempRes, ExtractedRes); + furi_string_set_str(TempMem, ExtractedMem); + furi_string_set_str(TempCrc, ExtractedCrc); + furi_string_set_str(TempPc, ExtractedPc); + flipper_format_file_close(App->EpcFile); + + + free(ExtractedEpc); + } + } + + //Open the file again + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } + + //Allocate new FuriStrings for storing the newly named UHF tag + FuriString* NumEpcs = furi_string_alloc(); + FuriString* EpcAndName = furi_string_alloc(); + furi_string_printf(NumEpcs, "Tag%ld", App->SelectedTagIndex); + furi_string_printf( + EpcAndName, + "%s:%s:%s:%s:%s:%s:%s", + App->TempSaveBuffer, + furi_string_get_cstr(TempEpcStr), + furi_string_get_cstr(TempTid), + furi_string_get_cstr(TempRes), + furi_string_get_cstr(TempMem), + furi_string_get_cstr(TempPc), + furi_string_get_cstr(TempCrc)); + + //Write to the file and update the current string in its index + if(!flipper_format_update_string_cstr( + App->EpcFile, furi_string_get_cstr(NumEpcs), furi_string_get_cstr(EpcAndName))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } + + //Close the file + flipper_format_file_close(App->EpcFile); + + //Free all FuriStrings used + furi_string_free(EpcAndName); + furi_string_free(NumEpcs); + furi_string_free(TempEpcStr); + furi_string_free(TempTid); + furi_string_free(TempRes); + furi_string_free(TempMem); + furi_string_free(TempCrc); + furi_string_free(TempPc); + furi_string_free(TempStr); + furi_string_free(TempTag); + + //Remove the current saved submenu to reallocate and update it + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewSaved); + submenu_free(App->SubmenuSaved); + App->SubmenuSaved = submenu_alloc(); + submenu_set_header(App->SubmenuSaved, "Saved EPCs"); + + //Update the index file or create a new one doesn't exist + FuriString* ExtractedNumTagsStr = furi_string_alloc(); + if(!flipper_format_file_open_existing(App->EpcIndexFile, APP_DATA_PATH("Index_File.txt"))) { + FURI_LOG_E(TAG, "Creating new index file"); + flipper_format_file_close(App->EpcIndexFile); + if(!flipper_format_file_open_new(App->EpcIndexFile, APP_DATA_PATH("Index_File.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } else { + if(!flipper_format_write_string_cstr(App->EpcIndexFile, "Number of Tags", "0")) { + FURI_LOG_E(TAG, "Failed to write to file"); + } else { + App->NumberOfSavedTags = 0; + } + } + } else { + if(!flipper_format_read_string(App->EpcIndexFile, "Number of Tags", ExtractedNumTagsStr)) { + } else { + App->NumberOfSavedTags = (uint32_t)atoi(furi_string_get_cstr(ExtractedNumTagsStr)); + } + } + furi_string_free(ExtractedNumTagsStr); + flipper_format_file_close(App->EpcIndexFile); + + //Open the saved epcs file to iterate through and create new submenu items + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open Saved file"); + flipper_format_file_close(App->EpcFile); + + } else { + for(uint32_t i = 0; i < (uint32_t)App->NumberOfSavedTags; i++) { + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + furi_string_printf(TempStr, "Tag%ld", i + 1); + if(!flipper_format_read_string(App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", i + 1); + } else { + const char* InputString = furi_string_get_cstr(TempTag); + char* ExtractedName = extract_name(InputString); + + //Update the saved submenu + if(ExtractedName != NULL) { + submenu_add_item( + App->SubmenuSaved, + ExtractedName, + (i + 1), + uhf_reader_submenu_saved_callback, + App); + free(ExtractedName); + } + } + furi_string_free(TempStr); + furi_string_free(TempTag); + } + flipper_format_file_close(App->EpcFile); + } + + view_set_previous_callback( + submenu_get_view(App->SubmenuSaved), uhf_reader_navigation_saved_exit_callback); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewSaved, submenu_get_view(App->SubmenuSaved)); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewSaved); +} + +/** + * @brief Tag Info Submenu Callback + * @details Handles the different submenu options for the tag actions menu. + * @param context The UHFReaderApp - the app for working with variables + * @param index The selected submenu index +*/ +void uhf_reader_submenu_tag_info_callback(void* context, uint32_t index) { + UHFReaderApp* App = (UHFReaderApp*)context; + + switch(index) { + //Handles the Tag Info menu + case UHFReaderSubmenuIndexTagInfo: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewEpcInfo); + break; + + //Handles the rename screen + case UHFReaderSubmenuIndexTagRename: + text_input_set_header_text(App->RenameInput, "Rename EPC"); + + //Grab the text from the rename text input + bool Redraw = false; + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + strncpy( + App->TempSaveBuffer, + furi_string_get_cstr(Model->EpcName), + App->TempBufferSaveSize); + }, + Redraw); + + // Configure the text input. When user enters text and clicks OK then the rename text updated callback will run + bool ClearPreviousText = true; + text_input_set_result_callback( + App->RenameInput, + uhf_reader_rename_text_updated, + App, + App->TempSaveBuffer, + App->TempBufferSaveSize, + ClearPreviousText); + + // Pressing the BACK button will reload the configure screen. + view_set_previous_callback( + text_input_get_view(App->RenameInput), uhf_reader_navigation_epc_info_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewRenameInput); + break; + + //Handles the write screen + case UHFReaderSubmenuIndexTagWrite: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewWrite); + break; + case UHFReaderSubmenuIndexTagLock: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewLock); + break; + case UHFReaderSubmenuIndexTagKill: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewKill); + break; + //Handles the delete screen + case UHFReaderSubmenuIndexTagDelete: + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewDelete); + break; + default: + break; + } +} + +/** + * @brief Callback when the user exits the tag action screen. + * @details This function is called when the user exits the tag action screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_tag_action_exit_callback(void* context) { + UNUSED(context); + return UHFReaderViewSaved; +} + +/** + * @brief Allocates the tag actions view. + * @details This function allocates all variables for the tag actions view. + * @param app The UHFReaderApp object. +*/ +void view_tag_actions_alloc(UHFReaderApp* App) { + //Allocate the tag actions submenu + App->SubmenuTagActions = submenu_alloc(); + submenu_set_header(App->SubmenuTagActions, "EPC Actions"); + submenu_add_item( + App->SubmenuTagActions, + "Tag Data", + UHFReaderSubmenuIndexTagInfo, + uhf_reader_submenu_tag_info_callback, + App); + submenu_add_item( + App->SubmenuTagActions, + "Rename", + UHFReaderSubmenuIndexTagRename, + uhf_reader_submenu_tag_info_callback, + App); + submenu_add_item( + App->SubmenuTagActions, + "Write", + UHFReaderSubmenuIndexTagWrite, + uhf_reader_submenu_tag_info_callback, + App); + submenu_add_item( + App->SubmenuTagActions, + "Lock", + UHFReaderSubmenuIndexTagLock, + uhf_reader_submenu_tag_info_callback, + App); + submenu_add_item( + App->SubmenuTagActions, + "Kill", + UHFReaderSubmenuIndexTagKill, + uhf_reader_submenu_tag_info_callback, + App); + submenu_add_item( + App->SubmenuTagActions, + "Delete", + UHFReaderSubmenuIndexTagDelete, + uhf_reader_submenu_tag_info_callback, + App); + view_set_previous_callback( + submenu_get_view(App->SubmenuTagActions), uhf_reader_navigation_tag_action_exit_callback); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewTagAction, submenu_get_view(App->SubmenuTagActions)); + + //Allocate the rename text input + App->RenameInput = text_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewRenameInput, text_input_get_view(App->RenameInput)); +} + +/** + * @brief Frees the tag action view. + * @details This function frees all variables for the tag actions view. + * @param context The context - UHFReaderApp object. +*/ +void view_tag_actions_free(UHFReaderApp* App) { + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewRenameInput); + text_input_free(App->RenameInput); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewTagAction); + submenu_free(App->SubmenuTagActions); +} diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_tag_actions.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_tag_actions.h new file mode 100644 index 00000000..79410163 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_tag_actions.h @@ -0,0 +1,15 @@ +#pragma once +#include "../app.h" + +//Function Declarations +void uhf_reader_submenu_tag_info_callback(void* context, uint32_t index); + +uint32_t uhf_reader_navigation_epc_info_callback(void* context); + +void uhf_reader_rename_text_updated(void* context); + +uint32_t uhf_reader_navigation_tag_action_exit_callback(void* context); + +void view_tag_actions_alloc(UHFReaderApp* App); + +void view_tag_actions_free(UHFReaderApp* App); \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_write.c b/non_catalog_apps/simultaneous_rfid_reader/views/view_write.c new file mode 100644 index 00000000..548aa5b2 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_write.c @@ -0,0 +1,751 @@ +#include "view_write.h" + +/** + * @brief Callback for returning to write submenu screen. + * @details This function is called when user press back button. + * @param context The context - unused + * @return next view id +*/ +uint32_t uhf_reader_navigation_write_callback(void* context) { + UNUSED(context); + return UHFReaderViewWrite; +} + +/** + * @brief Callback for the epc value text input screen. + * @details This function saves the current tag selected with all its info + * @param context The UHFReaderApp - Used to change app variables. +*/ +void uhf_reader_epc_value_text_updated(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool redraw = true; + with_view_model( + App->ViewWrite, + + //Keep track of the new epc value + UHFReaderWriteModel * Model, + { + + furi_string_set_str(Model->NewEpcValue, App->TempSaveBuffer);}, + + redraw); + + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewWrite); +} + +/** + * @brief Callback for write timer elapsed. + * @details This function is called when the timer is elapsed for the write screen and is currently not used for much... + * @param context The context - The UHFReaderApp +*/ +void uhf_reader_view_write_timer_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFReaderEventIdRedrawScreen); +} + +/** + * @brief Write enter callback function. + * @details This function is called when the view transitions to the write screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_write_enter_callback(void* context) { + //Grab the period for the timer + uint32_t Period = furi_ms_to_ticks(200); + UHFReaderApp* App = (UHFReaderApp*)context; + + //Allocate space for the FuriStrings used + FuriString* TempStr = furi_string_alloc(); + FuriString* TempTag = furi_string_alloc(); + + //Open the saved epcs file to extract the uhf tag info + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open Saved file"); + flipper_format_file_close(App->EpcFile); + } else { + furi_string_printf(TempStr, "Tag%ld", App->SelectedTagIndex); + if(!flipper_format_read_string(App->EpcFile, furi_string_get_cstr(TempStr), TempTag)) { + FURI_LOG_D(TAG, "Could not read tag %ld data", App->SelectedTagIndex); + } else { + //Grab the saved uhf tag info from the saved epcs file + const char* InputString = furi_string_get_cstr(TempTag); + furi_string_set(App->EpcToWrite, extract_epc(InputString)); + furi_string_set(App->EpcName, extract_name(InputString)); + + //Set the write model uhf tag values accordingly + bool redraw = true; + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(Model->EpcValue, extract_epc(InputString)); + furi_string_set(Model->TidValue, extract_tid(InputString)); + furi_string_set(Model->ResValue, extract_res(InputString)); + furi_string_set(Model->MemValue, extract_mem(InputString)); + furi_string_set(Model->Pc, extract_pc(InputString)); + furi_string_set(Model->Crc, extract_crc(InputString)); + }, + redraw); + //Close the file + flipper_format_file_close(App->EpcFile); + } + } + + //Start the timer + furi_assert(App->Timer == NULL); + App->Timer = + furi_timer_alloc(uhf_reader_view_write_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(App->Timer, Period); + + //Setting default reading states and freeing FuriStrings used + App->IsWriting = false; + + furi_string_free(TempTag); + furi_string_free(TempStr); +} + +/** + * @brief Callback when the user exits the write screen. + * @details This function is called when the user exits the write screen. + * @param context The context - UHFReaderApp object. +*/ +void uhf_reader_view_write_exit_callback(void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + furi_timer_stop(App->Timer); + furi_timer_free(App->Timer); + App->Timer = NULL; +} + +/** + * @brief Callback for the uhf worker for writing. + * @details This function is called when the uhf worker is started for writing. + * @param event The UHFWorkerEvent - UHFReaderApp object. + * @param context The context - UHFReaderApp object. +*/ +void uhf_write_tag_worker_callback(UHFWorkerEvent event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + bool Redraw = true; + if(event == UHFWorkerEventSuccess) { + dolphin_deed(DolphinDeedNfcReadSuccess); + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_success); + + //Reset booleans tracking if the kill or access password were set for writing. + App->YRM100XWorker->KillPwd = false; + App->YRM100XWorker->AccessPwd = false; + + //If the save on write option is toggled, then update the fields for the saved tag + if(App->UHFSaveType == YES_SAVE_ON_WRITE){ + if(!flipper_format_file_open_existing(App->EpcFile, APP_DATA_PATH("Saved_EPCs.txt"))) { + FURI_LOG_E(TAG, "Failed to open file"); + } + FuriString* NumEpcs = furi_string_alloc(); + FuriString* EpcAndName = furi_string_alloc(); + FuriString* TempTid = furi_string_alloc(); + FuriString* TempRes = furi_string_alloc(); + FuriString* TempMem = furi_string_alloc(); + FuriString* TempEpc = furi_string_alloc(); + FuriString* TempPc = furi_string_alloc(); + FuriString* TempCrc = furi_string_alloc(); + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + if(furi_string_equal(Model->WriteFunction,WRITE_EPC_VAL)){ + furi_string_set(TempTid, Model->TidValue); + furi_string_set(TempRes, Model->ResValue); + furi_string_set(TempMem, Model->MemValue); + furi_string_set(TempEpc, Model->NewEpcValue); + + } + else if(furi_string_equal(Model->WriteFunction,WRITE_USR_MEM)){ + furi_string_set(TempTid, Model->TidValue); + furi_string_set(TempRes, Model->ResValue); + furi_string_set(TempMem, Model->NewEpcValue); + furi_string_set(TempEpc, Model->EpcValue); + + } + else if(furi_string_equal(Model->WriteFunction,WRITE_TID_MEM)){ + furi_string_set(TempTid, Model->NewEpcValue); + furi_string_set(TempRes, Model->ResValue); + furi_string_set(TempMem, Model->MemValue); + furi_string_set(TempEpc, Model->EpcValue); + + } + else if(furi_string_equal(Model->WriteFunction,WRITE_RES_MEM)){ + furi_string_set(TempTid, Model->TidValue); + furi_string_set(TempRes, Model->NewEpcValue); + furi_string_set(TempMem, Model->MemValue); + furi_string_set(TempEpc, Model->EpcValue); + + } + furi_string_set(TempPc, Model->Pc); + furi_string_set(TempCrc, Model->Crc); + }, + Redraw); + + //Get the selected tag index and save all tag fields + furi_string_printf(NumEpcs, "Tag%ld", App->SelectedTagIndex); + furi_string_printf( + EpcAndName, + "%s:%s:%s:%s:%s:%s:%s", + furi_string_get_cstr(App->EpcName), + furi_string_get_cstr(TempEpc), + furi_string_get_cstr(TempTid), + furi_string_get_cstr(TempRes), + furi_string_get_cstr(TempMem), + furi_string_get_cstr(TempPc), + furi_string_get_cstr(TempCrc)); + + if(!flipper_format_update_string_cstr( + App->EpcFile, furi_string_get_cstr(NumEpcs), furi_string_get_cstr(EpcAndName))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } + + flipper_format_file_close(App->EpcFile); + furi_string_free(NumEpcs); + furi_string_free(EpcAndName); + furi_string_free(TempEpc); + furi_string_free(TempPc); + furi_string_free(TempCrc); + furi_string_free(TempTid); + furi_string_free(TempRes); + furi_string_free(TempMem); + dolphin_deed(DolphinDeedRfidAdd); + + } + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFCustomEventWorkerExit); + } else if(event == UHFWorkerEventAborted) { + notification_message(App->Notifications, &uhf_sequence_blink_stop); + notification_message(App->Notifications, &sequence_error); + App->YRM100XWorker->KillPwd = false; + App->YRM100XWorker->AccessPwd = false; + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFCustomEventWorkerExitAborted); + } +} + +/** + * @brief Callback for custom write events. + * @details This function is called when a custom event is sent to the view dispatcher. + * @param event The event id - UHFReaderAppEventId value. + * @param context The context - UHFReaderApp object. + * @return true if the event was handled, false otherwise. +*/ +bool uhf_reader_view_write_custom_event_callback(uint32_t event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + switch(event) { + // Redraw screen by passing true to last parameter of with_view_model. + case UHFReaderEventIdRedrawScreen: { + bool redraw = true; + with_view_model(App->ViewWrite, UHFReaderWriteModel * _Model, { UNUSED(_Model); }, redraw); + return true; + } + //Indicate a success with a message on the screen! + case UHFCustomEventWorkerExit: { + bool Redraw = true; + App->IsWriting = false; + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(Model->WriteFunction, WRITE_EPC_OK); + Model->IsWriting = false; + }, + Redraw); + + return true; + } + //Indicate a failure :( + case UHFCustomEventWorkerExitAborted: { + bool Redraw = true; + App->IsWriting = false; + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + furi_string_set(Model->WriteFunction, WRITE_EPC_CANCELED); + Model->IsWriting = false; + }, + Redraw); + + return true; + } + //The ok button was pressed to trigger a write + case UHFReaderEventIdOkPressed: { + + + bool redraw = true; + dolphin_deed(DolphinDeedNfcRead); + + + //If the user presses the ok button while the app is writing (to cancel the operation if the tag is inactive) then the worker is stopped + if(App->IsWriting){ + uhf_worker_stop(App->YRM100XWorker); + App->IsWriting = false; + + return true; + } + + with_view_model( + App->ViewWrite, + UHFReaderWriteModel * Model, + { + Model->IsWriting = true; + //TODO: Modify this to work for the M6E and M7E + if(App->UHFModuleType != YRM100X_MODULE) { + if(furi_string_equal(Model->WriteFunction, WRITE_EPC_VAL)) { + uart_helper_send(App->UartHelper, "WRITE\n", 6); + uart_helper_send_string(App->UartHelper, Model->EpcValue); + uart_helper_send_string(App->UartHelper, Model->NewEpcValue); + } else if(furi_string_equal(Model->WriteFunction, WRITE_RES_MEM)) { + uart_helper_send(App->UartHelper, "WRITERES\n", 9); + uart_helper_send_string(App->UartHelper, Model->EpcValue); + uart_helper_send_string(App->UartHelper, Model->NewEpcValue); + } else if(furi_string_equal(Model->WriteFunction, WRITE_USR_MEM)) { + uart_helper_send(App->UartHelper, "WRITEUSR\n", 9); + uart_helper_send_string(App->UartHelper, Model->EpcValue); + uart_helper_send_string(App->UartHelper, Model->NewEpcValue); + } else if(furi_string_equal(Model->WriteFunction, WRITE_TID_MEM)) { + uart_helper_send(App->UartHelper, "WRITETID\n", 9); + uart_helper_send_string(App->UartHelper, Model->EpcValue); + uart_helper_send_string(App->UartHelper, Model->NewEpcValue); + } + } else { + // Resetting the dynamically allocated arrays to zero + uhf_reader_fetch_selected_tag(App); + memset(App->EpcBytes, 0, 12 * sizeof(uint8_t)); + memset(App->ResBytes, 0, 8 * sizeof(uint8_t)); + memset(App->TidBytes, 0, 16 * sizeof(uint8_t)); + memset(App->UserBytes, 0, 16 * sizeof(uint8_t)); + memset(App->PcBytes, 0, 2 * sizeof(uint16_t)); + memset(App->CrcBytes, 0, 2 * sizeof(uint16_t)); + + // Resetting the size_t variables to zero + App->EpcBytesLen = 0; + App->ResBytesLen = 0; + App->TidBytesLen = 0; + App->UserBytesLen = 0; + App->PcBytesLen = 0; + App->CrcBytesLen = 0; + + uhf_tag_reset(App->YRM100XWorker->NewTag); + + hex_string_to_uint16(furi_string_get_cstr(Model->Pc), App->PcBytes, &App->PcBytesLen); + hex_string_to_uint16(furi_string_get_cstr(Model->Crc), App->CrcBytes, &App->CrcBytesLen); + + uint16_t combinedPc = 0; + uint16_t combinedCrc = 0; + + for(size_t i = 0; i < 4; i++) { + combinedPc |= App->PcBytes[i]; + } + + for(size_t i = 0; i < 4; i++) { + combinedCrc |= App->CrcBytes[i]; + } + + if(furi_string_equal(Model->WriteFunction, WRITE_EPC_VAL) && + Model->NewEpcValue != NULL) { + hex_string_to_bytes( + App->TempSaveBuffer, App->EpcBytes, &App->EpcBytesLen); + + uhf_tag_set_epc( + App->YRM100XWorker->NewTag, (uint8_t*)App->EpcBytes, App->EpcBytesLen * sizeof(uint8_t)); + + m100_enable_write_mask(App->YRM100XWorker->module, WRITE_EPC); + } else { + hex_string_to_bytes( + furi_string_get_cstr(Model->EpcValue), App->EpcBytes, &App->EpcBytesLen); + uhf_tag_set_epc( + App->YRM100XWorker->NewTag, (uint8_t*)App->EpcBytes, App->EpcBytesLen * sizeof(uint8_t)); + } + + if(furi_string_equal(Model->WriteFunction, WRITE_USR_MEM) && + Model->NewEpcValue != NULL) { + hex_string_to_bytes( + furi_string_get_cstr(Model->NewEpcValue), App->UserBytes, &App->UserBytesLen); + uhf_tag_set_user( + App->YRM100XWorker->NewTag, (uint8_t*)App->UserBytes, App->UserBytesLen * sizeof(uint8_t)); + m100_enable_write_mask(App->YRM100XWorker->module, WRITE_USER); + + } else { + hex_string_to_bytes( + furi_string_get_cstr(Model->MemValue), App->UserBytes, &App->UserBytesLen); + uhf_tag_set_user( + App->YRM100XWorker->NewTag, (uint8_t*)App->UserBytes, App->UserBytesLen * sizeof(uint8_t)); + } + if(furi_string_equal(Model->WriteFunction, WRITE_TID_MEM) && + Model->NewEpcValue != NULL) { + hex_string_to_bytes( + furi_string_get_cstr(Model->NewEpcValue), App->TidBytes, &App->TidBytesLen); + uhf_tag_set_tid( + App->YRM100XWorker->NewTag, (uint8_t*)App->TidBytes, App->TidBytesLen * sizeof(uint8_t)); + m100_enable_write_mask(App->YRM100XWorker->module, WRITE_TID); + } else { + hex_string_to_bytes( + furi_string_get_cstr(Model->TidValue), App->TidBytes, &App->TidBytesLen); + uhf_tag_set_tid( + App->YRM100XWorker->NewTag, (uint8_t*)App->TidBytes,App->TidBytesLen * sizeof(uint8_t)); + } + if(furi_string_equal(Model->WriteFunction, WRITE_RES_MEM) && + Model->NewEpcValue != NULL) { + hex_string_to_bytes( + furi_string_get_cstr(Model->NewEpcValue), App->ResBytes, &App->ResBytesLen); + uhf_tag_set_kill_pwd(App->YRM100XWorker->NewTag, App->ResBytes, App->ResBytesLen); + uhf_tag_set_access_pwd(App->YRM100XWorker->NewTag, App->ResBytes, App->ResBytesLen); + + m100_enable_write_mask(App->YRM100XWorker->module, WRITE_RFU); + } else { + hex_string_to_bytes( + furi_string_get_cstr(Model->ResValue), App->ResBytes, &App->ResBytesLen); + + + } + + + uhf_tag_set_epc_pc(App->YRM100XWorker->NewTag, combinedPc); + uhf_tag_set_epc_crc(App->YRM100XWorker->NewTag, combinedCrc); + uhf_tag_set_epc_size(App->YRM100XWorker->NewTag, App->EpcBytesLen * sizeof(uint8_t)); + uhf_tag_set_user_size(App->YRM100XWorker->NewTag, App->UserBytesLen * sizeof(uint8_t)); + uhf_tag_set_tid_size(App->YRM100XWorker->NewTag, App->TidBytesLen * sizeof(uint8_t)); + + App->YRM100XWorker->KillPwd = true; + App->YRM100XWorker->AccessPwd = true; + + App->IsWriting = true; + + + + uhf_worker_start( + App->YRM100XWorker, + UHFWorkerStateWriteSingle, + uhf_write_tag_worker_callback, + App); + notification_message(App->Notifications, &uhf_sequence_blink_start_cyan); + } + }, + redraw); + + return true; + } + default: + return false; + } +} + +/** + * @brief Write Draw Callback. + * @details This function is called when the user selects write on the main submenu. + * @param canvas The canvas - Canvas object for drawing the screen. + * @param model The view model - model for the view with variables required for drawing. +*/ +void uhf_reader_view_write_draw_callback(Canvas* canvas, void* model) { + UHFReaderWriteModel* MyModel = (UHFReaderWriteModel*)model; + FuriString* xstr = furi_string_alloc(); + + //Clearing the canvas, setting the color, font and content displayed. + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, " Write Menu:"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 33, "Write Mode:"); + + //Displaying the current write mode selected + canvas_draw_str(canvas, 51, 33, furi_string_get_cstr(MyModel->WriteFunction)); + + //Display the CRC + canvas_draw_str(canvas, 4, 22, "CRC:"); + + canvas_draw_str(canvas, 28, 22, furi_string_get_cstr(MyModel->Crc)); + + //Display the PC + canvas_draw_str(canvas, 70, 22, "PC:"); + canvas_draw_str(canvas, 90, 22, furi_string_get_cstr(MyModel->Pc)); + + //Display the current write status + canvas_draw_str(canvas, 0, 44, "Write Status: "); + canvas_draw_str(canvas, 65, 44, furi_string_get_cstr(MyModel->WriteStatus)); + + //Display the write button + if(!MyModel->IsWriting) { + elements_button_center(canvas, "Write"); + + } else { + elements_button_center(canvas, "Cancel"); + } + furi_string_free(xstr); +} + +/** + * @brief Callback for write screen input. + * @details This function is called when the user presses a button while on the write screen. + * @param event The event - InputEvent object. + * @param context The context - UHFReaderApp object. + * @return true if the event was handled, false otherwise. +*/ +bool uhf_reader_view_write_input_callback(InputEvent* event, void* context) { + UHFReaderApp* App = (UHFReaderApp*)context; + + //Handle the short input types + if(event->type == InputTypeShort) { + //If the left button is pressed, then pull up the EPC value and keyboard + if(event->key == InputKeyLeft && !App->IsWriting) { + text_input_set_header_text(App->EpcWrite, "EPC Value"); + + bool redraw = false; + with_view_model( + App->ViewWrite, + + //Store the new epc value and mark the write function as the epc selection + UHFReaderWriteModel * Model, + { + strncpy( + App->TempSaveBuffer, + furi_string_get_cstr(Model->EpcValue), + App->TempBufferSaveSize); + furi_string_set_str(Model->WriteFunction, WRITE_EPC_VAL); + }, + redraw); + + // Configure the text input + + + bool clear_previous_text = false; + text_input_set_result_callback( + App->EpcWrite, + uhf_reader_epc_value_text_updated, + App, + App->TempSaveBuffer, + App->TempBufferSaveSize, + clear_previous_text); + view_set_previous_callback( + text_input_get_view(App->EpcWrite), uhf_reader_navigation_write_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewEpcWriteInput); + return true; + } + + //If the right button is pressed, then display the reserved memory bank and display the keyboard + else if(event->key == InputKeyRight && !App->IsWriting) { + text_input_set_header_text(App->EpcWrite, "Reserved Memory Bank"); + bool redraw = false; + with_view_model( + App->ViewWrite, + + //Store the modified value for the reserved memory bank + UHFReaderWriteModel * Model, + { + strncpy( + App->TempSaveBuffer, + furi_string_get_cstr(Model->ResValue), + App->TempBufferSaveSize); + furi_string_set_str(Model->WriteFunction, WRITE_RES_MEM); + }, + redraw); + + // Configure the text input + bool clear_previous_text = false; + text_input_set_result_callback( + App->EpcWrite, + uhf_reader_epc_value_text_updated, + App, + App->TempSaveBuffer, + App->TempBufferSaveSize, + clear_previous_text); + view_set_previous_callback( + text_input_get_view(App->EpcWrite), uhf_reader_navigation_write_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewEpcWriteInput); + return true; + } + + //If the up button is pressed, then display the user memory bank and keyboard + else if(event->key == InputKeyUp && !App->IsWriting) { + text_input_set_header_text(App->EpcWrite, "User Memory Bank"); + bool redraw = false; + with_view_model( + App->ViewWrite, + + //Store the modified user memory value + UHFReaderWriteModel * Model, + { + strncpy( + App->TempSaveBuffer, + furi_string_get_cstr(Model->MemValue), + App->TempBufferSaveSize); + furi_string_set(Model->WriteFunction, WRITE_USR_MEM); + }, + redraw); + + // Configure the text input + bool clear_previous_text = false; + text_input_set_result_callback( + App->EpcWrite, + uhf_reader_epc_value_text_updated, + App, + App->TempSaveBuffer, + App->TempBufferSaveSize, + clear_previous_text); + view_set_previous_callback( + text_input_get_view(App->EpcWrite), uhf_reader_navigation_write_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewEpcWriteInput); + return true; + } + + //If the down button is pressed, then display the TID memory bank and the keyboard + else if(event->key == InputKeyDown && !App->IsWriting) { + text_input_set_header_text(App->EpcWrite, "TID Memory Bank"); + bool redraw = false; + with_view_model( + App->ViewWrite, + + //Store the modified TID value + UHFReaderWriteModel * Model, + { + strncpy( + App->TempSaveBuffer, + furi_string_get_cstr(Model->TidValue), + App->TempBufferSaveSize); + furi_string_set_str(Model->WriteFunction, WRITE_TID_MEM); + }, + redraw); + + // Configure the text input + bool clear_previous_text = false; + text_input_set_result_callback( + App->EpcWrite, + uhf_reader_epc_value_text_updated, + App, + App->TempSaveBuffer, + App->TempBufferSaveSize, + clear_previous_text); + view_set_previous_callback( + text_input_get_view(App->EpcWrite), uhf_reader_navigation_write_callback); + view_dispatcher_switch_to_view(App->ViewDispatcher, UHFReaderViewEpcWriteInput); + return true; + } + } else if(event->type == InputTypePress) { + if(event->key == InputKeyOk) { + //Handle the OK button event + + view_dispatcher_send_custom_event(App->ViewDispatcher, UHFReaderEventIdOkPressed); + return true; + } + } + return false; +} + +/** + * @brief Callback when the user exits the write screen. + * @details This function is called when the user exits the write screen. + * @param context The context - not used + * @return the view id of the next view. +*/ +uint32_t uhf_reader_navigation_write_exit_callback(void* context) { + UNUSED(context); + return UHFReaderViewTagAction; +} + +/** + * @brief Allocates the write view. + * @details This function allocates all variables for the write view. + * @param context The context - UHFReaderApp object. +*/ +void view_write_alloc(UHFReaderApp* App) { + //Allocating the view and setting all callback functions + App->ViewWrite = view_alloc(); + view_set_draw_callback(App->ViewWrite, uhf_reader_view_write_draw_callback); + view_set_input_callback(App->ViewWrite, uhf_reader_view_write_input_callback); + view_set_previous_callback(App->ViewWrite, uhf_reader_navigation_write_exit_callback); + view_set_enter_callback(App->ViewWrite, uhf_reader_view_write_enter_callback); + view_set_exit_callback(App->ViewWrite, uhf_reader_view_write_exit_callback); + view_set_context(App->ViewWrite, App); + view_set_custom_callback(App->ViewWrite, uhf_reader_view_write_custom_event_callback); + + //Allocating the view model + view_allocate_model(App->ViewWrite, ViewModelTypeLockFree, sizeof(UHFReaderWriteModel)); + UHFReaderWriteModel* ModelWrite = view_get_model(App->ViewWrite); + FuriString* EpcNameWriteDefault = furi_string_alloc(); + + App->EpcBytesLen = 0; + App->ResBytesLen = 0; + App->TidBytesLen = 0; + App->UserBytesLen = 0; + App->PcBytesLen = 0; + App->CrcBytesLen = 0; + App->EpcBytes = (uint8_t*)malloc(12 * sizeof(uint8_t)); + App->ResBytes = (uint8_t*)malloc(8 * sizeof(uint8_t)); + App->TidBytes = (uint8_t*)malloc(16 * sizeof(uint8_t)); + App->UserBytes = (uint8_t*)malloc(16 * sizeof(uint8_t)); + App->PcBytes = (uint16_t*)malloc(2 * sizeof(uint16_t)); + App->CrcBytes = (uint16_t*)malloc(2 * sizeof(uint16_t)); + + //Setting default values for the view model + ModelWrite->Setting1Index = App->Setting1Index; + ModelWrite->Setting2Power = App->Setting2PowerStr; + ModelWrite->Setting3Index = App->Setting3Index; + ModelWrite->SettingKillPwd = furi_string_alloc_set(App->DefaultKillPassword); + ModelWrite->EpcName = EpcNameWriteDefault; + ModelWrite->Setting1Value = furi_string_alloc_set(App->Setting1Names[App->Setting1Index]); + ModelWrite->Setting3Value = furi_string_alloc_set(App->Setting3Names[App->Setting3Index]); + ModelWrite->Pc = furi_string_alloc_set("XXXX"); + ModelWrite->Crc = furi_string_alloc_set("XXXX"); + FuriString* EpcWriteDefault = furi_string_alloc(); + furi_string_set_str(EpcWriteDefault, "Press Write"); + FuriString* EpcValueWriteDefault = furi_string_alloc(); + furi_string_set_str(EpcValueWriteDefault, "Press Write"); + ModelWrite->EpcValue = EpcValueWriteDefault; + FuriString* EpcValueWriteStatus = furi_string_alloc(); + furi_string_set_str(EpcValueWriteStatus, "Press Write"); + ModelWrite->WriteStatus = EpcValueWriteStatus; + FuriString* WriteDefaultEpc = furi_string_alloc(); + ModelWrite->NewEpcValue = WriteDefaultEpc; + FuriString* DefaultWriteFunction = furi_string_alloc(); + furi_string_set_str(DefaultWriteFunction, "Press Arrow Keys"); + ModelWrite->WriteFunction = DefaultWriteFunction; + FuriString* DefaultWriteTid = furi_string_alloc(); + furi_string_set_str(DefaultWriteTid, "TID HERE"); + ModelWrite->TidValue = DefaultWriteTid; + FuriString* DefaultWriteTidNew = furi_string_alloc(); + furi_string_set_str(DefaultWriteTidNew, "NEW TID HERE"); + ModelWrite->NewTidValue = DefaultWriteTidNew; + FuriString* DefaultWriteRes = furi_string_alloc(); + furi_string_set_str(DefaultWriteRes, "RES HERE"); + ModelWrite->ResValue = DefaultWriteRes; + FuriString* DefaultWriteResNew = furi_string_alloc(); + furi_string_set_str(DefaultWriteResNew, "NEW RES HERE"); + ModelWrite->NewResValue = DefaultWriteResNew; + FuriString* DefaultWriteMem = furi_string_alloc(); + furi_string_set_str(DefaultWriteMem, "MEM HERE"); + ModelWrite->MemValue = DefaultWriteMem; + FuriString* DefaultWriteMemNew = furi_string_alloc(); + furi_string_set_str(DefaultWriteMemNew, "NEW MEM HERE"); + ModelWrite->NewMemValue = DefaultWriteMemNew; + App->EpcName = furi_string_alloc_set("Enter Name"); + App->EpcToWrite = furi_string_alloc_set("Enter Name"); + App->EpcWrite = text_input_alloc(); + view_dispatcher_add_view( + App->ViewDispatcher, UHFReaderViewEpcWriteInput, text_input_get_view(App->EpcWrite)); + + view_dispatcher_add_view(App->ViewDispatcher, UHFReaderViewWrite, App->ViewWrite); +} + +/** + * @brief Frees the write view. + * @details This function frees all variables for the write view. + * @param context The context - UHFReaderApp object. +*/ +void view_write_free(UHFReaderApp* App) { + free(App->EpcBytes); + free(App->ResBytes); + free(App->TidBytes); + free(App->UserBytes); + free(App->PcBytes); + free(App->CrcBytes); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewEpcWriteInput); + text_input_free(App->EpcWrite); + view_dispatcher_remove_view(App->ViewDispatcher, UHFReaderViewWrite); + view_free(App->ViewWrite); +} \ No newline at end of file diff --git a/non_catalog_apps/simultaneous_rfid_reader/views/view_write.h b/non_catalog_apps/simultaneous_rfid_reader/views/view_write.h new file mode 100644 index 00000000..34de56e5 --- /dev/null +++ b/non_catalog_apps/simultaneous_rfid_reader/views/view_write.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../app.h" +#include "../helpers/yrm100x_module.h" +#include "../helpers/yrm100x_tag.h" + +//Function Declarations +void uhf_reader_view_write_draw_callback(Canvas* canvas, void* model); + +void uhf_reader_view_write_timer_callback(void* context); + +void uhf_reader_view_write_enter_callback(void* context); + +void uhf_write_tag_worker_callback(UHFWorkerEvent event, void* context); + +void uhf_reader_view_write_exit_callback(void* context); + +bool uhf_reader_view_write_custom_event_callback(uint32_t event, void* context); + +bool uhf_reader_view_write_input_callback(InputEvent* event, void* context); + +void uhf_reader_epc_value_text_updated(void* context); + +uint32_t uhf_reader_navigation_write_callback(void* context); + +uint32_t uhf_reader_navigation_write_exit_callback(void* context); + +void view_write_alloc(UHFReaderApp* App); + +void view_write_free(UHFReaderApp* App); +