Repository: RT-Thread-packages/micropython Branch: master Commit: 97e1a5f1435e Files: 405 Total size: 3.3 MB Directory structure: gitextract_erc644zb/ ├── .gitignore ├── LICENSE ├── README.md ├── README_ZH.md ├── SConscript ├── docs/ │ ├── external_c_modules.md │ ├── firmware-develop.md │ ├── introduction.md │ ├── micropython-ide.md │ ├── micropython-librarys.md │ ├── micropython-mpy.md │ ├── micropython_for_pandora_iot_board.md │ ├── micropython_for_sparrow_one_board.md │ ├── micropython_for_w601_iot_board.md │ ├── spec-librarys/ │ │ ├── machine/ │ │ │ ├── ADC.md │ │ │ ├── I2C.md │ │ │ ├── LCD.md │ │ │ ├── PWM.md │ │ │ ├── Pin.md │ │ │ ├── RTC.md │ │ │ ├── SPI.md │ │ │ ├── Timer.md │ │ │ ├── UART.md │ │ │ └── WDT.md │ │ ├── machine.md │ │ ├── micropython.md │ │ ├── network/ │ │ │ └── wlan.md │ │ ├── network.md │ │ └── rtthread.md │ ├── std-librarys/ │ │ ├── _thread.md │ │ ├── builtins.md │ │ ├── cmath.md │ │ ├── gc.md │ │ ├── math.md │ │ ├── rtthread.md │ │ ├── sys.md │ │ ├── uarray.md │ │ ├── ubinascii.md │ │ ├── ucollections.md │ │ ├── uctypes.md │ │ ├── uerrno.md │ │ ├── uhashlib.md │ │ ├── uheapq.md │ │ ├── uio.md │ │ ├── ujson.md │ │ ├── uos.md │ │ ├── urandom.md │ │ ├── ure.md │ │ ├── uselect.md │ │ ├── usocket.md │ │ ├── ussl.md │ │ ├── ustruct.md │ │ ├── utime.md │ │ └── uzlib.md │ └── tools-mpy-cross.md ├── drivers/ │ └── bus/ │ ├── qspi.h │ ├── softqspi.c │ ├── softspi.c │ └── spi.h ├── extmod/ │ ├── axtls-include/ │ │ ├── config.h │ │ └── version.h │ ├── crypto-algorithms/ │ │ ├── sha256.c │ │ └── sha256.h │ ├── lwip-include/ │ │ ├── arch/ │ │ │ ├── cc.h │ │ │ └── perf.h │ │ └── lwipopts.h │ ├── machine_i2c.c │ ├── machine_i2c.h │ ├── machine_mem.c │ ├── machine_mem.h │ ├── machine_pinbase.c │ ├── machine_pinbase.h │ ├── machine_pulse.c │ ├── machine_pulse.h │ ├── machine_signal.c │ ├── machine_signal.h │ ├── machine_spi.c │ ├── machine_spi.h │ ├── misc.h │ ├── modbtree.c │ ├── modframebuf.c │ ├── modonewire.c │ ├── modubinascii.c │ ├── modubinascii.h │ ├── moducryptolib.c │ ├── moductypes.c │ ├── moduhashlib.c │ ├── moduheapq.c │ ├── modujson.c │ ├── modurandom.c │ ├── modure.c │ ├── moduselect.c │ ├── modussl_axtls.c │ ├── modussl_mbedtls.c │ ├── modutimeq.c │ ├── moduwebsocket.c │ ├── moduwebsocket.h │ ├── moduzlib.c │ ├── modwebrepl.c │ ├── modwebsocket.c │ ├── modwebsocket.h │ ├── re1.5/ │ │ ├── charclass.c │ │ ├── compilecode.c │ │ ├── dumpcode.c │ │ ├── re1.5.h │ │ └── recursiveloop.c │ ├── uos_dupterm.c │ ├── utime_mphal.c │ ├── utime_mphal.h │ ├── uzlib/ │ │ ├── adler32.c │ │ ├── crc32.c │ │ ├── defl_static.h │ │ ├── tinf.h │ │ ├── tinf_compat.h │ │ ├── tinfgzip.c │ │ ├── tinflate.c │ │ ├── tinfzlib.c │ │ ├── uzlib.h │ │ └── uzlib_conf.h │ ├── vfs.c │ ├── vfs.h │ ├── vfs_fat.c │ ├── vfs_fat.h │ ├── vfs_fat_diskio.c │ ├── vfs_fat_file.c │ ├── vfs_posix.c │ ├── vfs_posix.h │ ├── vfs_posix_file.c │ ├── vfs_reader.c │ ├── virtpin.c │ ├── virtpin.h │ └── webrepl/ │ ├── manifest.py │ ├── webrepl.py │ ├── webrepl_setup.py │ └── websocket_helper.py ├── lib/ │ ├── mp-readline/ │ │ ├── readline.c │ │ └── readline.h │ ├── netutils/ │ │ ├── netutils.c │ │ └── netutils.h │ ├── oofatfs/ │ │ ├── diskio.h │ │ ├── ff.c │ │ ├── ff.h │ │ ├── ffconf.h │ │ └── option/ │ │ ├── ccsbcs.c │ │ └── unicode.c │ ├── timeutils/ │ │ ├── timeutils.c │ │ └── timeutils.h │ └── utils/ │ ├── gchelper.h │ ├── gchelper_m0.s │ ├── gchelper_m3.s │ ├── interrupt_char.c │ ├── interrupt_char.h │ ├── mpirq.c │ ├── mpirq.h │ ├── printf.c │ ├── pyexec.c │ ├── pyexec.h │ ├── stdout_helpers.c │ └── sys_stdio_mphal.c ├── port/ │ ├── frozen_mpy.c │ ├── gccollect.c │ ├── genhdr/ │ │ ├── gen_qstr.py │ │ ├── moduledefs.h │ │ ├── mpversion.h │ │ └── qstrdefs.generated.h │ ├── modules/ │ │ ├── machine/ │ │ │ ├── machine_adc.c │ │ │ ├── machine_adc.h │ │ │ ├── machine_hw_i2c.c │ │ │ ├── machine_hw_spi.c │ │ │ ├── machine_lcd.c │ │ │ ├── machine_lcd.h │ │ │ ├── machine_pin.c │ │ │ ├── machine_pwm.c │ │ │ ├── machine_pwm.h │ │ │ ├── machine_rtc.c │ │ │ ├── machine_rtc.h │ │ │ ├── machine_timer.c │ │ │ ├── machine_timer.h │ │ │ ├── machine_uart.c │ │ │ ├── machine_uart.h │ │ │ ├── machine_wdt.c │ │ │ ├── machine_wdt.h │ │ │ ├── modmachine.c │ │ │ └── modmachine.h │ │ ├── modffi.c │ │ ├── modfile.c │ │ ├── modnetwork.c │ │ ├── modnetwork.h │ │ ├── modnetwork_wlan.c │ │ ├── modpyb.c │ │ ├── modrtthread.c │ │ ├── moduos.c │ │ ├── moduos_file.c │ │ ├── moduos_file.h │ │ ├── modusocket.c │ │ ├── modutils.c │ │ ├── modutime.c │ │ └── user/ │ │ └── moduserfunc.c │ ├── mpconfigport.h │ ├── mpgetcharport.c │ ├── mpgetcharport.h │ ├── mphalport.c │ ├── mphalport.h │ ├── mpputsnport.c │ ├── mpputsnport.h │ ├── mpthreadport.c │ ├── mpthreadport.h │ ├── mpy_main.c │ ├── mpy_project_cfg.h │ ├── native/ │ │ ├── easyflash_module.c │ │ ├── easyflash_module.py │ │ ├── native_module.c │ │ └── native_module.py │ └── qstrdefsport.h ├── py/ │ ├── argcheck.c │ ├── asmarm.c │ ├── asmarm.h │ ├── asmbase.c │ ├── asmbase.h │ ├── asmthumb.c │ ├── asmthumb.h │ ├── asmx64.c │ ├── asmx64.h │ ├── asmx86.c │ ├── asmx86.h │ ├── asmxtensa.c │ ├── asmxtensa.h │ ├── bc.c │ ├── bc.h │ ├── bc0.h │ ├── binary.c │ ├── binary.h │ ├── builtin.h │ ├── builtinevex.c │ ├── builtinhelp.c │ ├── builtinimport.c │ ├── compile.c │ ├── compile.h │ ├── dynruntime.h │ ├── dynruntime.mk │ ├── emit.h │ ├── emitbc.c │ ├── emitcommon.c │ ├── emitglue.c │ ├── emitglue.h │ ├── emitinlinethumb.c │ ├── emitinlinextensa.c │ ├── emitnarm.c │ ├── emitnative.c │ ├── emitnthumb.c │ ├── emitnx64.c │ ├── emitnx86.c │ ├── emitnxtensa.c │ ├── emitnxtensawin.c │ ├── formatfloat.c │ ├── formatfloat.h │ ├── frozenmod.c │ ├── frozenmod.h │ ├── gc.c │ ├── gc.h │ ├── grammar.h │ ├── lexer.c │ ├── lexer.h │ ├── makecompresseddata.py │ ├── makemoduledefs.py │ ├── makeqstrdata.py │ ├── makeqstrdefs.py │ ├── makeversionhdr.py │ ├── malloc.c │ ├── map.c │ ├── misc.h │ ├── mkenv.mk │ ├── mkrules.mk │ ├── modarray.c │ ├── modbuiltins.c │ ├── modcmath.c │ ├── modcollections.c │ ├── modgc.c │ ├── modio.c │ ├── modmath.c │ ├── modmicropython.c │ ├── modstruct.c │ ├── modsys.c │ ├── modthread.c │ ├── moduerrno.c │ ├── mpconfig.h │ ├── mperrno.h │ ├── mphal.h │ ├── mpprint.c │ ├── mpprint.h │ ├── mpstate.c │ ├── mpstate.h │ ├── mpthread.h │ ├── mpz.c │ ├── mpz.h │ ├── nativeglue.c │ ├── nativeglue.h │ ├── nlr.c │ ├── nlr.h │ ├── nlrpowerpc.c │ ├── nlrsetjmp.c │ ├── nlrthumb.c │ ├── nlrx64.c │ ├── nlrx86.c │ ├── nlrxtensa.c │ ├── obj.c │ ├── obj.h │ ├── objarray.c │ ├── objarray.h │ ├── objattrtuple.c │ ├── objbool.c │ ├── objboundmeth.c │ ├── objcell.c │ ├── objclosure.c │ ├── objcomplex.c │ ├── objdeque.c │ ├── objdict.c │ ├── objenumerate.c │ ├── objexcept.c │ ├── objexcept.h │ ├── objfilter.c │ ├── objfloat.c │ ├── objfun.c │ ├── objfun.h │ ├── objgenerator.c │ ├── objgenerator.h │ ├── objgetitemiter.c │ ├── objint.c │ ├── objint.h │ ├── objint_longlong.c │ ├── objint_mpz.c │ ├── objlist.c │ ├── objlist.h │ ├── objmap.c │ ├── objmodule.c │ ├── objmodule.h │ ├── objnamedtuple.c │ ├── objnamedtuple.h │ ├── objnone.c │ ├── objobject.c │ ├── objpolyiter.c │ ├── objproperty.c │ ├── objrange.c │ ├── objreversed.c │ ├── objset.c │ ├── objsingleton.c │ ├── objslice.c │ ├── objstr.c │ ├── objstr.h │ ├── objstringio.c │ ├── objstringio.h │ ├── objstrunicode.c │ ├── objtuple.c │ ├── objtuple.h │ ├── objtype.c │ ├── objtype.h │ ├── objzip.c │ ├── opmethods.c │ ├── pairheap.c │ ├── pairheap.h │ ├── parse.c │ ├── parse.h │ ├── parsenum.c │ ├── parsenum.h │ ├── parsenumbase.c │ ├── parsenumbase.h │ ├── persistentcode.c │ ├── persistentcode.h │ ├── profile.c │ ├── profile.h │ ├── py.mk │ ├── pystack.c │ ├── pystack.h │ ├── qstr.c │ ├── qstr.h │ ├── qstrdefs.h │ ├── reader.c │ ├── reader.h │ ├── repl.c │ ├── repl.h │ ├── ringbuf.c │ ├── ringbuf.h │ ├── runtime.c │ ├── runtime.h │ ├── runtime0.h │ ├── runtime_utils.c │ ├── scheduler.c │ ├── scope.c │ ├── scope.h │ ├── sequence.c │ ├── showbc.c │ ├── smallint.c │ ├── smallint.h │ ├── stackctrl.c │ ├── stackctrl.h │ ├── stream.c │ ├── stream.h │ ├── unicode.c │ ├── unicode.h │ ├── vm.c │ ├── vmentrytable.h │ ├── vstr.c │ └── warning.c └── tools/ └── mpy-cross/ ├── hellortt.mpy └── hellortt.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /.vscode/settings.json ================================================ FILE: LICENSE ================================================ 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. ================================================ FILE: README.md ================================================ # MicroPython [中文页](README_ZH.md) | English ## 1. Introduction This is a port of `MicroPython` on RT-Thread, which can run on **RT-Thread 3.0** or higher. This software package can run `MicroPython` on embedded systems equipped with RT-Thread. If it is the first time to come into contact with RT-Thread MicroPython, it is recommended that you use RT-Thread officially supported development boards to get started quickly. These development boards have complete firmware functions and provide source code, suitable for introductory learning, and officially support development boards [firmware download Please click on me](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20). ### 1.1 Directory structure | Name | Description | | ---- | ---- | | docs | Document directory, including getting started guide and development manual | | drivers | MicroPython source code directory | | extmod | MicroPython Source Code Directory | | lib | MicroPython source code directory | | py | MicroPython source code directory | | port | Porting code directory | | LICENSE | Micropython MIT License | ### 1.2 License RT-Thread MicroPython follows the MIT license, see the `LICENSE` file for details. ### 1.3 Dependency - RT-Thread 3.0+ ## 2. How to open RT-Thread MicroPython To use `MicroPython package`, you need to select it in the RT-Thread package manager. The specific path is as follows: ![elect_micropytho](./docs/assets/select_micropython.png) Then let the RT-Thread package manager automatically update, or use the `pkgs --update` command to update the package to the BSP. ## 3. Use RT-Thread MicroPython ### 3.1 Add software package to project After selecting `MicroPython package`, when compiling with `bsp` again, it will be added to the `bsp` project for compilation. * For firmware development, please refer to [《MicroPython Firmware Development Guide》](./docs/firmware-develop.md) * For more MicroPython documentation, please visit [RT-Thread Documentation Center](https://www.rt-thread.org/document/site/submodules/micropython/docs/introduction/) ### 3.2 Using MicroPython IDE [RT-Thread MicroPython IDE](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) provides a powerful development environment for MicroPython, which can be directly searched and downloaded through the VScode application store. Examples are as follows: ![08_direct_run_files](docs/assets/08_direct_run_files.gif) ### 3.3 Add C extension to MicroPython In order to facilitate users to add their own C functions to MicroPython to be called by Python scripts, RT-Thread provides [MicroPython C binding code automatic generator](https://summerlife.github.io/RT-MicroPython-Generator/) For everyone to use. With this tool, users only need a few simple steps to achieve C function extension. The following figure shows the form of the automatically generated C code. ![08_direct_run_files](docs/assets/c-gen.png) ## 4. Matters needing attention - Need to use **RT-Thread 3.0** or above - Select the `latest` version of `Micropython` in the `menuconfig` option - Currently, the `ffi` module under `System Module` only supports GCC toolchain, and relevant information needs to be added to the link script ## 5. Development resources * [RT-Thread MicroPython Forum](https://www.rt-thread.org/qa/forum.php) * [RT-Thread MicroPython Documentation Center](https://www.rt-thread.org/document/site/submodules/micropython/docs/introduction/) * [Click to join the RT-Thread MicroPython exchange group](https://jq.qq.com/?_wv=1027&k=5EhyEjx) ================================================ FILE: README_ZH.md ================================================ # MicroPython 中文页 | [English](README.md) ## 1、介绍 这是一个在 RT-Thread 上的 `MicroPython` 移植,可以运行在 **RT-Thread 3.0** 版本以上。通过该软件包可以在搭载了 RT-Thread 的嵌入式系统上运行 `MicroPython`。 如果是第一次接触 RT-Thread MicroPython,推荐你先通过 RT-Thread 官方支持的开发板来快速上手,这些开发板的固件功能完善并提供源代码,适合入门学习,官方支持开发板 [固件下载请点我](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20)。 ### 1.1 目录结构 | 名称 | 说明 | | ---- | ---- | | docs | 文档目录,包括入门指南和开发手册 | | drivers | MicroPython 源代码目录 | | extmod | MicroPython 源代码目录 | | lib | MicroPython 源代码目录 | | py | MicroPython 源代码目录 | | port | 移植代码目录 | | LICENSE | Micropython MIT 许可证 | ### 1.2 许可证 RT-Thread MicroPython 遵循 MIT 许可,详见 `LICENSE` 文件。 ### 1.3 依赖 - RT-Thread 3.0+ ## 2、如何打开 RT-Thread MicroPython 使用 `MicroPython package` 需要在 RT-Thread 的包管理器中选择它,具体路径如下: ![elect_micropytho](./docs/assets/select_micropython.png) 然后让 RT-Thread 的包管理器自动更新,或者使用 `pkgs --update` 命令更新包到 BSP 中。 ## 3、使用 RT-Thread MicroPython ### 3.1 添加软件包到工程 选中 `MicroPython package` 后,再次进行 `bsp` 编译时,它会被加入到 `bsp` 工程中进行编译。 * 固件开发可参考 [《MicroPython 固件开发指南》](./docs/firmware-develop.md) * 查阅更多 MicroPython 说明文档请访问 [RT-Thread 文档中心](https://www.rt-thread.org/document/site/submodules/micropython/docs/introduction/) ### 3.2 使用 MicroPython IDE [RT-Thread MicroPython IDE](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) 为 MicroPython 提供了强大的开发环境,可以通过 VScode 应用商店直接查询下载,示例如下所示: ![08_direct_run_files](docs/assets/08_direct_run_files.gif) ### 3.3 向 MicroPython 添加 C 扩展 为了方便用户添加自己编写的 C 函数到 MicroPython 中被 Python 脚本调用,RT-Thread 提供了 [MicroPython C 绑定代码自动生成器](https://summerlife.github.io/RT-MicroPython-Generator/) 供大家使用。通过该工具,用户只需要简单几步,即可实现 C 函数扩展,下图展示了自动生成的 C 代码的形式。 ![08_direct_run_files](docs/assets/c-gen.png) ## 4、注意事项 - 需要使用 **RT-Thread 3.0** 以上版本 - 在 `menuconfig` 选项中选择 `Micropython` 的 `latest` 版本 - 目前 `System Module` 下的 `ffi` 模块只支持 GCC 工具链,且需要在链接脚本中添加相关段信息 ## 5、开发资源 * [RT-Thread MicroPython 论坛](https://www.rt-thread.org/qa/forum.php) * [RT-Thread MicroPython 文档中心](https://www.rt-thread.org/document/site/submodules/micropython/docs/introduction/) * [点击加入 RT-Thread MicroPython 交流群](https://jq.qq.com/?_wv=1027&k=5EhyEjx) ================================================ FILE: SConscript ================================================ from building import * import rtconfig # get current directory cwd = GetCurrentDir() # The set of source files associated with this SConscript file. src = Glob('py/*.c') src += Glob('lib/mp-readline/*.c') src += Glob('lib/utils/*.c') src += Glob('extmod/*.c') src += Glob('port/*.c') src += Glob('port/modules/*.c') src += Glob('port/modules/machine/*.c') src += Glob('port/modules/user/*.c') src += Glob('lib/netutils/*.c') src += Glob('lib/timeutils/*.c') src += Glob('drivers/bus/*.c') src += Glob('port/native/*.c') path = [cwd + '/'] path += [cwd + '/port'] path += [cwd + '/port/modules'] path += [cwd + '/port/modules/machine'] LOCAL_CCFLAGS = '' if rtconfig.PLATFORM in ['gcc', 'armclang']: LOCAL_CCFLAGS += ' -std=gnu99' elif rtconfig.PLATFORM in ['keil']: LOCAL_CCFLAGS += ' --c99 --gnu' group = DefineGroup('MicroPython', src, depend = ['PKG_USING_MICROPYTHON'], CPPPATH = path, LOCAL_CCFLAGS = LOCAL_CCFLAGS) Return('group') ================================================ FILE: docs/external_c_modules.md ================================================ # 为 MicroPython 扩展 C 模块 当使用原生 MicroPython 进行开发时,你可能会遇到这样一些限制,比如官方没有实现自己想要的功能,或者你觉得这些实现不符合自己的工作需求。此时,添加自己的 C 模块到 MicroPython 中是一个不错的选择,你可以按照自己的想法,设计适合自己的 Python 函数调用。 为了帮助各位开发者快速添加 C 模块,RT-Thread 提供了相应的辅助工具 [C 绑定代码自动生成器](https://summerlife.github.io/RT-MicroPython-Generator/)。该工具可以帮助开发者自动生成 C 代码和 MicroPython 之间的接口层,开发者只需将 C 语言编写的功能代码添加到指定位置,MicroPython 即可直接调用该功能。 ## Python 调用 C 函数的实现原理 C 语言和 Python 是两种完全不同的语言,如何使用 MicroPython 来调用 C 语言所实现的函数是许多小伙伴非常疑惑的地方。简单来说,这个问题的关键点在于,如何用 C 语言的形式在 MicroPython 源代码中**表述函数的入参和出参**。我举一个例子来讲解这个问题, 请观察如下 Python 函数: ```python def add(a, b): return a + b ``` 该函数有两个入参,返回一个参数。此时如果能用 C 语言表示该 **Python 函数的输入输出参数**,就可以将一个实现该功能的 C 函数对接到 MicroPython 中。 ### 添加用户函数到 MicroPython 假设上述函数的参数类型都为整形,通过自动生成器可以得到如下样板函数: ```c STATIC mp_obj_t add( mp_obj_t arg_1_obj, mp_obj_t arg_2_obj) { mp_int_t arg_1 = mp_obj_get_int(arg_1_obj); /* 通过 Python 获取的第一个整形参数 arg_1 */ mp_int_t arg_2 = mp_obj_get_int(arg_2_obj); /* 通过 Python 获取的第二个整形参数 arg_2 */ mp_int_t ret_val; /* Your code start! */ ret_val = arg_1 + arg_2; /* 处理入参 arg_1 和 arg_2,并将结果赋给返回值 ret_val */ /* Your code end! */ return mp_obj_new_int(ret_val); /* 向 python 返回整形参数 ret_val */ } MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add); ``` 生成器会处理好需要导出到 MicroPython 的函数的入参和出参,而开发者只需要编写相应的代码来处理这些输入参数,并且把返回值赋给输出参数即可。 通过包含头文件的方式,可以调用先前编写的 C 函数来对输入参数进行处理,或者根据输入参数来执行相应的动作,添加控制硬件的驱动的原理也是一样的。 最终使用 Python 调用 C 函数的效果如下: ```python >>> import userfunc >>> userfunc.add(666,777) 1443 ``` ### 添加用户模块到 MicroPython 添加用户模块到 MicroPython 中也不难,首先应当熟练掌握上述添加 C 函数的过程,然后参考 PR [add module userfunc to MicroPython](https://github.com/RT-Thread-packages/micropython/pull/144) 来添加属于自己的模块,该 PR 实现了添加 `userfunc` 模块到 MicroPython 的功能,你可以按照同样的方式将自己编写的模块注册到 MicroPython 中,要注意仔细查看这个 PR 中修改的 4 个文件,不要漏掉修改的细节。 ================================================ FILE: docs/firmware-develop.md ================================================ ## MicroPython 固件开发指南 如果手上没有官方支持固件的开发板,就需要自己来动手制作 MicroPython 固件了。由于 RT-Thread 官方提供了 [MicroPython 软件包](https://github.com/RT-Thread-packages/micropython),并且 MicroPython 底层和硬件绑定时对接了 RT-Thread 驱动框架,因此可以很方便地在运行了 RT-Thread 的板卡上将 MicroPython 跑起来。 **注意**:RT-Thread MicroPython 需要运行在 **RT-Thread 3.0** 版本以上。 ### 选择合适的 BSP 平台 RT-Thread MicroPython mini 版本占用资源最大不超过: - ROM : 190KB - RAM : 20KB 只要系统资源满足上述要求,常见的许多开发板都可以运行 MicroPython,例如 STM32 系列 BSP。 接下来我们以 `rt-thread\bsp\stm32\stm32f407-atk-explorer` 上的 MDK 工程为例,讲解如何在 BSP 的基础上制作 MicroPython 固件。 ### 获取 MicroPython 软件包 先使用 `pkgs --upgrade` 命令更新软件包列表,然后通过 env 工具选中 MicroPython 软件包,最后使用 `pkgs --update` 命令将软件包拉取到本地。 ![select_mpy_package](assets/select_mpy_package.png) ### 增大 main 线程栈 为了能后续在 main 线程中启动 MicroPython 运行时环境,需要增大 main 线程的栈大小,这里我们将栈大小增加到 8k。 ![add_main_stack](assets/add_main_stack.png) ### 配置 MicroPython 运行环境堆大小 接下来根据板卡内存实际剩余情况来给 MicroPython 运行环境分配内存,这里填写的数值越大,就能运行更大代码量的 Python 程序。但是如果这里填写的数值超过了实际可分配内存,就可能会出现无法分配内存而报错。因此在配置此项目之前,需要对系统 RAM 资源的分配情况有一定了解。 #### 查看系统剩余内存 重新生成工程,编译下载后通过 `msh` 的 `free` 命令来查看内存使用情况。 ![check_memory](assets/check_memory.png) #### 配置系统 通过上一步查询的内存分配情况,对系统 RAM 资源有了一定的了解。在本次示例中,我们分配 20k 内存用于 MicroPython 运行时环境。后续如果想要运行更多 MicroPython 代码,可以将更多空余内存分配给 MicroPython 运行时环境,配置如下图所示: ![config_runtime](assets/config_runtime.png) ### 在系统根目录挂载文件系统 最后要确保系统中 `/` 目录挂载了文件系统。有了文件系统,后续才能使用 [**MicroPython 开发环境**](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) 将 Python 代码文件同步到板卡中来运行。 1. 打开 MicroPython 的文件同步功能选项 ![open filesync option](assets/open_filesync_option.png) 2. 本次示例使用的开发板,文件系统存放在 SPI Flash 上,BSP 对该存储设备的支持已经做好了,在这里只需开启 elm-fat 文件系统即可,对系统进行如下配置: ![mount_fs](assets/mount_fs.png) 配置完成后,记得要使用 `scons --target=mkd5` 重新生成工程,使配置在工程中生效。 当你在自己的板卡上运行 MicroPython 时,你可以自由选择文件系统的存储介质,但是有一点很重要,文件系统要被挂载到根目录 / 上,这样才能保证在后续使用 MicroPython IDE 进行文件传输时不会出错。 ### 在 main 线程中启动 MicroPython 最后要在 main 线程中启动 MicroPython,代码修改如下所示: ```c #include #include #include #include #include /* 文件系统所在分区名称,根据实际情况填写 */ #define FS_PARTITION_NAME "W25Q128" int main(void) { /* 挂载 elm 文件系统到 / 目录,如果你所使用的开发板没有文件系统也可以跳过这一步 */ if (dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0) { rt_kprintf("Filesystem initialized!"); } else { /* 如果挂载失败,则尝试在文件系统分区重新创建文件系统 */ dfs_mkfs("elm", FS_PARTITION_NAME); /* 尝试重新挂载文件系统 */ if (dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0) { /* 仍然挂载文件系统失败,请自行检查失败原因 */ rt_kprintf("Failed to initialize filesystem!"); } } rt_thread_mdelay(100); while(1) { /* 在这里让程序进入循环,通过这种方式实现 MicroPython 的软复位*/ extern void mpy_main(const char *filename); /* 启动 MicroPython */ mpy_main(NULL); } return RT_EOK; } ``` 重新编译工程并下载程序到板卡中,就会在 main 线程中自动启动 MicroPython,接下来就可以使用 [**RT-Thread MicroPython 开发环境**](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) 来进行应用开发了。 通过开发环境连接到开发板,即可看到 MicroPython 的交互环境 REPL,如下图所示: ![en_connect_board](assets/en_connect_board.gif) ================================================ FILE: docs/introduction.md ================================================ # MicroPython 入门必读 本文档将初步介绍 MicroPython 的基本概念,RT-Thread MicroPython 的特性与优势,以及可以被用在哪些领域。 ## 主要特性 - MicroPython 是 Python 3 编程语言的一种精简而高效的实现,它包含 Python 标准库的一个子集,并被优化为在微控制器和受限环境中运行。 - RT-Thread MicroPython 可以运行在任何搭载了 RT-Thread 操作系统并且有一定资源的嵌入式平台上。 - MicroPython 可以运行在有一定资源的开发板上,给你一个低层次的 Python 操作系统,可以用来控制各种电子系统。 - MicroPython 富有各种高级特性,比如交互式提示、任意精度整数、闭包函数、列表解析、生成器、异常处理等等。 - MicroPython 的目标是尽可能与普通 Python 兼容,使开发者能够轻松地将代码从桌面端转移到微控制器或嵌入式系统。程序可移植性很强,因为不需要考虑底层驱动,所以程序移植变得轻松和容易。 ## MicroPython 的优势 - Python 是一款容易上手的脚本语言,同时具有强大的功能,语法优雅简单。使用 MicroPython 编程可以降低嵌入式的开发门槛,让更多的人体验嵌入式的乐趣。 - 通过 MicroPython 实现硬件底层的访问和控制,不需要了解底层寄存器、数据手册、厂家的库函数等,即可轻松控制硬件。 - 外设与常用功能都有相应的模块,降低开发难度,使开发和移植变得容易和快速。 ## MicroPython 的应用领域 - MicroPython 在嵌入式系统上完整实现了 Python3 的核心功能,可以在产品开发的各个阶段给开发者带来便利。 - 通过 MicroPython 提供的库和函数,开发者可以快速控制 LED、液晶、舵机、多种传感器、SD、UART、I2C 等,实现各种功能,而不用再去研究底层硬件模块的使用方法,翻看寄存器手册。这样不但降低了开发难度,而且减少了重复开发工作,可以加快开发速度,提高开发效率。以前需要较高水平的嵌入式工程师花费数天甚至数周才能完成的功能,现在普通的嵌入式开发者用几个小时就能实现类似的功能。 - 随着半导体技术的不断发展,芯片的功能、内部的存储器容量和资源不断增加,成本不断降低,可以使用 MicroPython 来进行开发设计的应用领域也会越来越多。 ### 产品原型验证 - 众所周知,在开发新产品时,原型设计是一个非常重要的环节,这个环节需要以最快速的方式设计出产品的大致模型,并验证业务流程或者技术点。与传统开发方法相比,使用 MicroPython 对于原型验证非常有用,让原型验证过程变得轻松,加速原型验证过程。 - 在进行一些物联网功能开发时,网络功能也是 MicroPython 的长处,可以利用现成的众多 MicroPython 网络模块,节省开发时间。而这些功能如果使用 C/C++ 来完成,会耗费几倍的时间。 ### 硬件测试 - 嵌入式产品在开发时,一般会分为硬件开发及软件开发。硬件工程师并不一定都擅长软件开发,所以在测试新硬件时,经常需要软件工程师参与。这就导致软件工程师可能会耗费很多时间帮助硬件工程师查找设计或者焊接问题。有了 MicroPython 后,将 MicroPython 固件烧入待测试的新硬件,在检查焊接、连线等问题时,只需使用简单的 Python 命令即可测试。这样,硬件工程师一人即可搞定,再也不用麻烦别人了。 ### 创客 DIY - MicroPython 无需复杂的设置,不需要安装特别的软件环境和额外的硬件,使用任何文本编辑器就可以进行编程。大部分硬件功能,使用一个命令就能驱动,不用了解硬件底层就能快速开发。这些特性使得 MicroPython 非常适合创客使用来开发一些有创意的项目。 - 下面是使用 MicroPython 开发的一些 DIY 项目: - [显示温湿度的 WIFI 时钟](https://www.bilibili.com/video/av15929152?from=search&seid=16285206333541196172) - [OpenMV 智能摄像头](https://www.bilibili.com/video/av16418889?from=search&seid=16285206333541196172) - [快速实现人脸识别](https://www.bilibili.com/video/av73853903?from=search&seid=9793819178982436353) - [搭建 MQTT 服务器](http://www.360doc.com/content/17/1218/22/8473307_714341237.shtml) ### 教育 - MicroPython 使用简单、方便,非常适合于编程入门。在校学生或者业余爱好者都可以通过 MicroPython 快速的开发一些好玩的项目,在开发的过程中学习编程思想,提高自己的动手能力。 ================================================ FILE: docs/micropython-ide.md ================================================ # MicroPython IDE RT-Thread 为广大开发者提供了 VSCode 最好用的 MicroPython 插件 来帮助大家使用 MicroPython 来开发应用程序。该插件为 MicroPython 开发提供了功能强大的开发环境,主要特性如下: - 便捷的开发板连接方式(串口、网络、USB) - 支持基于 MicroPython 的代码智能补全与语法检查 - 支持 MicroPython REPL 交互环境 - 提供丰富的代码示例与 demo 程序 - 提供工程同步功能 - 支持下载单个文件或文件夹至开发板 - 支持在内存中快速运行代码文件功能 - 支持运行代码片段功能 - 支持多款主流 MicroPython 开发板 - 支持 Windows、Ubuntu、Mac 操作系统 ### 安装 IDE 开发环境 开发者可以通过 RT-Thread MicroPython IDE 来快速开发 MicroPython 应用,下图展示了 IDE 的快速调试功能: ![08_direct_run_files](assets/08_direct_run_files.gif) 可通过查看如下文档进一步了解并使用 RT-Thread MicroPython IDE: - [RT-Thread MicroPython Develop Environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) ================================================ FILE: docs/micropython-librarys.md ================================================ # MicroPython 库 ### MicroPython 标准库 - [`Builtin functions and exceptions`](std-librarys/builtins.md) – 内置函数与异常 - [`cmath`](std-librarys/cmath.md) – 复数运算函数功能 - [`gc`](std-librarys/gc.md) – 控制垃圾收集器 - [`math`](std-librarys/math.md) – 数学函数功能 - [`sys`](std-librarys/sys.md) – 系统特定功能 - [`uarray`](std-librarys/uarray.md) – 数组存储功能 - [`ubinascii`](std-librarys/ubinascii.md) – 二进制与 ASCII 码转换功能 - [`ucollections`](std-librarys/ucollections.md) – 集合与容器类型 - [`uerrno`](std-librarys/uerrno.md) – 系统错误码 - [`uhashlib`](std-librarys/uhashlib.md) – 哈希算法 - [`uheapq`](std-librarys/uheapq.md) – 堆队列算法 - [`uio`](std-librarys/uio.md) – 输入输出流 - [`ujson`](std-librarys/ujson.md) – JSON 编解码 - [`uos`](std-librarys/uos.md) – 基本的操作系统服务 - [`ure`](std-librarys/ure.md) – 正则表达式 - [`uselect`](std-librarys/uselect.md) – 在一组 streams 上等待事件 - [`usocket`](std-librarys/usocket.md) – socket 模块 - [`ussl`](std-librarys/ussl.md) – SSL/TLS 模块 - [`ustruct`](std-librarys/ustruct.md) – 原生数据类型的打包和解包 - [`utime`](std-librarys/utime.md) – 时间相关功能 - [`uzlib`](std-librarys/uzlib.md) – zlib 解压 - [`_thread`](std-librarys/_thread.md) – 多线程支持 ### MicroPython 特定库 在 RT-Thread 移植的 MicroPython 版本中,实现了如下特定功能库: - [`micropython`](spec-librarys/micropython.md) – 实现 MicroPython 内部功能访问与控制 - [`rtthread`](spec-librarys/rtthread.md) – RT-Thread 系统功能模块 - [`machine`](spec-librarys/machine.md) – 硬件控制模块 - [Pin](spec-librarys/machine/Pin.md) - [I2C ](spec-librarys/machine/I2C.md) - [SPI](spec-librarys/machine/SPI.md) - [UART](spec-librarys/machine/UART.md) - [LCD](spec-librarys/machine/LCD.md) - [RTC](spec-librarys/machine/RTC.md) - [PWM](spec-librarys/machine/PWM.md) - [ADC](spec-librarys/machine/ADC.md) - [WDT](spec-librarys/machine/WDT.md) - [TIMER](spec-librarys/machine/Timer.md) - [`network`](spec-librarys/network.md) – 网络功能配置模块 - [wlan](spec-librarys/network/wlan.md) ================================================ FILE: docs/micropython-mpy.md ================================================ # MicroPython .mpy 文件详解 MicroPython 定义了 `.mpy` 文件的概念,该文件是一种二进制容器文件格式,在其中包含了预编译的 Python 代码,这种类型的文件可以像普通的 `.py` 模块一样被导入到 MicroPython 程序中。举个例子来说明这种类型文件的使用方法。例如,只要 `foo.mpy` 存在于指定的路径中,我们就可以通过 `import foo` 语句来导入 `foo.mpy` 文件。 这种类型文件的导入规则是这样的,首先按顺序搜索 `sys.path` 中列出的每个目录。当搜索特定目录时,首先查找 `foo.py`,如果找不到该目录,则查找 `foo.mpy`,如果没有找到,则在下一个目录中继续搜索。通过这种方式,`foo.py` 文件的优先级将高于 `foo.mpy` 文件。这些 `.mpy` 文件中的主要内容是字节码,这种类型的文件可以通过 `mpy-cross` 程序从 Python 源文件(`.py`文件)生成。 ================================================ FILE: docs/micropython_for_pandora_iot_board.md ================================================ # MicroPython for Pandora IoT Board ![IoT_Board](assets/IoT_Board.png) [**IoT Board 潘多拉**](https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-18400369818.12.2ba47ea5PzJxZx&id=583843059625) 是 RT-Thread 推出的一款物联网开发板,它给开发者带来了物联网时代的无限可能。而现在,它已经不仅仅是一块简单的物联网开发板,因为它已经全面支持 **MicroPython** 。在 IoT Board 上,你将会体验到有别于传统的,前所未有的开发方式。 借助于 MicroPython,你将有能力使用 Python 语言控制所有硬件外设,体验高级语言带来的便利特性,与此同时你还可以利用高级软件库快速实现你的 IoT 构想。 ## 硬件支持 Pandora MicroPython 固件硬件功能如下所示: | 外设名称 | 引脚号 | 简介 | | -------- | ---------------------------------------------- | ----------------------------------------- | | pin | PA4 PA8, PB8-9 PB10-15, PC2 PC4 PC6-7, PD12-15 | 开发板引出的可自由分配的 IO,支持引脚中断 | | led | PE7 | 红色 led 灯 | | rgb | R: PE7, G: PE8, B: PE9 | rgb 灯 | | key | KEY0: PD10, KEY1: PD9, KEY2: PD8 | 输入按键 | | uart1 | PA9, PA10 | 串口1 | | i2c | | 软件 i2c 可选择任意 pin | | spi | | 软件 spi 可选择任意引出 pin | | adc | PC4 | adc1,通道 13 | | pwm | PB0 | pwm3, 通道 3, 用于红外发射 | | timer | | 硬件定时器 15 | | wdt | | 看门狗 | | rtc | | 实时时钟 | | beeper | PB2 | 蜂鸣器 | | lcd | | lcd 显示屏 | | wifi | | wifi 网络连接 | | aht10 | CLK: PD6, SDA: PC1 | 温湿度传感器 | | ap3216c | CLK: PC0, SDA: PC1 | 接近与光强传感器 | | icm20608 | CLK: PC0, SDA: PC1 | 六轴传感器 | ## 入门必读 如果您从来没有了解过 MicroPython, 可以阅读这篇简短的文章来 [带你入门 MicroPython](introduction.md)。 ## 开启 MicroPython 之旅 推荐遵循如下步骤开始进行 MicroPython 开发: - 在您的开发板上烧录合适的固件 - 在 PC 机上安装 RT-Thread MicroPython 开发环境并连接上开发板 接下来就可以尽情挥洒您的创意了,更详细的内容可以点击下文中的链接来进一步了解。 ### 下载合适的固件 - [Pandora IoT Board firmware](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20) ### 安装 IDE 开发环境 - [RT-Thread MicroPython develop environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) ## 开发资料 ### 示例程序 以下示例程序可以在 RT-Thread MicroPython IDE 开发环境中直接添加到工程: ![check_pandora_examples](assets/check_pandora_examples.png) ### MicroPython 模块详解 - [MicroPython Librarys](micropython-librarys.md) ## 联系我们 如果在使用的过程中遇到问题,您可以用如下方式联系我们: - 在 github 上提交 issue - [RT-Thread MicroPython 官方论坛](https://www.rt-thread.org/qa/forum.php?mod=forumdisplay&fid=2&filter=typeid&typeid=20) - RT-Thread MicroPython 交流 QQ 群:703840633 ================================================ FILE: docs/micropython_for_sparrow_one_board.md ================================================ # MicroPython for sparrow one board ![sparrow_one](assets/sparrow_one_board.png) [**麻雀一号开发板**](https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-5210898174.2.29401ae39JyGKY&id=606684373403) 是 RT-Thread 推出的一款物联网音视频开发板,现在它已经全面支持 **MicroPython** 。在麻雀一号开发板上,你将会体验到有别于传统的,前所未有的开发方式。 借助于 MicroPython,你将有能力使用 Python 语言控制所有硬件外设,体验高级语言带来的便利特性,与此同时你还可以利用高级软件库快速实现你的 IoT 构想。 ## 硬件支持 麻雀一号开发板固件硬件功能如下所示: | 外设名称 | 简介 | | --------- | ---------------------------------------------------------- | | key | 输入按键 | | uart1 | 串口1 | | lcd | lcd 显示屏 | | wifi | wifi 网络连接 | | bluetooth | 蓝牙 | | player | 扬声器,音频播放 | | recorder | 麦克风,录音功能 | | camera | 摄像头,可拍照并存入文件系统,开启 TCP Server 查看实时图像 | ## 入门必读 如果您从来没有了解过 MicroPython, 可以阅读这篇简短的文章来 [带你入门 MicroPython](https://github.com/RT-Thread-packages/micropython/blob/master/docs/introduction.md)。 ## 开启 MicroPython 之旅 推荐遵循如下步骤开始进行 MicroPython 开发: - 在您的开发板上烧录合适的固件 - 在 PC 机上安装 RT-Thread MicroPython 开发环境并连接上开发板 接下来就可以尽情挥洒您的创意了,更详细的内容可以点击下文中的链接来进一步了解。 ### 下载合适的固件 - [Sparrow One Board firmware](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20) ### 安装 IDE 开发环境 - [RT-Thread MicroPython develop environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) ## 开发资料 ### 示例程序 以下示例程序可以在 RT-Thread MicroPython IDE 开发环境中直接添加到工程: ![sparrow_example](assets/sparrow_example.png) ### MicroPython 模块详解 - [MicroPython Librarys](https://github.com/RT-Thread-packages/micropython/blob/master/docs/micropython-librarys.md) ## 联系我们 如果在使用的过程中遇到问题,您可以用如下方式联系我们: - 在 github 上提交 issue - [RT-Thread MicroPython 官方论坛](https://www.rt-thread.org/qa/forum.php?mod=forumdisplay&fid=2&filter=typeid&typeid=20) - RT-Thread MicroPython 交流 QQ 群:703840633 ================================================ FILE: docs/micropython_for_w601_iot_board.md ================================================ # MicroPython for W601 IoT Board ![IoT_Board](assets/W60x_HW_origin.png) [**W601 IoT Board**](https://item.taobao.com/item.htm?spm=a230r.1.14.13.7c5b4a9bS2LYUD&id=602233847745&ns=1&abbucket=17#detail) 是 RT-Thread 推出的一款物联网开发板,它给开发者带来了物联网时代的无限可能。而现在,它已经不仅仅是一块简单的物联网开发板,因为它已经全面支持 **MicroPython** 。在 IoT Board 上,你将会体验到有别于传统的,前所未有的开发方式。 借助于 MicroPython,你将有能力使用 Python 语言控制所有硬件外设,体验高级语言带来的便利特性,与此同时你还可以利用高级软件库快速实现你的 IoT 构想。 ## 硬件支持 W601 IoT Board MicroPython 固件硬件功能如下所示: | 外设名称 | 引脚号 | 简介 | | -------- | -------------------------------------- | ----------------------------------------- | | pin | PA11, PB4、10-14 、17-18、23-26、30-31 | 开发板引出的可自由分配的 IO,支持引脚中断 | | led | PA13 | 红色 led 灯 | | rgb | R: PA13, G: PA14, B: PA15 | rgb 灯 | | key | KEY0: PA7, KEY1: PA6, | 输入按键 | | uart1 | PA4, PA5 | 串口1 | | i2c | | 软件 i2c 可选择任意 pin | | spi | | 软件 spi 可选择任意引出 pin | | adc | PB23 - 26 | adc,通道 5 - 8 | | pwm | PB17、PB18 | pwm1, 通道 1、2 | | timer | | 硬件定时器 1 | | wdt | | 看门狗 | | rtc | | 实时时钟 | | beeper | PB15 | 蜂鸣器 | | lcd | | lcd 显示屏 | | wifi | | wifi 网络连接 | | aht10 | CLK: PA0, SDA: PA1 | 温湿度传感器 | | ap3216c | CLK: PA2, SDA: PA1 | 接近与光强传感器 | ## 入门必读 如果您从来没有了解过 MicroPython, 可以阅读这篇简短的文章来 [带你入门 MicroPython](https://github.com/RT-Thread-packages/micropython/blob/master/docs/introduction.md)。 ## 开启 MicroPython 之旅 推荐遵循如下步骤开始进行 MicroPython 开发: - 在您的开发板上烧录合适的固件 - 在 PC 机上安装 RT-Thread MicroPython 开发环境并连接上开发板 接下来就可以尽情挥洒您的创意了,更详细的内容可以点击下文中的链接来进一步了解。 ### 下载合适的固件 - [W601 IoT Board firmware](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20) ### 安装 IDE 开发环境 - [RT-Thread MicroPython develop environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) ## 开发资料 ### 示例程序 以下示例程序可以在 RT-Thread MicroPython IDE 开发环境中直接添加到工程: ![w601_examples](assets/w601_examples.png) ### MicroPython 模块详解 - [MicroPython Librarys](https://github.com/RT-Thread-packages/micropython/blob/master/docs/micropython-librarys.md) ## 联系我们 如果在使用的过程中遇到问题,您可以用如下方式联系我们: - 在 github 上提交 issue - [RT-Thread MicroPython 官方论坛](https://www.rt-thread.org/qa/forum.php?mod=forumdisplay&fid=2&filter=typeid&typeid=20) - RT-Thread MicroPython 交流 QQ 群:703840633 ================================================ FILE: docs/spec-librarys/machine/ADC.md ================================================ ## machine.ADC **machine.ADC** 类是 machine 模块下的一个硬件类,用于指定 ADC 设备的配置和控制,提供对 ADC 设备的操作方法。 - ADC(Analog-to-Digital Converter,模数转换器),用于将连续变化的模拟信号转化为离散的数字信号。 - ADC 设备两个重要参数:采样值、分辨率; - 采样值:当前时间由模拟信号转化的数值信号的数值; - 分辨率:以二进制(或十进制)数的位数来表示,一般有 8 位、10 位、12 位、16 位等,它说明模数转换器对输入信号的分辨能力,位数越多,表示分辨率越高,采样值会更精确。 ### 构造函数 在 RT-Thread MicroPython 中 `ADC` 对象的构造函数如下: #### **class machine.ADC**(id, channel) - **id**:使用的 ADC 设备编号,`id = 1` 表示编号为 1 的 ADC 设备,或者表示使用的 ADC 设备名,如 `id = "adc"` 表示设备名为 `adc` 的 ADC 设备; - **channel**:使用的 ADC 设备通道号,每个 ADC 设备对应多个通道; 例如:`ADC(1,4)` 表示当前使用编号为 1 的 ADC 设备的 4 通道。 ### 方法 #### **ADC.init**(channel) 根据输入的层参数初始化 ADC 对象,入参为使用的 ADC 对象通道号; #### **ADC.deinit**() 用于关闭 ADC 对象,ADC 对象 deinit 之后需要重新 init 才能使用。 #### **ADC.read**() 用于获取并返回当前 ADC 对象的采样值。例如当前采样值为 2048,对应设备的分辨率为 12位,当前设备参考电压为 3.3V ,则该 ADC 对象通道上实际电压值的计算公式为:**采样值 * 参考电压 / (1 << 分辨率位数)**,即 `vol = 2048 / 4096 * 3.3 V = 1.15V`。 ### 示例 ``` python >>> from machine import ADC # 从 machine 导入 ADC 类 >>> adc = ADC(1, 13) # 创建 ADC 对象,当前使用编号为 1 的 ADC 设备的 13 通道 >>> adc.read() # 获取 ADC 对象采样值 4095 >>> adc.deinit() # 关闭 ADC 对象 >>> adc.init(13) # 开启并重新配置 ADC 对象 ``` ================================================ FILE: docs/spec-librarys/machine/I2C.md ================================================ ## machine.I2C **machine.I2C** 类是 `machine` 模块下面的一个硬件类,用于对 `I2C` 的配置和控制,提供对 `I2C` 设备的操作方法。 - `I2C` 是一种用于设备间通信的两线协议。在物理层上,它由两根线组成:`SCL` 和 `SDA` ,即时钟和数据线。 - `I2C` 对象被创建到一个特定的总线上,它们可以在创建时被初始化,也可以之后再来初始化。 - 打印 `I2C` 对象会打印出配置时的信息。 ### 构造函数 在 RT-Thread MicroPython 中 `I2C` 对象的构造函数如下: #### **class machine.I2C**(id= -1, scl, sda, freq=400000) 使用下面的参数构造并返回一个新的 `I2C` 对象: - **id** :标识特定的 `I2C` 外设。如果填入 id = -1,即选择软件模拟的方式实现 `I2C`,这时可以使用任意引脚来模拟 `I2C` 总线 ,这样在初始化时就必须指定 `scl` 和 `sda` 。 软件 I2C 的初始化方式可参考 [软件 I2C 示例](#i2c_2)。 硬件 I2C 的初始化方式可参考 [硬件 I2C 示例](#i2c_3)。 - **scl** : 应该是一个 `Pin` 对象,指定为一个用于 `scl` 的 `Pin` 对象。 - **sda** : 应该是一个 `Pin` 对象,指定为一个用于 `sda` 的 `Pin` 对象。 - **freq** :应该是为 `scl` 设置的最大频率。 ### 方法 #### **I2C.init**(scl, sda, freq=400000) 初始化 `I2C` 总线,参数介绍可以参考构造函数中的参数。 #### **I2C.deinit**() 关闭 `I2C` 总线。 #### **I2C.scan**() 扫描所有 0x08 和 0x77 之间的 `I2C` 地址,然后返回一个有响应地址的列表。如果一个设备在总线上收到了他的地址,就会通过拉低 `SDA` 的方式来响应。 ### I2C 基础方法 下面的方法实现了基本的 `I2C` 总线操作,可以组合成任何的 `I2C` 通信操作,如果需要对总线进行更多的控制,可以可以使用他们,否则可以使用后面介绍的标准使用方法。 #### **I2C.start**() 在总线上产生一个启动信号。(`SCL` 为高时,`SDA` 转换为低) #### **I2C.stop**() 在总线上产生一个停止信号。(`SCL` 为高时,`SDA` 转换为高) #### **I2C.readinto**(buf, nack=True) 从总线上读取字节并将他们存储到 `buf` 中,读取的字节数时 `buf` 的长度。在收到最后一个字节以外的所有内容后,将在总线上发送 `ACK`。在收到最后一个字节之后,如果 `NACK` 是正确的,那么就会发送一个 `NACK`,否则将会发送 `ACK`。 #### **I2C.write**(buf) 将 `buf` 中的数据接入到总线,检查每个字节之后是否收到 `ACK`,并在收到 `NACK` 时停止传输剩余的字节。这个函数返回接收到的 `ACK` 的数量。 ### I2C 标准总线操作 下面的方法实现了标准 `I2C` 主设备对一个给定从设备的读写操作。 #### **I2C.readfrom**(addr, nbytes, stop=True) 从 `addr` 指定的从设备中读取 n 个字节,如果 `stop = True`,那么在传输结束时会产生一个停止信号。函数会返回一个存储着读到数据的字节对象。 #### **I2C.readfrom_into**(addr, buf, stop=True) 从 `addr` 指定的从设备中读取数据存储到 `buf` 中,读取的字节数将是 `buf` 的长度,如果 `stop = True`,那么在传输结束时会产生一个停止信号。 这个方法没有返回值。 #### **I2C.writeto**(addr, buf, stop=True) 将 `buf` 中的数据写入到 `addr` 指定的的从设备中,如果在写的过程中收到了 `NACK` 信号,那么就不会发送剩余的字节。如果 `stop = True`,那么在传输结束时会产生一个停止信号,即使收到一个 `NACK`。这个函数返回接收到的 `ACK` 的数量。 ### 内存操作 一些 `I2C` 设备充当一个内存设备,可以读取和写入。在这种情况下,有两个与 `I2C` 相关的地址,从机地址和内存地址。下面的方法是与这些设备进行通信的便利函数。 #### **I2C.readfrom_mem**(addr, memaddr, nbytes, \*, addrsize=8) 从 `addr` 指定的从设备中 `memaddr` 地址开始读取 n 个字节。`addrsize` 参数指定地址的长度。返回一个存储读取数据的字节对象。 #### **I2C.readfrom_mem_into**(addr, memaddr, buf, \*, addrsize=8) 从 `addr` 指定的从设备中 `memaddr` 地址读取数据到 `buf` 中,,读取的字节数是 `buf` 的长度。 这个方法没有返回值。 #### **I2C.writeto_mem**(addr, memaddr, buf, \*, addrsize=8) 将 `buf` 里的数据写入 `addr` 指定的从机的 `memaddr` 地址中。 这个方法没有返回值。 ### 示例 #### 软件模拟 I2C ```python >>> from machine import Pin, I2C >>> clk = Pin(("clk", 29), Pin.OUT_OD) # Select the 29 pin device as the clock >>> sda = Pin(("sda", 30), Pin.OUT_OD) # Select the 30 pin device as the data line >>> i2c = I2C(-1, clk, sda, freq=100000) # create I2C peripheral at frequency of 100kHz >>> i2c.scan() # scan for slaves, returning a list of 7-bit addresses [81] # Decimal representation >>> i2c.writeto(0x51, b'123') # write 3 bytes to slave with 7-bit address 42 3 >>> i2c.readfrom(0x51, 4) # read 4 bytes from slave with 7-bit address 42 b'\xf8\xc0\xc0\xc0' >>> i2c.readfrom_mem(0x51, 0x02, 1) # read 1 bytes from memory of slave 0x51(7-bit), b'\x12' # starting at memory-address 8 in the slave >>> i2c.writeto_mem(0x51, 2, b'\x10') # write 1 byte to memory of slave 42, # starting at address 2 in the slave ``` #### 硬件 I2C 需要先开启 `I2C` 设备驱动,查找设备可以在 `msh` 中输入`list_device` 命令。 在构造函数的第一个参数传入 `0`,系统就会搜索名为 `i2c0` 的设备,找到之后使用这个设备来构建 `I2C` 对象: ```python >>> from machine import Pin, I2C >>> i2c = I2C(0) # create I2C peripheral at frequency of 100kHz >>> i2c.scan() # scan for slaves, returning a list of 7-bit addresses [81] # Decimal representation ``` 更多内容可参考 [machine.I2C](http://docs.micropython.org/en/latest/pyboard/library/machine.I2C.html) 。 ================================================ FILE: docs/spec-librarys/machine/LCD.md ================================================ ## machine.LCD **machine.LCD** 类是 machine 模块下面的一个硬件类,用于对 LCD 的配置和控制,提供对 LCD 设备的操作方法。 IoT board 板载一块 1.3 寸,分辨率为 `240*240` 的 LCD 显示屏,因此对该屏幕操作时,(x, y) 坐标的范围是 `0 - 239`。 ### 构造函数 在 RT-Thread MicroPython 中 `LCD` 对象的构造函数如下: #### **class machine.LCD**() 在给定总线上构造一个 `LCD` 对象,无入参,初始化的对象取决于特定硬件,初始化方式可参考 [示例](#_3)。 ### 方法 #### **LCD.light**(value) 控制是否开启 LCD 背光,入参为 True 则打开 LCD 背光,入参为 False 则关闭 LCD 背光。 #### **LCD.fill**(color) 根据给定的颜色填充整个屏幕,支持多种颜色,可以传入的参数有: ``` WHITE BLACK BLUE BRED GRED GBLUE RED MAGENTA GREEN CYAN YELLOW BROWN BRRED GRAY GRAY175 GRAY151 GRAY187 GRAY240 ``` 详细的使用方法可参考[示例](#_3)。 #### **LCD.pixel**(x, y, color) 向指定的位置(x, y)画点,点的颜色为 color 指定的颜色,可指定的颜色和上一个功能相同。 > 注意:(x, y) 坐标不要超过实际范围,使用下面的方法对坐标进行操作时同样需要遵循此限制。 #### **LCD.text**(str, x, y, size) 在指定的位置(x,y)写入字符串,字符串由 str 指定,字体的大小由 size 指定,size 的大小可为 16,24,32。 #### **LCD.line**(x1, y1, x2, y2) 在 LCD 上画一条直线,起始地址为 (x1, y1),终点为(x2, y2)。 #### **LCD.rectangle**(x1, y1, x2, y2) 在 LCD 上画一个矩形,左上角的位置为(x1, y1),右下角的位置为(x2, y2)。 #### **LCD.circle**(x1, y1, r) 在 LCD 上画一个圆形,圆心的位置为(x1, y1),半径长度为 r。 #### **LCD.show_bmp**( x, y, pathname) 在 LCD 指定位置上显示 32-bit bmp 格式的图片信息,注意显示 bmp 图片时,(x, y) 坐标是图片的左下角。 ### 示例 ```python from machine import LCD # 从 machine 导入 LCD 类 lcd = LCD() # 创建一个 lcd 对象 lcd.light(False) # 关闭背光 lcd.light(True) # 打开背光 lcd.fill(lcd.BLACK) # 将整个 LCD 填充为黑色 lcd.fill(lcd.RED) # 将整个 LCD 填充为红色 lcd.fill(lcd.GRAY) # 将整个 LCD 填充为灰色 lcd.fill(lcd.WHITE) # 将整个 LCD 填充为白色 lcd.pixel(50, 50, lcd.BLUE) # 将(50,50)位置的像素填充为蓝色 lcd.text("hello RT-Thread", 0, 0, 16) # 在(0, 0) 位置以 16 字号打印字符串 lcd.text("hello RT-Thread", 0, 16, 24) # 在(0, 16)位置以 24 字号打印字符串 lcd.text("hello RT-Thread", 0, 48, 32) # 在(0, 48)位置以 32 字号打印字符串 lcd.line(0, 50, 239, 50) # 以起点(0,50),终点(239,50)画一条线 lcd.line(0, 50, 239, 50) # 以起点(0,50),终点(239,50)画一条线 lcd.rectangle(100, 100, 200, 200) # 以左上角为(100,100),右下角(200,200)画矩形 lcd.circle(150, 150, 80) # 以圆心位置(150,150),半径为 80 画圆 lcd.show_bmp(180, 50, "sun.bmp") # 以位置(180,50)为图片左下角坐标显示文件系统中的 bmp 图片 "sun.bmp" ``` ================================================ FILE: docs/spec-librarys/machine/PWM.md ================================================ ## machine.PWM **machine.PWM** 类是 machine 模块下的一个硬件类,用于指定 PWM 设备的配置和控制,提供对 PWM 设备的操作方法。 - PWM (Pulse Width Modulation,脉冲宽度调制) 是一种对模拟信号电平进行数字编码的方式; - PWM 设备可以通过调节有效电平在一个周期信号中的比例时间来操作设备; - PWM 设备两个重要的参数:频率(freq)和占空比(duty); - 频率:从一个上升沿(下降沿)到下一个上升沿(下降沿)的时间周期,单位为 Hz; - 占空比:有效电平(通常为电平)在一个周期内的时间比例; ### 构造函数 在 RT-Thread MicroPython 中 `PWM` 对象的构造函数如下: #### **class machine.PWM**(id, channel, freq, duty) 在给定的总线上构建一个 `PWM` 对象,参数介绍如下: - **id**:使用的 PWM 设备编号,如 `id = 1` 表示编号为 1 的 PWM 设备,或者表示使用的 PWM 设备名,如 `id = "pwm"` 表示设备名为 `pwm` 的 PWM 设备; - **channel**:使用的 PWM 设备通道号,每个 PWM 设备包含多个通道,范围为 [0, 4]; - **freq**:初始化频率,范围 [1, 156250]; - **duty**:初始化占空比数值,范围 [0 255]; 例如:`PWM(1,4,100,100)` 表示当前使用 编号为 1 的 PWM 设备的 4 通道,初始化频率为 1000 Hz,初始化占空比的数值为 100。 ### 方法 #### **PWM.init**(channel, freq, duty) 根据输入的参数初始化 PWM 对象,参数说明同上。 #### **PWM.deinit**() 用于关闭 PWM 对象,对象 deinit 之后需要重新 init 才能使用。 #### **PWM.freq**(freq) 用于获取或者设置 PWM 对象的频率,频率的范围为 [1, 156250]。如果参数为空,返回当前 PWM 对象的频率;如果参数非空,则使用该参数设置当前 PWM 对象的频率。 #### **PWM.duty**(duty) 用于获取或者设置 PWM 对象的占空比数值,占空比数值的范围为 [0, 255],例如 `duty = 100`,表示当前设备占空比为 `100/255 = 39.22%` 。如果参数为空,返回当前 PWM 对象的占空比数值;如果参数非空,则使用该参数设置当前 PWM 对象的占空比数值。 ### 示例 ``` python >>> from machine import PWM # 从 machine 导入 PWM 类 >>> pwm = PWM(3, 3, 1000, 100) # 创建 PWM 对象,当前使用编号为 3 的 PWM 设备的 3 通道,初始化的频率为 1000Hz,占空比数值为 100(占空比为 100/255 = 39.22%) >>> pwm.freq(2000) # 设置 PWM 对象频率 >>> pwm.freq() # 获取 PWM 对象频率 2000 >>> pwm.duty(200) # 设置 PWM 对象占空比数值 >>> pwm.duty() # 获取 PWM 对象占空比数值 200 >>> pwm.deinit() # 关闭 PWM 对象 >>> pwm.init(3, 1000, 100) # 开启并重新配置 PWM 对象 ``` ================================================ FILE: docs/spec-librarys/machine/Pin.md ================================================ ## machine.Pin **machine.Pin** 类是 machine 模块下面的一个硬件类,用于对引脚的配置和控制,提供对 `Pin` 设备的操作方法。 `Pin` 对象用于控制输入/输出引脚(也称为 `GPIO`)。`Pin` 对象通常与一个物理引脚相关联,他可以驱动输出电压和读取输入电压。Pin 类中有设置引脚模式(输入/输出)的方法,也有获取和设置数字逻辑(`0` 或 `1`)的方法。 一个 `Pin` 对象是通过一个标识符来构造的,它明确地指定了一个特定的输入输出。标识符的形式和物理引脚的映射是特定于一次移植的。标识符可以是整数,字符串或者是一个带有端口和引脚号码的元组。在 RT-Thread MicroPython 中,引脚标识符是一个由代号和引脚号组成的元组,如 `Pin(("PB15", 31), Pin.OUT_PP)` 中的` ("PB15", 31)`。 ### 构造函数 在 RT-Thread MicroPython 中 `Pin` 对象的构造函数如下: #### **class machine.Pin**( id, mode = -1, pull = -1,value) - **id** :由用户自定义的引脚名和 `Pin` 设备引脚号组成,如 ("PB15", 31),"PB15" 为用户自定义的引脚名,`31` 为 `RT-Thread Pin` 设备驱动在本次移植中的引脚号。 - **mode** : 指定引脚模式,可以是以下几种: - **Pin.IN** :输入模式 - **Pin.OUT_PP** :输出模式 - **Pin.OUT_OD** :开漏模式 - **pull** : 如果指定的引脚连接了上拉下拉电阻,那么可以配置成下面的状态: - **None** :没有上拉或者下拉电阻。 - **Pin.PULL_UP** :使能上拉电阻。 - **Pin.PULL_DOWN** :使能下拉电阻。 - **value** : `value` 的值只对输出模式和开漏输出模式有效,用来设置初始输出值。 ### 方法 #### **Pin.init**(mode= -1, pull= -1, \*, value, drive, alt) 根据输入的参数重新初始化引脚。只有那些被指定的参数才会被设置,其余引脚的状态将保持不变,详细的参数可以参考上面的构造函数。 #### **Pin.value**([x]) 如果没有给定参数 `x` ,这个方法可以获得引脚的值。 如果给定参数 `x` ,如 `0` 或 `1`,那么设置引脚的值为 逻辑 `0` 或 逻辑 `1`。 #### **Pin.name**() 返回引脚对象在构造时用户自定义的引脚名。 #### **Pin.irq**(handler=None, trigger=(Pin.IRQ_RISING)) 配置在引脚的触发源处于活动状态时调用的中断处理程序。如果引脚模式是, `Pin.IN` 则触发源是引脚上的外部值。 如果引脚模式是, `Pin.OUT` 则触发源是引脚的输出缓冲器。 否则,如果引脚模式是, `Pin.OPEN_DRAIN` 那么触发源是状态'0'的输出缓冲器和状态'1'的外部引脚值。 参数: - `handler` 是一个可选的函数,在中断触发时调用 - `trigger` 配置可以触发中断的事件。可能的值是: - `Pin.IRQ_FALLING` 下降沿中断 - `Pin.IRQ_RISING` 上升沿中断 - `Pin.IRQ_RISING_FALLING` 上升沿或下降沿中断 - `Pin.IRQ_LOW_LEVEL` 低电平中断 - `Pin.IRQ_HIGH_LEVEL` 高电平中断 ### 常量 下面的常量用来配置 `Pin` 对象。 #### 选择引脚模式: ##### **Pin.IN** ##### **Pin.OUT_PP** ##### **Pin.OUT_OD** #### 选择上/下拉模式: ##### **Pin.PULL_UP** ##### **Pin.PULL_DOWN** ##### **None** 使用值 `None` 代表不进行上下拉。 #### 选择中断触发模式: ##### **Pin.IRQ_FALLING** ##### **Pin.IRQ_RISING** ##### **Pin.IRQ_RISING_FALLING** ##### **Pin.IRQ_LOW_LEVEL** ##### **Pin.IRQ_HIGH_LEVEL** ### 示例一 控制引脚输出高低电平信号,并读取按键引脚电平信号。 ``` from machine import Pin PIN_OUT = 31 PIN_IN = 58 p_out = Pin(("PB15", PIN_OUT), Pin.OUT_PP) p_out.value(1) # set io high p_out.value(0) # set io low p_in = Pin(("key_0", PIN_IN), Pin.IN, Pin.PULL_UP) print(p_in.value() ) # get value, 0 or 1 ``` ### 示例二 上升沿信号触发引脚中断后执行中断处理函数。 ``` from machine import Pin PIN_KEY0 = 58 # PD10 key_0 = Pin(("key_0", PIN_KEY0), Pin.IN, Pin.PULL_UP) def func(v): print("Hello rt-thread!") key_0.irq(trigger=Pin.IRQ_RISING, handler=func) ``` 更多内容可参考 [machine.Pin](http://docs.micropython.org/en/latest/pyboard/library/machine.Pin.html) 。 ================================================ FILE: docs/spec-librarys/machine/RTC.md ================================================ ## machine.RTC **machine.RTC** 类是 machine 模块下面的一个硬件类,用于对指定 RTC 设备的配置和控制,提供对 RTC 设备的操作方法。 - RTC(Real-Time Clock )实时时钟可以提供精确的实时时间,它可以用于产生年、月、日、时、分、秒等信息。 ### 构造函数 在 RT-Thread MicroPython 中 `RTC` 对象的构造函数如下: #### **class machine.RTC**() 所以在给定的总线上构造一个 `RTC` 对象,无入参对象,使用方式可参考 [示例](#_3)。 ### 方法 #### **RTC.init**(datetime) 根据传入的参数初始化 RTC 设备起始时间。入参 `datetime` 为一个时间元组,格式如下: ``` (year, month, day, wday, hour, minute, second, yday) ``` 参数介绍如下所示: - **year**:年份; - **month**:月份,范围 [1, 12]; - **day**:日期,范围 [1, 31]; - **wday**:星期,范围 [0, 6],0 表示星期一,以此类推; - **hour**:小时,范围 [0, 23]; - **minute**:分钟,范围[0, 59]; - **second**:秒,范围[0, 59]; - **yday**:从当前年份 1 月 1 日开始的天数,范围 [0, 365],一般置位 0 未实现。 使用的方式可参考 [示例](#_3)。 #### **RTC.deinit**() 重置 RTC 设备时间到 2015 年 1 月 1日,重新运行 RTC 设备。 #### **RTC.now**() 获取当前时间,返回值为上述 `datetime` 时间元组格式。 ### 示例 ```python >>> from machine import RTC >>> rtc = RTC() # 创建 RTC 设备对象 >>> rtc.init((2019,6,5,2,10,22,30,0)) # 设置初始化时间 >>> rtc.now() # 获取当前时间 (2019, 6, 5, 2, 10, 22, 40, 0) >>> rtc.deinit() # 重置时间到2015年1月1日 >>> rtc.now() # 获取当前时间 (2015, 1, 1, 3, 0, 0, 1, 0) ``` ================================================ FILE: docs/spec-librarys/machine/SPI.md ================================================ ## machine.SPI **machine.SPI** 类是 machine 模块下面的一个硬件类,用于对 SPI 的配置和控制,提供对 SPI 设备的操作方法。 - `SPI` 是一个由主机驱动的同步串行协议。在物理层,总线有三根:`SCK`、`MOSI`、`MISO`。多个设备可以共享同一总线,每个设备都由一个单独的信号 `SS` 来选中,也称片选信号。 - 主机通过片选信号选定一个设备进行通信。`SS` 信号的管理应该由用户代码负责。(通过 [machine.Pin](Pin.md)) ### 构造函数 在 RT-Thread MicroPython 中 `SPI` 对象的构造函数如下: #### **class machine.SPI**(id, ...) 在给定总线上构造一个 `SPI` 对象,`id` 取决于特定的移植。 如果想要使用软件 `SPI` , 即使用引脚模拟 `SPI` 总线,那么初始化的第一个参数需要设置为 `-1` ,可参考 [软件 SPI 示例](#spi) 。 使用硬件 `SPI` 在初始化时只需传入 `SPI` 设备的编号即可,如 '50' 表示 `SPI5` 总线上的第 0 个设备。初始化方式可参考 [硬件 SPI 示例](#spi_1)。 如果没有额外的参数,`SPI` 对象会被创建,但是不会被初始化,如果给出额外的参数,那么总线将被初始化,初始化参数可以参考下面的 `SPI.init` 方法。 ### 方法 #### **SPI.init**(baudrate=1000000, \*, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=None, mosi=None, miso=None) 用给定的参数初始化`SPI`总线: - **baudrate** :`SCK` 时钟频率。 - **polarity** :极性可以是 `0` 或 `1`,是时钟空闲时所处的电平。 - **phase** :相位可以是 `0` 或 `1`,分别在第一个或者第二个时钟边缘采集数据。 - **bits** :每次传输的数据长度,一般是 8 位。 - **firstbit** :传输数据从高位开始还是从低位开始,可以是 `SPI.MSB` 或者 `SPI.LSB`。 - **sck** :用于 `sck` 的 `machine.Pin` 对象。 - **mosi** :用于 `mosi` 的 `machine.Pin` 对象。 - **miso** :用于`miso` 的 `machine.Pin` 对象。 #### **SPI.deinit**() 关闭 `SPI` 总线。 #### **SPI.read**(nbytes, write=0x00) 读出 n 字节的同时不断的写入 `write` 给定的单字节。返回一个存放着读出数据的字节对象。 #### **SPI.readinto**(buf, write=0x00) 读出 n 字节到 `buf` 的同时不断地写入 `write` 给定的单字节。 这个方法返回读入的字节数。 #### **SPI.write**(buf) 写入 `buf` 中包含的字节。返回`None`。 #### **SPI.write_readinto**(write_buf, read_buf) 在读出数据到 `readbuf` 时,从 `writebuf` 中写入数据。缓冲区可以是相同的或不同,但是两个缓冲区必须具有相同的长度。返回 `None`。 ### 常量 #### **SPI.MSB** 设置从高位开始传输数据。 #### **SPI.LSB** 设置从低位开始传输数据。 ### 示例 #### 软件模拟 SPI ``` >>> from machine import Pin, SPI >>> clk = Pin(("clk", 26), Pin.OUT_PP) >>> mosi = Pin(("mosi", 27), Pin.OUT_PP) >>> miso = Pin(("miso", 28), Pin.IN) >>> spi = SPI(-1, 500000, polarity = 0, phase = 0, bits = 8, firstbit = 0, sck = clk, mosi = mosi, miso = miso) >>> print(spi) SoftSPI(baudrate=500000, polarity=0, phase=0, sck=clk, mosi=mosi, miso=miso) >>> spi.write("hello rt-thread!") >>> spi.read(10) b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' ``` #### 硬件 SPI 需要先开启 `SPI` 设备驱动,查找设备可以在 `msh` 中输入`list_device` 命令。 在构造函数的第一个参数传入 `50`,系统就会搜索名为 `spi50` 的设备,找到之后使用这个设备来构建 `SPI` 对象: ``` >>> from machine import SPI >>> spi = SPI(50) >>> print(spi) SPI(device port : spi50) >>> spi.write(b'\x9f') >>> spi.read(5) b'\xff\xff\xff\xff\xff' >>> buf = bytearray(1) >>> spi.write_readinto(b"\x9f",buf) >>> buf bytearray(b'\xef') >>> spi.init(100000,0,0,8,1) # Resetting SPI parameter ``` 更多内容可参考 [machine.SPI](http://docs.micropython.org/en/latest/pyboard/library/machine.SPI.html) 。 ================================================ FILE: docs/spec-librarys/machine/Timer.md ================================================ ## machine.Timer **machine.Timer** 类是 machine 模块下的一个硬件类,用于 Timer 设备的配置和控制,提供对 Timer 设备的操作方法。 - Timer(硬件定时器),是一种用于处理周期性和定时性事件的设备。 - Timer 硬件定时器主要通过内部计数器模块对脉冲信号进行计数,实现周期性设备控制的功能。 - Timer 硬件定时器可以自定义**超时时间**和**超时回调函数**,并且提供两种**定时器模式**: - `ONE_SHOT`:定时器只执行一次设置的回调函数; - `PERIOD`:定时器会周期性执行设置的回调函数; - 打印 Timer 对象会打印出配置的信息。 ### 构造函数 在 RT-Thread MicroPython 中 `Timer` 对象的构造函数如下: #### **class machine.Timer**(id) - **id**:使用的 Timer 设备编号,`id = 1` 表示编号为 1 的 Timer 设备,或者表示使用的 timer 设备名,如 `id = "timer"` 表示设备名为 `timer` 的 Timer 设备; 该函数主要用于通过设备编号创建 Timer 设备对象。 ### 方法 #### **Timer.init**(mode = Timer.PERIODIC, period = 0, callback = None) - **mode**:设置 Timer 定时器模式,可以设置两种模式:`ONE_SHOT`(执行一次)、`PERIOD`(周期性执行),默认设置的模式为 `PERIOD` 模式; - **period**:设置 Timer 定时器定时周期,单位:毫秒(ms) - **callback**:设置 Timer 定义器超时回调函数,默认设置的函数为 None 空函数,设置的函数格式如下所示: ```python def callback_test(device): # 回调函数有且只有一个入参,为创建的 Timer 对象 print("Timer callback test") print(device) # 打印 Timer 对象配置信息 ``` 该函数使用方式如下示例所示: ```python timer.init(wdt.PERIOD, 5000, callback_test) # 设置定时器模式为周期性执行,超时时间为 5 秒, 超时函数为 callback_test ``` #### **Timer.deinit**() 该函数用于停止并关闭 Timer 设备。 ### 常量 下面的常量用来配置 `Timer` 对象。 #### 选择定时器模式: ##### **Timer.PERIODIC** ##### **Timer.ONE_SHOT** ### 示例 ```python >>> from machine import Timer # 从 machine 导入 Timer 类 >>> timer = Timer(15) # 创建 Timer 对象,当前设备编号为 11 >>> # 进入粘贴模式 paste mode; Ctrl-C to cancel, Ctrl-D to finish === def callback_test(device): # 定义超时回调函数 === print("Timer callback test") >>> timer.init(timer.PERIODIC, 5000, callback_test) # 初始化 Timer 对象,设置定时器模式为循环执行,超时时间为 5 秒,超时回调函数 callback_test >>> Timer callback test # 5 秒超时循环执行回调函数,打印日志 >>> Timer callback test >>> Timer callback test >>> timer.init(timer.ONE_SHOT, 5000, callback_test) # 设置定时器模式为只执行一次,超时时间为 5 秒,超时回调函数为 callback_test >>> Timer callback test # 5 秒超时后执行一次回调函数,打印日志 >>> timer.deinit() # 停止并关闭 Timer 定时器 ``` 更多内容可参考 [machine.Timer](http://docs.micropython.org/en/latest/library/machine.Timer.html)。 ================================================ FILE: docs/spec-librarys/machine/UART.md ================================================ ## machine.UART **machine.UART** 类是 machine 模块下面的一个硬件类,用于对 UART 的配置和控制,提供对 UART 设备的操作方法。 `UART` 实现了标准的 `uart/usart` 双工串行通信协议,在物理层上,他由两根数据线组成:`RX` 和 `TX`。通信单元是一个字符,它可以是 8 或 9 位宽。 ### 构造函数 在 RT-Thread MicroPython 中 `UART` 对象的构造函数如下: #### **class machine.UART**(id, ...) 在给定总线上构造一个 `UART` 对象,`id` 取决于特定的移植。 初始化参数可以参考下面的 `UART.init` 方法。 使用硬件 UART 在初始化时只需传入 `UART` 设备的编号即可,如传入 `1` 表示 `uart1` 设备。 初始化方式可参考 [示例](#_3)。 ### 方法 #### **UART.init**(baudrate = 9600, bits=8, parity=None, stop=1) - **baudrate** :`SCK` 时钟频率。 - **bits** :每次发送数据的长度。 - **parity** :校验方式。 - **stop** :停止位的长度。 #### **UART.deinit**() 关闭串口总线。 #### **UART.read**([nbytes]) 读取字符,如果指定读 n 个字节,那么最多读取 n 个字节,否则就会读取尽可能多的数据。 返回值:一个包含读入数据的字节对象。如果如果超时则返回 `None`。 #### **UART.readinto**(buf[, nbytes]) 读取字符到 `buf` 中,如果指定读 n 个字节,那么最多读取 n 个字节,否则就读取尽可能多的数据。另外读取数据的长度不超过 `buf` 的长度。 返回值:读取和存储到 `buf` 中的字节数。如果超时则返回 `None`。 #### **UART.readline**() 读一行数据,以换行符结尾。 返回值:读入的行数,如果超时则返回 `None`。 #### **UART.write**(buf) 将 `buf` 中的数据写入总线。 返回值:写入的字节数,如果超时则返回 `None`。 ### 示例 在构造函数的第一个参数传入`1`,系统就会搜索名为 `uart1` 的设备,找到之后使用这个设备来构建 `UART` 对象: ```python from machine import UART uart = UART(1, 115200) # init with given baudrate uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters uart.read(10) # read 10 characters, returns a bytes object uart.read() # read all available characters uart.readline() # read a line uart.readinto(buf) # read and store into the given buffer uart.write('abc') # write the 3 characters ``` 更多内容可参考 [machine.UART](http://docs.micropython.org/en/latest/pyboard/library/machine.UART.html) 。 ================================================ FILE: docs/spec-librarys/machine/WDT.md ================================================ ## machine.WDT **machine.WDT** 类是 machine 模块下的一个硬件类,用于 WDT 设备的配置和控制,提供对 WDT 设备的操作方法。 如下为 WDT 设备基本介绍: - WDT(WatchDog Timer,硬件看门狗),是一个定时器设备,用于系统程序结束或出错导致系统进入不可恢复状态时重启系统。 - WDT 启动之后,计数器开始计数,在计数器溢出前没有被复位,会对 CPU 产生一个复位信号使设备重启(简称 “被狗咬”); - 系统正常运行时,需要在 WDT 设备允许的时间间隔内对看门狗计数清零(简称“喂狗”),WDT 设备一旦启动,需要定时“喂狗”以确保设备正常运行。 ### 构造函数 在 RT-Thread MicroPython 中 `WDT` 对象的构造函数如下: #### **class machine.WDT**(id = "wdt", timeout=5) - **id**: 使用的 WDT 设备编号,`id = 1` 表示编号为 1 的 WDT 设备,或者表示使用的 WDT 设备名,如 `id = "wdt"` 表示设备名为 `wdt` 的 WDT 设备; - **timeout**:设置看门狗超时时间,单位:秒(s); 用于创建一个 WDT 对象并且启动 WDT 功能。一旦启动,设置的超时时间无法改动,WDT 功能无法停止。 如果该函数入参为空,则设置超时时间为 5 秒;如果入参非空,则使用该入参设置 WDT 超时时间,超时时间最小设置为 1 秒。 ### 方法 #### **WDT.feed**() 用于执行“喂狗”操作,清空看门狗设备计数。应用程序应该合理的周期性调用该函数,以防止系统重启。 ### 示例 ``` python >>> from machine import WDT # 从 machine 导入 WDT 类 >>> wdt = WDT() # 创建 WDT 对象,默认超时时间为 5 秒 >>> wdt = WDT(10) # 创建 WDT 对象,设置超时时间为 10 秒 >>> wdt.feed() # 在 10 秒超时时间内需要执行“喂狗”操作,清空看门狗设备计数,否则系统将重启 ``` 更多内容可参考 [machine.WDT](http://docs.micropython.org/en/latest/library/machine.WDT.html) 。 ================================================ FILE: docs/spec-librarys/machine.md ================================================ ## **machine** – 与硬件相关的功能 **machine** 模块包含与特定开发板上的硬件相关的特定函数。 在这个模块中的大多数功能允许实现直接和不受限制地访问和控制系统上的硬件块(如CPU,定时器,总线等)。如果使用不当,会导致故障,死机,崩溃,在极端的情况下,硬件会损坏。 需要注意的是,由于不同开发板的硬件资源不同,MicroPython 移植所能控制的硬件也是不一样的。因此对于控制硬件的例程来说,在使用前需要修改相关的配置参数来适配不同的开发板,或者直接运行已经对某一开发板适配好的 MicroPython 示例程序。本文档中的例程都是基于 RT-Thread IoT Board 潘多拉开发板而讲解的。 ### 函数 #### 复位相关函数 ##### **machine.info**() 显示关于系统介绍和内存占用等信息。 ##### **machine.reset**() 注:暂未实现 重启设备,类似于按下复位按钮。 ##### **machine.reset_cause**() 注:暂未实现 获得复位的原因,查看可能的返回值的常量。 #### 中断相关函数 ##### **machine.disable_irq**() 禁用中断请求。返回先前的 `IRQ` 状态,该状态应该被认为是一个未知的值。这个返回值应该在 `disable_irq` 函数被调用之前被传给 `enable_irq` 函数来重置中断到初始状态。 ##### **machine.enable_irq**(state) 重新使能中断请求。状态参数应该是从最近一次禁用功能的调用中返回的值。 #### 功耗相关函数 ##### **machine.freq**() 返回 `CPU` 的运行频率。 ##### **machine.idle**() 注:暂未实现 阻断给 `CPU` 的时钟信号,在较短或者较长的周期里减少功耗。当中断发生时,外设将继续工作。 ##### **machine.sleep**() 注:暂未实现 停止 `CPU` 并禁止除了 `WLAN` 之外的所有外设。系统会从睡眠请求的地方重新恢复工作。为了确保唤醒一定会发生,应当首先配置中断源。 ##### **machine.deepsleep**() 注:暂未实现 停止 `CPU` 和所有外设(包括网络接口)。执行从主函数中恢复,就像被复位一样。复位的原因可以检查 `machine.DEEPSLEEP` 参数获得。为了确保唤醒一定会发生,应该首先配置中断源,比如一个引脚的变换或者 `RTC` 的超时。 ================================================ FILE: docs/spec-librarys/micropython.md ================================================ # micropython – 内部功能访问与控制模块 ## Functions ### micropython.const(expr) Used to declare that the expression is a constant so that the compile can optimise it. The use of this function should be as follows: ```python from micropython import const CONST_X = const(123) CONST_Y = const(2 * CONST_X + 1) ``` Constants declared this way are still accessible as global variables from outside the module they are declared in. On the other hand, if a constant begins with an underscore then it is hidden, it is not available as a global variable, and does not take up any memory during execution. This const function is recognised directly by the MicroPython parser and is provided as part of the micropython module mainly so that scripts can be written which run under both CPython and MicroPython, by following the above pattern. ### micropython.opt_level([level]) If level is given then this function sets the optimisation level for subsequent compilation of scripts, and returns None. Otherwise it returns the current optimisation level. - The optimisation level controls the following compilation features: - Assertions: at level 0 assertion statements are enabled and compiled into the bytecode; at levels 1 and higher assertions are not compiled. - Built-in __debug__ variable: at level 0 this variable expands to True; at levels 1 and higher it expands to False. Source-code line numbers: at levels 0, 1 and 2 source-code line number are stored along with the bytecode so that exceptions can report the line number they occurred at; at levels 3 and higher line numbers are not stored. - The default optimisation level is usually level 0. ### micropython.alloc_emergency_exception_buf(size) Allocate size bytes of RAM for the emergency exception buffer (a good size is around 100 bytes). The buffer is used to create exceptions in cases when normal RAM allocation would fail (eg within an interrupt handler) and therefore give useful traceback information in these situations. A good way to use this function is to put it at the start of your main script (eg boot.py or main.py) and then the emergency exception buffer will be active for all the code following it. ### micropython.mem_info([verbose]) Print information about currently used memory. If the verbose argument is given then extra information is printed. The information that is printed is implementation dependent, but currently includes the amount of stack and heap used. In verbose mode it prints out the entire heap indicating which blocks are used and which are free. ### micropython.qstr_info([verbose]) Print information about currently interned strings. If the verbose argument is given then extra information is printed. The information that is printed is implementation dependent, but currently includes the number of interned strings and the amount of RAM they use. In verbose mode it prints out the names of all RAM-interned strings. ### micropython.stack_use() Return an integer representing the current amount of stack that is being used. The absolute value of this is not particularly useful, rather it should be used to compute differences in stack usage at different points. ### micropython.heap_lock() ### micropython.heap_unlock() Lock or unlock the heap. When locked no memory allocation can occur and a MemoryError will be raised if any heap allocation is attempted. These functions can be nested, ie heap_lock() can be called multiple times in a row and the lock-depth will increase, and then heap_unlock() must be called the same number of times to make the heap available again. If the REPL becomes active with the heap locked then it will be forcefully unlocked. ### micropython.kbd_intr(chr) Set the character that will raise a KeyboardInterrupt exception. By default this is set to 3 during script execution, corresponding to Ctrl-C. Passing -1 to this function will disable capture of Ctrl-C, and passing 3 will restore it. This function can be used to prevent the capturing of Ctrl-C on the incoming stream of characters that is usually used for the REPL, in case that stream is used for other purposes. ### micropython.schedule(func, arg) Schedule the function func to be executed “very soon”. The function is passed the value arg as its single argument. “Very soon” means that the MicroPython runtime will do its best to execute the function at the earliest possible time, given that it is also trying to be efficient, and that the following conditions hold: - A scheduled function will never preempt another scheduled function. - Scheduled functions are always executed “between opcodes” which means that all fundamental Python operations (such as appending to a list) are guaranteed to be atomic. - A given port may define “critical regions” within which scheduled functions will never be executed. Functions may be scheduled within a critical region but they will not be executed until that region is exited. An example of a critical region is a preempting interrupt handler (an IRQ). A use for this function is to schedule a callback from a preempting IRQ. Such an IRQ puts restrictions on the code that runs in the IRQ (for example the heap may be locked) and scheduling a function to call later will lift those restrictions. Note: If schedule() is called from a preempting IRQ, when memory allocation is not allowed and the callback to be passed to schedule() is a bound method, passing this directly will fail. This is because creating a reference to a bound method causes memory allocation. A solution is to create a reference to the method in the class constructor and to pass that reference to schedule(). This is discussed in detail here reference documentation under “Creation of Python objects”. There is a finite queue to hold the scheduled functions and schedule() will raise a RuntimeError if the queue is full. ================================================ FILE: docs/spec-librarys/network/wlan.md ================================================ ## class WLAN – 控制内置的 WiFi 网络接口 该类为 WiFi 网络处理器提供一个驱动程序。使用示例: ```python import network # enable station interface and connect to WiFi access point nic = network.WLAN(network.STA_IF) nic.active(True) nic.connect('your-ssid', 'your-password') # now use sockets as usual ``` ### 构造函数 在 RT-Thread MicroPython 中 `WLAN` 对象的构造函数如下: #### class network.WLAN(interface_id) 创建一个 WLAN 网络接口对象。支持的接口是 ` network.STA_IF`(STA 模式,可以连接到上游的 WiFi 热点上) 和 `network.AP_IF`(AP 模式,允许其他 WiFi 客户端连接到自身的热点)。下面方法的可用性取决于接口的类型。例如,只有STA 接口可以使用 `WLAN.connect()` 方法连接到 AP 热点上。 ### 方法 #### **WLAN.active**([is_active]) 如果向该方法传入布尔数值,传入 True 则使能卡,传入 False 则禁止网卡。否则,如果不传入参数,则查询当前网卡的状态。 #### **WLAN.connect**(ssid, password) 使用指定的账号和密码链接指定的无线热点。 #### **WLAN.disconnect**() 从当前链接的无线网络中断开。 #### **WLAN.scan**() 扫描当前可以连接的无线网络。 只能在 STA 模式下进行扫描,使用元组列表的形式返回 WiFi 接入点的相关信息。 (ssid, bssid, channel, rssi, authmode, hidden) #### **WLAN.status**([param]) 返回当前无线连接的状态。 当调用该方法时没有附带参数,就会返回值描述当前网络连接的状态。如果还没有从热点连接中获得 IP 地址,此时的状态为 `STATION_IDLE`。如果已经从连接的无线网络中获得 IP 地址,此时的状态为 `STAT_GOT_IP`。 当调用该函数使用的参数为 `rssi` 时,则返回 `rssi` 的值,该函数目前只支持这一个参数。 #### **WLAN.isconnected**() 在 STA 模式时,如果已经连接到 WiFi 网络,并且获得了 IP 地址,则返回 True。如果处在 AP 模式,此时已经与客户端建立连接,则返回 True。其他情况下都返回 False。 #### WLAN.ifconfig([(ip, subnet, gateway, dns)]) 获取或者设置网络接口的参数,IP 地址,子网掩码,网关,DNS 服务器。当调用该方法不附带参数时,该方法会返回一个包含四个元素的元组来描述上面的信息。想要设置上面的值,传入一个包含上述四个元素的元组,例如: ```python nic.ifconfig(('192.168.0.4', '255.255.255.0', '192.168.0.1', '8.8.8.8')) ``` #### **WLAN.config**('param') #### WLAN.config(param=value, ...) 获取或者设置一般网络接口参数,这些方法允许处理标准的 ip 配置之外的其他参数,如 `WLAN. ifconfig()` 函数处理的参数。这些参数包括特定网络和特定硬件的参数。对于参数的设置,应该使用关键字的语法,可以一次性设置多个参数。 当查询参数时,参数名称的引用应该为字符串,且每次只能查询一个参数。 ```python # Set WiFi access point name (formally known as ESSID) and WiFi password ap.config(essid='My_AP', password="88888888") # Query params one by one print(ap.config('essid')) print(ap.config('channel')) ``` 下面是目前支持的参数: | Parameter | Description | | --------- | --------------------------------- | | mac | MAC address (bytes) | | essid | WiFi access point name (string) | | channel | WiFi channel (integer) | | hidden | Whether ESSID is hidden (boolean) | | password | Access password (string) | ### 示例 STA 模式下: ```python import network wlan = network.WLAN(network.STA_IF) wlan.scan() wlan.connect("rtthread","02188888888") wlan.isconnected() ``` AP 模式下: ```python import network ap = network.WLAN(network.AP_IF) ap.config(essid="hello_rt-thread", password="88888888") ap.active(True) ap.config("essid") ``` ================================================ FILE: docs/spec-librarys/network.md ================================================ ## network – 网络配置 此模块提供网络驱动程序和路由配置。特定硬件的网络驱动程序在此模块中可用,用于配置硬件网络接口。然后,配置接口提供的网络服务可以通过 `usocket` 模块使用。 ### 专用的网络类配置 下面具体的类实现了抽象网卡的接口,并提供了一种控制各种网络接口的方法。 - [class WLAN – control built-in WiFi interfaces](network/wlan.md) ================================================ FILE: docs/spec-librarys/rtthread.md ================================================ ## **rtthread** – 系统相关函数 **rtthread** 模块提供了与 RT-Thread 操作系统相关的功能,如查看栈使用情况等。 ### 函数 #### rtthread.current_tid() 返回当前线程的 id 。 #### rtthread.is_preempt_thread() 返回是否是可抢占线程。 ### 示例 ``` >>> import rtthread >>> >>> rtthread.is_preempt_thread() # determine if it's a preemptible thread True >>> rtthread.current_tid() # current thread id 268464956 >>> ``` ================================================ FILE: docs/std-librarys/_thread.md ================================================ ## _thread – 多线程支持 `_thread` 模块提供了用于处理多线程的基本方法——多个控制线程共享它们的全局数据空间。为了实现同步,提供了简单的锁(也称为互斥锁或二进制信号量)。 ### 示例 ```python import _thread import time def testThread(): while True: print("Hello from thread") time.sleep(2) _thread.start_new_thread(testThread, ()) while True: pass ``` 输出结果(启动新的线程,每隔两秒打印字符): Hello from thread Hello from thread Hello from thread Hello from thread Hello from thread 更多内容可参考 [_thread](http://docs.micropython.org/en/latest/pyboard/library/_thread.html) 。 ================================================ FILE: docs/std-librarys/builtins.md ================================================ # Builtin functions and exceptions 所有的内饰函数和异常类型都在下面描述。 ## 函数和类型 - abs() - all() - any() - bin() - **class** bool - **class** bytearray - **class** bytes - callable() - chr() - classmethod() - compile() - **class** complex - delattr(obj, name) - **class** dict - dir() - divmod() - enumerate() - eval() - exec() - filter() - **class** float - **class** frozenset - getattr() - globals() - hasattr() - hash() - hex() - id() - input() - **class** int - classmethod from_bytes(bytes, byteorder) In MicroPython, byteorder parameter must be positional (this is compatible with CPython). - to_bytes(size, byteorder) In MicroPython, byteorder parameter must be positional (this is compatible with CPython). - isinstance() - issubclass() - iter() - len() - class list - locals() - map() - max() - **class** memoryview - min() - next() - **class** object - oct() - open() - ord() - pow() - print() - property() - range() - repr() - reversed() - round() - class set - setattr() - **class** slice - The slice builtin is the type that slice objects have. - sorted() - staticmethod() - **class** str - sum() - super() - **class** tuple - type() - zip() ## 异常类型 - **exception** AssertionError - **exception** AttributeError - **exception** Exception - **exception** ImportError - **exception** IndexError - **exception** KeyboardInterrupt - **exception** KeyError - **exception** MemoryError - **exception** NameError - **exception** NotImplementedError - **exception** OSError - See CPython documentation: OSError. MicroPython doesn’t implement errno attribute, instead use the standard way to access exception arguments: exc.args[0]. - **exception** RuntimeError - **exception** StopIteration - **exception** SyntaxError - **exception** SystemExit - See CPython documentation: SystemExit. - **exception** TypeError - See CPython documentation: TypeError. - **exception** ValueError - **exception** ZeroDivisionError ================================================ FILE: docs/std-librarys/cmath.md ================================================ ## **cmath** – 复数运算函数功能 `cmath` 模块提供了一些用于复数运算的函数。这个模块中的函数接受整数、浮点数或复数作为参数。他们还将接受任何有复数或浮点方法的 Python 对象:这些方法分别用于将对象转换成复数或浮点数,然后将该函数应用到转换的结果中。 ### 函数 #### **cmath.cos**(z) 返回``z``的余弦。 #### **cmath.exp**(z) 返回``z``的指数。 #### **cmath.log**(z) 返回``z``的对数。 #### **cmath.log10**(z) 返回``z``的常用对数。 #### **cmath.phase**(z) 返回``z``的相位, 范围是(-pi, +pi],以弧度表示。 #### **cmath.polar**(z) 返回``z``的极坐标。 #### **cmath.rect**(r, phi) 返回`模量r`和相位``phi``的复数。 #### **cmath.sin**(z) 返回``z``的正弦。 #### **cmath.sqrt**(z) 返回``z``的平方根。 ### 常数 #### **cmath.e** 自然对数的指数。 #### **cmath.pi** 圆周率。 更多内容可参考 [cmath](http://docs.micropython.org/en/latest/pyboard/library/cmath.html) 。 ================================================ FILE: docs/std-librarys/gc.md ================================================ ## **gc** – 控制垃圾回收 **gc** 模块提供了垃圾收集器的控制接口。 ### 函数 #### **gc.enable**() 允许自动回收内存碎片。 #### **gc.disable**() 禁止自动回收,但可以通过collect()函数进行手动回收内存碎片。 #### **gc.collect**() 运行一次垃圾回收。 #### **gc.mem_alloc**() 返回已分配的内存数量。 #### **gc.mem_free**() 返回剩余的内存数量。 更多内容可参考 [gc](http://docs.micropython.org/en/latest/pyboard/library/gc.html) 。 ================================================ FILE: docs/std-librarys/math.md ================================================ ## **math** – 数学函数 **math** 模块提供了对 C 标准定义的数学函数的访问。 > 注意:本模块需要带有硬件 FPU,精度是32位,这个模块需要浮点功能支持。 ### 常数 #### **math.e** 自然对数的底数。 示例: ``` >>>import math >>>print(math.e) 2.718282 ``` #### **math.pi** 圆周长与直径的比值。 示例: ``` >>> print(math.pi) 3.141593 ``` ### 函数 #### **math.acos(x)** 传入弧度值,计算cos(x)的反三角函数。 #### **math.acosh(x)** 返回 ``x`` 的逆双曲余弦。 #### **math.asin(x)** 传入弧度值,计算sin(x)的反三角函数。 示例: ``` >>> x = math.asin(0.5) >>> print(x) 0.5235988 ``` #### **math.asinh(x)** 返回``x`` 的逆双曲正弦。 #### **math.atan(x)** 返回 ``x`` 的逆切线。 #### **math.atan2(y, x)** Return the principal value of the inverse tangent of y/x. #### **math.atanh(x)** Return the inverse hyperbolic tangent of x. #### **math.ceil(x)** 向上取整。 示例: ``` >>> x = math.ceil(5.6454) >>> print(x) 6 ``` #### **math.copysign(x, y)** Return x with the sign of y. #### **math.cos(x)** 传入弧度值,计算余弦。 示例:计算cos60° ``` >>> math.cos(math.radians(60)) 0.5 ``` #### **math.cosh(x)** Return the hyperbolic cosine of x. #### **math.degrees(x)** 弧度转化为角度。 示例: ``` >>> x = math.degrees(1.047198) >>> print(x) 60.00002 ``` #### **math.erf(x)** Return the error function of x. #### **math.erfc(x)** Return the complementary error function of x. #### **math.exp(x)** 计算e的x次方(幂)。 示例: ``` >>> x = math.exp(2) >>> print(x) 7.389056 ``` #### **math.expm1(x)** 计算 math.exp(x) - 1。 #### **math.fabs(x)** 计算绝对值。 示例: ``` >>> x = math.fabs(-5) >>> print(x) 5.0 >>> y = math.fabs(5.0) >>> print(y) 5.0 ``` #### **math.floor(x)** 向下取整。 示例: ``` >>> x = math.floor(2.99) >>> print(x) 2 >>> y = math.floor(-2.34) >>> print(y) -3 ``` #### **math.fmod(x, y)** 取x除以y的模。 示例: ``` >>> x = math.fmod(4, 5) >>> print(x) 4.0 ``` #### **math.frexp(x)** Decomposes a floating-point number into its mantissa and exponent. The returned value is the tuple (m, e) such that x == m * 2**e exactly. If x == 0 then the function returns (0.0, 0), otherwise the relation 0.5 <= abs(m) < 1 holds. #### **math.gamma(x)** 返回伽马函数。 示例: ``` >>> x = math.gamma(5.21) >>> print(x) 33.08715 ``` #### **math.isfinite(x)** Return True if x is finite. #### **math.isinf(x)** Return True if x is infinite. #### **math.isnan(x)** Return True if x is not-a-number #### **math.ldexp(x, exp)** Return x * (2**exp). #### **math.lgamma(x)** 返回伽马函数的自然对数。 示例: ``` >>> x = math.lgamma(5.21) >>> print(x) 3.499145 ``` #### **math.log(x)** 计算以e为底的x的对数。 示例: ``` >>> x = math.log(10) >>> print(x) 2.302585 ``` #### **math.log10(x)** 计算以10为底的x的对数。 示例: ``` >>> x = math.log10(10) >>> print(x) 1.0 ``` #### **math.log2(x)** 计算以2为底的x的对数。 示例: ``` >>> x = math.log2(8) >>> print(x) 3.0 ``` #### **math.modf(x)** Return a tuple of two floats, being the fractional and integral parts of x. Both return values have the same sign as x. #### **math.pow(x, y)** 计算 x 的 y 次方(幂)。 示例: ``` >>> x = math.pow(2, 3) >>> print(x) 8.0 ``` #### **math.radians(x)** 角度转化为弧度。 示例: ``` >>> x = math.radians(60) >>> print(x) 1.047198 ``` #### **math.sin(x)** 传入弧度值,计算正弦。 示例:计算sin90° ``` >>> math.sin(math.radians(90)) 1.0 ``` #### **math.sinh(x)** Return the hyperbolic sine of x. #### **math.sqrt(x)** 计算平方根。 示例: ``` >>> x = math.sqrt(9) >>> print(x) 3.0 ``` #### **math.tan(x)** 传入弧度值,计算正切。 示例:计算tan60° ``` >>> math.tan(math.radians(60)) 1.732051 ``` #### **math.tanh(x)** Return the hyperbolic tangent of x. #### **math.trunc(x)** 取整。 示例: ``` >>> x = math.trunc(5.12) >>> print(x) 5 >>> y = math.trunc(-6.8) >>> print(y) -6 ``` 更多内容可参考 [math](http://docs.micropython.org/en/latest/pyboard/library/math.html) 。 ================================================ FILE: docs/std-librarys/rtthread.md ================================================ ## **rtthread** – 系统相关函数 **rtthread** 模块提供了与 RT-Thread 操作系统相关的功能,如查看栈使用情况等。 ### 函数 #### rtthread.current_tid() 返回当前线程的 id 。 #### rtthread.is_preempt_thread() 返回是否是可抢占线程。 #### rtthread.stacks_analyze() 返回当前系统线程和栈使用信息。 ### 示例 ``` >>> import rtthread >>> >>> rtthread.is_preempt_thread() # determine if code is running in a preemptible thread True >>> rtthread.current_tid() # current thread id 268464956 >>> rtthread.stacks_analyze() # show thread information thread pri status sp stack size max used left tick error ---------- --- ------- ---------- ---------- ------ ---------- --- elog_async 31 suspend 0x000000a8 0x00000400 26% 0x00000003 000 tshell 20 ready 0x00000260 0x00001000 39% 0x00000003 000 tidle 31 ready 0x00000070 0x00000100 51% 0x0000000f 000 SysMonitor 30 suspend 0x000000a4 0x00000200 32% 0x00000005 000 timer 4 suspend 0x00000080 0x00000200 25% 0x00000009 000 >>> ``` ================================================ FILE: docs/std-librarys/sys.md ================================================ ## **sys** – 系统特有功能函数 **sys** 模块提供系统特有的功能。 ### 函数 #### **sys.exit**(retval=0) 终止当前程序给定的退出代码。 函数会抛出 `SystemExit` 异常。 #### **sys.print_exception**(exc, file=sys.stdout) 打印异常与追踪到一个类似文件的对象 file (或者缺省 `sys.stdout` ). > 提示:这是 CPython 中回溯模块的简化版本。不同于 `traceback.print_exception()`,这个函数用异常值代替了异常类型、异常参数和回溯对象。文件参数在对应位置,不支持更多参数。CPython 兼容回溯模块在 `micropython-lib`。 ### 常数 #### **sys.argv** 当前程序启动时参数的可变列表。 #### **sys.byteorder** 系统字节顺序 (“little” or “big”). #### **sys.implementation** 关于当前 Python 实现的信息,对于 MicroPython 来说,有以下属性: - 名称 - ‘’micropython“ - 版本 - 元组(主要,次要,小),比如(1,9,3) #### **sys.modules** 已加载模块的字典。在一部分移植中,它可能不包含内置模块。 #### **sys.path** 用来搜索导入模块地址的列表。 #### **sys.platform** 返回当前平台的信息。 #### **sys.stderr** 标准错误流。 #### **sys.stdin** 标准输入流。 #### **sys.stdout** 标准输出流。 #### **sys.version** 符合的 Python 语言版本,如字符串。 #### **sys.version_info** 本次实现使用的 Python 语言版本,用一个元组的方式表示。 ### 示例 ``` >>> import sys >>> sys.version '3.4.0' >>> sys.version_info (3, 4, 0) >>> sys.path ['', '/libs/mpy/'] >>> sys.__name__ 'sys' >>> sys.platform 'rt-thread' >>> sys.byteorder 'little' ``` 更多内容可参考 [sys](http://docs.micropython.org/en/latest/pyboard/library/sys.html) 。 ================================================ FILE: docs/std-librarys/uarray.md ================================================ ## **array** – 数字数据数组 **array** 模块定义了一个对象类型,它可以简洁地表示基本值的数组:字符、整数、浮点数。支持代码格式: b, B, h, H, i, I, l, L, q, Q, f, d (最后2个需要支持浮点数)。 ### 构造函数 #### **class array.array**(typecode[, iterable]) 用给定类型的元素创建数组。数组的初始内容由 iterable 提供,如果没有提供,则创建一个空数组。 ``` typecode:数组的类型 iterable:数组初始内容 ``` 示例: ```python >>> import array >>> a = array.array('i', [2, 4, 1, 5]) >>> b = array.array('f') >>> print(a) array('i', [2, 4, 1, 5]) >>> print(b) array('f') ``` ### 方法 #### **array.append**(val) 将一个新元素追加到数组的末尾。 示例: ```python >>> a = array.array('f', [3, 6]) >>> print(a) array('f', [3.0, 6.0]) >>> a.append(7.0) >>> print(a) array('f', [3.0, 6.0, 7.0]) ``` #### **array.extend**(iterable) 将一个新的数组追加到数组的末尾,注意追加的数组和原来数组的数据类型要保持一致。 示例: ```python >>> a = array.array('i', [1, 2, 3]) >>> b = array.array('i', [4, 5]) >>> a.extend(b) >>> print(a) array('i', [1, 2, 3, 4, 5]) ``` 更多内容可参考 [array](http://docs.micropython.org/en/latest/pyboard/library/array.html) 。 ================================================ FILE: docs/std-librarys/ubinascii.md ================================================ ## **ubinascii** – 二进制/ ASCII转换 `ubinascii` 模块包含许多在二进制和各种 ascii 编码的二进制表示之间转换的方法。 ### 函数 #### **ubinascii.hexlify**(data[, sep]) 将字符串转换为十六进制表示的字符串。 示例: ``` >>> ubinascii.hexlify('hello RT-Thread') b'68656c6c6f2052542d546872656164' >>> ubinascii.hexlify('summer') b'73756d6d6572' ``` 如果指定了第二个参数sep,它将用于分隔两个十六进制数。 示例: ``` 如果指定了第二个参数sep,它将用于分隔两个十六进制数。 示例: >>> ubinascii.hexlify('hello RT-Thread'," ") b'68 65 6c 6c 6f 20 52 54 2d 54 68 72 65 61 64' >>> ubinascii.hexlify('hello RT-Thread',",") b'68,65,6c,6c,6f,20,52,54,2d,54,68,72,65,61,64' ``` #### **ubinascii.unhexlify**(data) 转换十六进制字符串为二进制字符串,功能和 hexlify 相反。 示例: ``` >>> ubinascii.unhexlify('73756d6d6572') b'summer' ``` #### **ubinascii.a2b_base64**(data) Base64编码的数据转换为二进制表示。返回字节串。 #### **ubinascii.b2a_base64**(data) 编码base64格式的二进制数据。返回的字符串。 更多内容可参考 [ubinascii](http://docs.micropython.org/en/latest/pyboard/library/ubinascii.html) 。 ================================================ FILE: docs/std-librarys/ucollections.md ================================================ ## **ucollections** – 集合与容器类型 **ucollections** 模块实现了专门的容器数据类型,它提供了 Python 的通用内置容器的替代方案,包括了字典、列表、集合和元组。 ### 类 #### **ucollections.namedtuple**(name, fields) 这是工厂函数创建一个新的 `namedtuple` 型与一个特定的字段名称和集合。`namedtuple` 是元组允许子类要访问它的字段不仅是数字索引,而且还具有属性使用符号字段名访问语法。 字段是字符串序列指定字段名称。为了兼容的实现也可以用空间分隔的字符串命名的字段(但效率较低) 。 代码示例: ```python from ucollections import namedtuple MyTuple = namedtuple("MyTuple", ("id", "name")) t1 = MyTuple(1, "foo") t2 = MyTuple(2, "bar") print(t1.name) assert t2.name == t2[1] ucollections.OrderedDict(...) ``` #### **ucollections.OrderedDict**(...) 字典类型的子类,会记住并保留键/值的追加顺序。当有序的字典被迭代输出时,键/值 会按照他们被添加的顺序返回 : ```python from ucollections import OrderedDict # To make benefit of ordered keys, OrderedDict should be initialized # from sequence of (key, value) pairs. d = OrderedDict([("z", 1), ("a", 2)]) # More items can be added as usual d["w"] = 5 d["b"] = 3 for k, v in d.items(): print(k, v) ``` 输出: z 1 a 2 w 5 b 3 更多的内容可参考 [ucollections](http://docs.micropython.org/en/latest/pyboard/library/ucollections.html) 。 ================================================ FILE: docs/std-librarys/uctypes.md ================================================ ## **uctypes** – 以结构化的方式访问二进制数据 uctypes 模块用来访问二进制数据结构,它提供 C 兼容的数据类型。 ### 常量 - uctypes.LITTLE_ENDIAN — 小端压缩结构。 - uctypes.BIG_ENDIAN — 大端压缩结构类型。 - NATIVE — mricopython 本地的存储类型 ### 构造函数 #### class uctypes.struct(addr, descriptor, type) 将内存中以 c 形式打包的结构体或联合体转换为字典,并返回该字典。 ``` addr:开始转换的地址 descriptor:转换描述符 格式:"field_name":offset|uctypes.UINT32 offset:偏移量, 单位:字节、VOID、UINT8、INT8、UINT16、INT16、UINT32、INT32、UINT64、INT64、BFUINT8、BFINT8、BFUINT16、BFINT16、BFUINT32、BFINT32、BF_POS、BF_LEN、FLOAT32、FLOAT64、PTR、ARRAY type:c 结构体或联合体存储类型,默认为本地存储类型 ``` 示例: ```python >>> a = b"0123" >>> s = uctypes.struct(uctypes.addressof(a), {"a": uctypes.UINT8 | 0, "b": uctypes.UINT16 | 1}, uctypes.LITTLE_ENDIAN) >>> print(s) >>> print(s.a) 48 >>> s.a = 49 >>> print(a) b'1123' ``` ### 方法 #### **uctypes.sizeof**(struct) 按字节返回数据的大小。参数可以是类或者数据对象 (或集合)。 示例: ```python >>> a = b"0123" >>>b = uctypes.struct(uctypes.addressof(a), {"a": uctypes.UINT8 | 0, "b": uctypes.UINT16 | 1}, uctypes.LITTLE_ENDIAN) >>> b.a 48 >>> print(uctypes.sizeof(b)) 3 ``` #### **uctypes.addressof**(obj) 返回对象地址。参数需要是 bytes, bytearray 。 示例: ```python >>> a = b"0123" >>> print(uctypes.addressof(a)) 1073504048 ``` #### **uctypes.bytes_at**(addr, size) 捕捉从 addr 开始到 size 个地址偏移量结束的内存数据为 bytearray 对象并返回。 示例: ```python >>> a = b"0123" >>>print( uctypes.bytes_at(uctypes.addressof(a), 4)) b'0123' ``` #### **uctypes.bytearray_at**(addr, size) 捕捉给定大小和地址内存为 bytearray 对象。与 bytes_at() 函数不同的是,它可以被再次写入,可以访问给定地址的参数。 示例: ```python >>> a = b"0123" >>> print(uctypes.bytearray_at(uctypes.addressof(a), 2)) bytearray(b'01') ``` 更多内容可参考 [uctypes](http://docs.micropython.org/en/latest/pyboard/library/uctypes.html) 。 ================================================ FILE: docs/std-librarys/uerrno.md ================================================ ## **uerrno** – 系统错误码模块 `uerrno` 模块提供了标准的 errno 系统符号,每个符号都有对应的整数值。 ### 示例 ```python try: uos.mkdir("my_dir") except OSError as exc: if exc.args[0] == uerrno.EEXIST: print("Directory already exists") uerrno.errorcode Dictionary mapping numeric error codes to strings with symbolic error code (see above): >>> print(uerrno.errorcode[uerrno.EEXIST]) EEXIST ``` 更多内容可参考 [uerrno](http://docs.micropython.org/en/latest/pyboard/library/uerrno.html) 。 ================================================ FILE: docs/std-librarys/uhashlib.md ================================================ ## **uhashlib** – 哈希算法 `uhashlib` 模块实现了二进制数据哈希算法。 ### 算法功能 #### **SHA256** 当代的散列算法(SHA2系列),它适用于密码安全的目的。被包含在 MicroPython 内核中,除非有特定的代码大小限制,否则推荐任何开发板都支持这个功能。 #### **SHA1** 上一代的算法,不推荐新的应用使用这种算法,但是 SHA1 算法是互联网标准和现有应用程序的一部分,所以针对网络连接便利的开发板会提供这种功能。 #### **MD5** 一种遗留下来的算法,作为密码使用被认为是不安全的。只有特定的开发板,为了兼容老的应用才会提供这种算法。 ### 函数 #### **class uhashlib.sha256**([data]) 创建一个SHA256哈希对象并提供 data 赋值。 #### **class uhashlib.sha1**([data]) 创建一个SHA1哈希对象并提供 data 赋值。 #### **class uhashlib.md5**([data]) 创建一个MD5哈希对象并提供 data 赋值。 #### **hash.update**(data) 将更多二进制数据放入哈希表中。 #### **hash.digest**() 返回字节对象哈希的所有数据。调用此方法后,将无法将更多数据送入哈希。 #### **hash.hexdigest**() 此方法没有实现, 使用 ubinascii.hexlify(hash.digest()) 达到类似效果。 更多内容可参考 [uhashlib](http://docs.micropython.org/en/latest/pyboard/library/uhashlib.html) 。 ================================================ FILE: docs/std-librarys/uheapq.md ================================================ ## **uheapq** – 堆排序算法 `uheapq` 模块提供了堆排序相关算法,堆队列是一个列表,它的元素以特定的方式存储。 ### 函数 #### **uheapq.heappush**(heap, item) 将对象压入堆中。 #### **uheapq.heappop**(heap) 从 heap 弹出第一个元素并返回。 如果是堆时空的会抛出 IndexError。 #### **uheapq.heapify**(x) 将列表 x 转换成堆。 更多内容可参考 [uheapq](http://docs.micropython.org/en/latest/pyboard/library/uheapq.html) 。 ================================================ FILE: docs/std-librarys/uio.md ================================================ ## **uio** – 输入/输出流 **uio** 模块包含流类型 (类似文件) 对象和帮助函数。 ### 函数 #### **uio.open**(name, mode='r', \*\*kwargs) 打开一个文件,关联到内建函数``open()``。所有端口 (用于访问文件系统) 需要支持模式参数,但支持其他参数不同的端口。 ### 类 #### **class uio.FileIO**(...) 这个文件类型用二进制方式打开文件,等于使用``open(name, “rb”)``。 不应直接使用这个实例。 #### **class uio.TextIOWrapper**(...) 这个类型以文本方式打开文件,等同于使用``open(name, “rt”)``不应直接使用这个实例。 #### **class uio.StringIO**([string]) #### **class uio.BytesIO**([string]) 内存文件对象。`StringIO` 用于文本模式 I/O (用 “t” 打开文件),`BytesIO` 用于二进制方式 (用 “b” 方式)。文件对象的初始内容可以用字符串参数指定(`stringio`用普通字符串,`bytesio`用`bytes`对象)。所有的文件方法,如 `read(), write(), seek(), flush(), close()` 都可以用在这些对象上,包括下面方法: #### **getvalue**() 获取缓存区内容。 更多内容可参考 [uio](http://docs.micropython.org/en/latest/pyboard/library/uio.html) 。 ================================================ FILE: docs/std-librarys/ujson.md ================================================ ## **ujson** – JSON编码与解码 `ujson` 模块提供 Python 对象到 JSON(JavaScript Object Notation) 数据格式的转换。 ### 函数 #### **ujson.dumps**(obj) 将 dict 类型转换成 str。 ``` obj:要转换的对象 ``` 示例: ``` >>> obj = {1:2, 3:4, "a":6} >>> print(type(obj), obj) #原来为dict类型 {3: 4, 1: 2, 'a': 6} >>> jsObj = json.dumps(obj) #将dict类型转换成str >>> print(type(jsObj), jsObj) {3: 4, 1: 2, "a": 6} ``` #### **ujson.loads**(str) 解析 JSON 字符串并返回对象。如果字符串格式错误将引发 ValueError 异常。 示例: ``` >>> obj = {1:2, 3:4, "a":6} >>> jsDumps = json.dumps(obj) >>> jsLoads = json.loads(jsDumps) >>> print(type(obj), obj) {3: 4, 1: 2, 'a': 6} >>> print(type(jsDumps), jsDumps) {3: 4, 1: 2, "a": 6} >>> print(type(jsLoads), jsLoads) {'a': 6, 1: 2, 3: 4} ``` 更多内容可参考 [ujson](http://docs.micropython.org/en/latest/pyboard/library/ujson.html) 。 ================================================ FILE: docs/std-librarys/uos.md ================================================ ## **uos** – 基本的操作系统服务 `uos` 模块包含了对文件系统的访问操作,是对应 CPython 模块的一个子集。 ### 函数 #### **uos.chdir**(path) 更改当前目录。 #### **uos.getcwd**() 获取当前目录。 #### **uos.listdir**([dir]) 没有参数就列出当前目录,否则列出给定目录。 #### **uos.mkdir**(path) 创建一个目录。 #### **uos.remove**(path) 删除文件。 #### **uos.rmdir**(path) 删除目录。 #### **uos.rename**(old_path, new_path) 重命名文件或者文件夹。 #### **uos.stat**(path) 获取文件或目录的状态。 #### **uos.sync**() 同步所有的文件系统。 ### 示例 ``` >>> import uos >>> uos. # Tab __name__ uname chdir getcwd listdir mkdir remove rmdir stat unlink mount umount >>> uos.mkdir("rtthread") >>> uos.getcwd() '/' >>> uos.chdir("rtthread") >>> uos.getcwd() '/rtthread' >>> uos.listdir() ['web_root', 'rtthread', '11'] >>> uos.rmdir("11") >>> uos.listdir() ['web_root', 'rtthread'] >>> ``` 更多内容可参考 [uos](http://docs.micropython.org/en/latest/pyboard/library/uos.html) 。 ================================================ FILE: docs/std-librarys/urandom.md ================================================ ## **urandom** - 随机数生成模块 `urandom` 模块实现了伪随机数生成器。 ### 函数 #### **urandom.choice**(obj) 随机生成对象 obj 中的元数。 ``` obj:元数列表 ``` 示例: ```python >>> print(random.choice("DFRobot")) R >>> print(random.choice("DFRobot")) D >>> print(random.choice([0, 2, 4, 3])) 3 >>> print(random.choice([0, 2, 4, 3])) 3 >>> print(random.choice([0, 2, 4, 3])) 2 ``` #### **urandom.getrandbits**(size) 随机生成 0 到 size 个位二进制数范围内的正整数。 比如 : - size = 4,那么便是从 0 到0b1111中随机一个正整数。 - size = 8,那么便是从 0 到 0b11111111中随机一个正整数。 ```python size:位大小 ``` 示例: ```python >>> print( random.getrandbits(1)) #1位二进制位,范围为0~1(十进制:0~1) 1 >>> print(random.getrandbits(1)) 0 >>> print(random.getrandbits(8)) #8位二进制位,范围为0000 0000~1111 11111(十进制:0~255) 224 >>> print(random.getrandbits(8)) 155 ``` #### **urandom.randint**(start, end) 随机生成一个 start 到 end 之间的整数。 ``` start:指定范围内的开始值,包含在范围内 end:指定范围内的结束值,包含在范围内 ``` 示例: ```python >>> import random >>> print(random.randint(1, 4)) 4 >>> print(random.randint(1, 4)) 2 ``` #### **urandom.random**() 随机生成一个 0 到 1 之间的浮点数。 示例: ```python >>> print(random.random()) 0.7111824 >>> print(random.random()) 0.3168149 ``` #### **urandom.randrange**(start, end, step) 随机生成 start 到 end 并且递增为 step 的范围内的正整数。例如,randrange(0, 8, 2)中,随机生成的数为 0、2、4、6 中任一个。 ``` start:指定范围内的开始值,包含在范围内 end:指定范围内的结束值,包含在范围内 step:递增基数 ``` 示例: ```python >>> print(random.randrange(2, 8, 2)) 4 >>> print(random.randrange(2, 8, 2)) 6 >>> print(random.randrange(2, 8, 2)) 2 ``` #### **urandom.seed**(sed) 指定随机数种子,通常和其他随机数生成函数搭配使用。 **注意:** MicroPython 中的随机数其实是一个稳定算法得出的稳定结果序列,而不是一个随机序列。sed 就是这个算法开始计算的第一个值。所以就会出现只要 sed 是一样的,那么后续所有“随机”结果和顺序也都完全一致。 ``` sed:随机数种子 ``` 示例: ```python import random for j in range(0, 2): random.seed(13) #指定随机数种子 for i in range(0, 10): #生成0到10范围内的随机序列 print(random.randint(1, 10)) print("end") ``` 运行结果: ``` 5 2 3 2 3 4 2 5 8 2 end 5 2 3 2 3 4 2 5 8 2 end ``` 从上面可以看到生成两个随机数列表是一样的,你也可以多生成几个随机数列表查看结果。 #### **urandom.uniform**(start, end) 随机生成start到end之间的浮点数。 ``` start:指定范围内的开始值,包含在范围内 stop:指定范围内的结束值,包含在范围内 ``` 示例: ```python >>> print(random.uniform(2, 4)) 2.021441 >>> print(random.uniform(2, 4)) 3.998012 ``` 更多内容可参考 [urandom](https://docs.python.org/3/library/random.html?highlight=random#module-random) 。 ================================================ FILE: docs/std-librarys/ure.md ================================================ ## **ure** – 正则表达式 `ure` 模块用于测试字符串的某个模式,执行正则表达式操作。 ### 匹配字符集 #### 匹配任意字符 ``'.'`` #### 匹配字符集合,支持单个字符和一个范围 ``'[]'`` #### 支持多种匹配元字符 ``'^'`` ``'$'`` ``'?'`` ``'*'`` ``'+'`` ``'??'`` ``'*?'`` ``'+?'`` ``'{m,n}'`` ### 函数 #### **ure.compile**(regex) 编译正则表达式,返回 regex 对象。 #### **ure.match**(regex, string) 用 string 匹配 regex,匹配总是从字符串的开始匹配。 #### **ure.search**(regex, string) 在 string 中搜索 regex。不同于匹配,它搜索第一个匹配位置的正则表达式字符串 (结果可能会是0)。 #### **ure.DEBUG** 标志值,显示表达式的调试信息。 ### **正则表达式对象**: 编译正则表达式,使用 `ure.compile()` 创建实例。 #### **regex.match**(string) #### **regex.search**(string) #### **regex.split**(string, max_split=-1) ### **匹配对象** : 匹配对象是 match() 和 search() 方法的返回值。 #### **match.group**([index]) 只支持数字组。 更多内容可参考 [ure](http://docs.micropython.org/en/latest/pyboard/library/ure.html) 。 ================================================ FILE: docs/std-librarys/uselect.md ================================================ ## **uselect** – 等待流事件 `uselect` 模块提供了等待数据流的事件功能。 ### 常数 #### **select.POLLIN** - 读取可用数据 #### **select.POLLOUT** - 写入更多数据 #### **select.POLLERR** - 发生错误 #### **select.POLLHUP** - 流结束/连接终止检测 ### 函数 #### **select.select**(rlist, wlist, xlist[, timeout]) 监控对象何时可读或可写,一旦监控的对象状态改变,返回结果(阻塞线程)。这个函数是为了兼容,效率不高,推荐用 `poll` 函数 。 ``` rlist:等待读就绪的文件描述符数组 wlist:等待写就绪的文件描述符数组 xlist:等待异常的数组 timeout:等待时间(单位:秒) ``` 示例: ```python def selectTest(): global s rs, ws, es = select.select([s,], [], []) #程序会在此等待直到对象s可读 print(rs) for i in rs: if i == s: print("s can read now") data,addr=s.recvfrom(1024) print('received:',data,'from',addr) ``` ### Poll 类 #### **select.poll**() 创建 poll 实例。 示例: ``` >>>poller = select.poll() >>>print(poller) ``` #### **poll.register**(obj[, eventmask]) 注册一个用以监控的对象,并设置被监控对象的监控标志位 flag。 ``` obj:被监控的对象 flag:被监控的标志 select.POLLIN — 可读 select.POLLHUP — 已挂断 select.POLLERR — 出错 select.POLLOUT — 可写 ``` #### **poll.unregister**(obj) 解除监控的对象的注册。 ``` obj:注册过的对象 ``` 示例: ``` >>>READ_ONLY = select.POLLIN | select.POLLHUP | select.POLLERR >>>READ_WRITE = select.POLLOUT | READ_ONLY >>>poller.register(s, READ_WRITE) >>>poller.unregister(s) ``` #### **poll.modify**(obj, eventmask) 修改已注册的对象监控标志。 ``` obj:已注册的被监控对象 flag:修改为的监控标志 ``` 示例: ``` >>>READ_ONLY = select.POLLIN | select.POLLHUP | select.POLLERR >>>READ_WRITE = select.POLLOUT | READ_ONLY >>>poller.register(s, READ_WRITE) >>>poller.modify(s, READ_ONLY) ``` #### **poll.poll**([timeout]) 等待至少一个已注册的对象准备就绪。返回 (obj, event, ...) 元组, event 元素指定了一个流发生的事件,是上面所描述的 `select.POLL*`常量组合。 根据平台和版本的不同,在元组中可能有其他元素,所以不要假定元组的大小是 2 。如果超时,则返回空列表。 更多内容可参考 [uselect](http://docs.micropython.org/en/latest/pyboard/library/uselect.html) 。 ================================================ FILE: docs/std-librarys/usocket.md ================================================ ## **usocket** – 套接字模块 `usocket` 模块提供对BSD套接字接口的访问。 ### 常数 #### 地址簇 - socket.AF_INET =2 — TCP/IP – IPv4 - socket.AF_INET6 =10 — TCP/IP – IPv6 #### 套接字类型 - socket.SOCK_STREAM =1 — TCP流 - socket.SOCK_DGRAM =2 — UDP数据报 - socket.SOCK_RAW =3 — 原始套接字 - socket.SO_REUSEADDR =4 — socket可重用 #### IP协议号 - socket.IPPROTO_TCP =16 - socket.IPPROTO_UDP =17 #### 套接字选项级别 - socket.SOL_SOCKET =4095 ### 函数 #### **socket.socket** `socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)` 创建新的套接字,使用指定的地址、类型和协议号。 #### **socket.getaddrinfo**(host, port) 将主机域名(host)和端口(port)转换为用于创建套接字的5元组序列。元组列表的结构如下: ``` (family, type, proto, canonname, sockaddr) ``` 示例: ``` >>> info = socket.getaddrinfo("rt-thread.org", 10000) >>> print(info) [(2, 1, 0, '', ('118.31.15.152', 10000))] ``` #### **socket.close**() 关闭套接字。一旦关闭后,套接字所有的功能都将失效。远端将接收不到任何数据 (清理队列数据后)。 虽然在垃圾回收时套接字会自动关闭,但还是推荐在必要时用 close() 去关闭。 #### **socket.bind**(address) 将套接字绑定到地址,套接字不能是已经绑定的。 #### **socket.listen**([backlog]) 监听套接字,使服务器能够接收连接。 ``` backlog:接受套接字的最大个数,至少为0,如果没有指定,则默认一个合理值。 ``` #### **socket.accept**() 接收连接请求。 **注意:** 只能在绑定地址端口号和监听后调用,返回 conn 和 address。 ``` conn:新的套接字对象,可以用来收发消息 address:连接到服务器的客户端地址 ``` #### **socket.connect**(address) 连接服务器。 ``` address:服务器地址和端口号的元组或列表 ``` #### **socket.send**(bytes) 发送数据,并返回成功发送的字节数,返回字节数可能比发送的数据长度少。 ``` bytes:bytes类型数据 ``` #### **socket.recv**(bufsize) 接收数据,返回接收到的数据对象。 ``` bufsize:指定一次接收的最大数据量 ``` 示例: ``` data = conn.recv(1024) ``` #### **socket.sendto**(bytes, address) 发送数据,目标由address决定,常用于UDP通信,返回发送的数据大小。 ``` bytes:bytes类型数据 address:目标地址和端口号的元组 ``` 示例: ``` data = sendto("hello RT-Thread", ("192.168.10.110", 100)) ``` #### **socket.recvfrom**(bufsize) 接收数据,常用于UDP通信,并返回接收到的数据对象和对象的地址。 ``` bufsize:指定一次接收的最大数据量 ``` 示例: ``` data,addr=fd.recvfrom(1024) ``` #### **socket.setsockopt**(level, optname, value) 根据选项值设置套接字。 ``` level:套接字选项级别 optname:套接字的选项 value:可以是一个整数,也可以是一个表示缓冲区的bytes类对象。 ``` 示例: ``` s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ``` #### **socket.settimeout**(value) 设置超时时间,单位:秒。 示例: ``` s.settimeout(2) ``` #### **socket.setblocking**(flag) 设置阻塞或非阻塞模式: 如果 flag 是 false,设置非阻塞模式。 #### **socket.read**([size]) Read up to size bytes from the socket. Return a bytes object. If size is not given, it reads all data available from the socket until EOF; as such the method will not return until the socket is closed. This function tries to read as much data as requested (no “short reads”). This may be not possible with non-blocking socket though, and then less data will be returned. #### **socket.readinto**(buf[, nbytes]) Read bytes into the buf. If nbytes is specified then read at most that many bytes. Otherwise, read at most len(buf) bytes. Just as read(), this method follows “no short reads” policy. Return value: number of bytes read and stored into buf. #### **socket.readline**() 接收一行数据,遇换行符结束,并返回接收数据的对象 。 #### **socket.write**(buf) 将字节类型数据写入套接字,并返回写入成功的数据大小。 ### 示例 #### TCP Server example ``` >>> import usocket >>> s = usocket.socket(usocket.AF_INET,usocket.SOCK_STREAM) # Create STREAM TCP socket >>> s.bind(('192.168.12.32', 6001)) >>> s.listen(5) >>> s.setblocking(True) >>> sock,addr=s.accept() >>> sock.recv(10) b'rt-thread\r' >>> s.close() ``` #### TCP Client example ``` >>> import usocket >>> s = usocket.socket(usocket.AF_INET,usocket.SOCK_STREAM) >>> s.connect(("192.168.10.110",6000)) >>> s.send("micropython") 11 >>> s.close() ``` `connect to a web site example`: ``` s = socket.socket() s.connect(socket.getaddrinfo('www.micropython.org', 80)[0][-1]) ``` 更多的内容可参考 [usocket](http://docs.micropython.org/en/latest/pyboard/library/usocket.html) 。 ================================================ FILE: docs/std-librarys/ussl.md ================================================ # ussl – SSL/TLS 模块 This module implements a subset of the corresponding* [`CPython`](http://docs.micropython.org/en/latest/reference/glossary.html#term-cpython) *module, as described below. For more information, refer to the original CPython documentation: [`ssl`](https://docs.python.org/3.5/library/ssl.html#module-ssl). This module provides access to Transport Layer Security (previously and widely known as “Secure Sockets Layer”) encryption and peer authentication facilities for network sockets, both client-side and server-side. ## 功能函数 - `ussl.wrap_socket`(sock, server_side=False, key=None, cert=None) Takes a [`stream`](http://docs.micropython.org/en/latest/reference/glossary.html#term-stream) *sock* (usually usocket.socket instance of `SOCK_STREAM` type), and returns an instance of ssl.SSLSocket, which wraps the underlying stream in an SSL context. Returned object has the usual [`stream`](http://docs.micropython.org/en/latest/reference/glossary.html#term-stream) interface methods like `read()`, `write()`, etc. In MicroPython, the returned object does not expose socket interface and methods like `recv()`, `send()`. In particular, a server-side SSL socket should be created from a normal socket returned from[`accept()`](http://docs.micropython.org/en/latest/library/usocket.html#usocket.socket.accept) on a non-SSL listening server socket. Depending on the underlying module implementation in a particular [`MicroPython port`](http://docs.micropython.org/en/latest/reference/glossary.html#term-micropython-port), some or all keyword arguments above may be not supported. Warning: Some implementations of `ussl` module do NOT validate server certificates, which makes an SSL connection established prone to man-in-the-middle attacks. ## 异常类型 - `ssl.SSLError` This exception does NOT exist. Instead its base class, OSError, is used. ## 常量 - `ussl.CERT_NONE` - `ussl.CERT_OPTIONAL` - `ussl.CERT_REQUIRED` - Supported values for **cert_reqs** parameter. ================================================ FILE: docs/std-librarys/ustruct.md ================================================ ## **ustruct** – 打包和解包原始数据类型 **ustruct** 模块在 Python 值和以 Python 字节对象表示的 C 结构之间执行转换。 - 支持 size/byte 的前缀: @, <, >, !. - 支持的格式代码: b, B, h, H, i, I, l, L, q, Q, s, P, f, d (最后2个需要支持浮点数). ### 函数 #### **ustruct.calcsize**(fmt) 返回存放某一类型数据 fmt 需要的字节数。 ``` fmt:数据类型 b — 字节型 B — 无符号字节型 h — 短整型 H — 无符号短整型 i — 整型 I — 无符号整型 l — 整型 L — 无符号整型 q — 长整型 Q — 无符号长整型 f — 浮点型 d — 双精度浮点型 P — 无符号型 ``` 示例: ```python >>> print(struct.calcsize("i")) 4 >>> print(struct.calcsize("B")) 1 ``` #### **ustruct.pack**(fmt, v1, v2, ...) 按照格式字符串 fmt 打包参数 v1, v2, ... 。返回值是参数打包后的字节对象。 ``` fmt:同上 ``` 示例: ```python >>> struct.pack("ii", 3, 2) b'\x03\x00\x00\x00\x02\x00\x00\x00' ``` #### **ustruct.unpack**(fmt, data) 从 fmt 中解包数据。返回值是解包后参数的元组。 ``` data:要解压的字节对象 ``` 示例: ```python >>> buf = struct.pack("bb", 1, 2) >>> print(buf) b'\x01\x02' >>> print(struct.unpack("bb", buf)) (1, 2) ``` #### **ustruct.pack_into**(fmt, buffer, offset, v1, v2, ...) 按照格式字符串 fmt 压缩参数 v1, v2, ... 到缓冲区 buffer,开始位置是 offset。当offset 为负数时,从缓冲区末尾开始计数。 #### **ustruct.unpack_from**(fmt, data, offset=0) 以 fmt 作为规则从 data 的 offset 位置开始解包数据,如果 offset 是负数就是从缓冲区末尾开始计算。返回值是解包后的参数元组。 ```python >>> buf = struct.pack("bb", 1, 2) >>> print(struct.unpack("bb", buf)) (1, 2) >>> print(struct.unpack_from("b", buf, 1)) (2,) ``` 更多的内容可参考 [ustruct](http://docs.micropython.org/en/latest/pyboard/library/ustruct.html) 。 ================================================ FILE: docs/std-librarys/utime.md ================================================ ## **utime** – 时间相关函数 **utime** 模块提供获取当前时间和日期、测量时间间隔和延迟的功能。 **初始时刻**: `Unix` 使用 `POSIX` 系统标准,从 1970-01-01 00:00:00 `UTC` 开始。 嵌入式程序从 2000-01-01 00:00:00 `UTC` 开始。 **保持实际日历日期/时间**:需要一个实时时钟 `(RTC)`。在底层系统 (包括一些 `RTOS` 中),`RTC` 已经包含在其中。设置时间是通过 `OS/RTOS` 而不是 MicroPython 完成,查询日期/时间也需要通过系统 `API`。对于裸板系统时钟依赖于 ``machine.RTC()`` 对象。设置时间通过 ``machine.RTC().datetime(tuple)`` 函数,并通过下面方式维持: * 后备电池 (可能是选件、扩展板等)。 * 使用网络时间协议 (需要用户设置)。 * 每次上电时手工设置 (大部分只是在硬复位时需要设置,少部分每次复位都需要设置)。 如果实际时间不是通过系统 / MicroPython RTC 维持,那么下面函数结果可能不是和预期的相同。 ### 函数 #### **utime.localtime**([secs]) 从初始时间的秒转换为元组: (年, 月, 日, 时, 分, 秒, 星期, ``yearday``) 。如果 ``secs`` 是空或者 ``None``,那么使用当前时间。 - `year ` 年份包括世纪(例如2014)。 - `month` 范围 1-12 - `day` 范围 1-31 - `hour` 范围 0-23 - `minute` 范围 0-59 - `second` 范围 0-59 - `weekday` 范围 0-6 对应周一到周日 - `yearday` 范围 1-366 #### **utime.mktime**() 时间的反函数,它的参数是完整8参数的元组,返回值一个整数自2000年1月1日以来的秒数。 #### **utime.sleep**(seconds) 休眠指定的时间(秒),``Seconds`` 可以是浮点数。注意有些版本的 MicroPython不支持浮点数,为了兼容可以使用 ``sleep_ms()`` 和 ``sleep_us()``函数。 #### **utime.sleep_ms**(ms) 延时指定毫秒,参数不能小于0。 #### **utime.sleep_us**(us) 延时指定微秒,参数不能小于0。 #### **utime.ticks_ms**() 返回不断递增的毫秒计数器,在某些值后会重新计数(未指定)。计数值本身无特定意义,只适合用在``ticks_diff()``。 注: 直接在这些值上执行标准数学运算(+,-)或关系运算符(<,>,>,> =)会导致无效结果。执行数学运算然后传递结果作为参数给`ticks_diff()` 或 ` ticks_add() ` 也将导致函数产生无效结果。 #### **utime.ticks_us**() 和上面 `ticks_ms()` 类似,只是返回微秒。 #### **utime.ticks_cpu**() 与 ``ticks_ms()`` 和 ``ticks_us()`` 类似,具有更高精度 (使用 CPU 时钟),并非每个端口都实现此功能。 #### **utime.ticks_add**(ticks, delta) 给定一个数字作为节拍的偏移值 `delta`,这个数字的值是正数或者负数都可以。 给定一个 `ticks` 节拍值,本函数允许根据节拍值的模算数定义来计算给定节拍值之前或者之后 `delta` 个节拍的节拍值 。 `ticks` 参数必须是 `ticks_ms()`, `ticks_us()`, or `ticks_cpu()` 函数的直接返回值。然而,`delta` 可以是一个任意整数或者是数字表达式。`ticks_add` 函数对计算事件/任务的截至时间很有用。(注意:必须使用 `ticksdiff()` 函数来处理 最后期限)。 代码示例: ```python ## 查找 100ms 之前的节拍值 print(utime.ticks_add(utime.ticks_ms(), -100)) ## 计算操作的截止时间然后进行测试 deadline = utime.ticks_add(utime.ticks_ms(), 200) while utime.ticks_diff(deadline, utime.ticks_ms()) > 0: do_a_little_of_something() ## 找出本次移植节拍值的最大值 print(utime.ticks_add(0, -1)) ``` #### **utime.ticks_diff**(ticks1, ticks2) 计算两次调用 `ticksms()`, `ticks_us()`, 或 `ticks_cpu()`之间的时间。因为这些函数的计数值可能会回绕,所以不能直接相减,需要使用 `ticks_diff()` 函数。“旧” 时间需要在 “新” 时间之前,否则结果无法确定。这个函数不要用在计算很长的时间 (因为 `ticks*()` 函数会回绕,通常周期不是很长)。通常用法是在带超时的轮询事件中调用: 代码示例: ```python ## 等待 GPIO 引脚有效,但是最多等待500微秒 start = time.ticks_us() while pin.value() == 0: if time.ticks_diff(time.ticks_us(), start) > 500: raise TimeoutError ``` #### **utime.time**() 返回从开始时间的秒数(整数),假设 `RTC` 已经按照前面方法设置好。如果 `RTC` 没有设置,函数将返回参考点开始计算的秒数 (对于 `RTC` 没有后备电池的板子,上电或复位后的情况)。如果你开发便携版的 MicroPython 应用程序,你不要依赖函数来提供超过秒级的精度。如果需要高精度,使用 `ticks_ms()` 和 `ticks_us()` 函数。如果需要日历时间,使用不带参数的 `localtime()` 是更好选择。 !!! tip "与 CPython 的区别" 在 `CPython` 中,这个函数用浮点数返回从 `Unix` 开始时间(1970-01-01 00:00 `UTC`)的秒数,通常是毫秒级的精度。在 MicroPython 中,只有 `Unix` 版才使用相同开始时间,如果允许浮点精度,将返回亚秒精度。嵌入式硬件通常没有用浮点数表示长时间访问和亚秒精度,所以返回值是整数。一些嵌入式系统硬件不支持 `RTC` 电池供电方式,所以返回的秒数是从最后上电、或相对某个时间、以及特定硬件时间 (如复位)。 ### 示例 ``` >>> import utime >>> utime.sleep(1) # sleep for 1 second >>> utime.sleep_ms(500) # sleep for 500 milliseconds >>> utime.sleep_us(10) # sleep for 10 microseconds >>> start = utime.ticks_ms() # get value of millisecond counter >>> delta = utime.ticks_diff(utime.ticks_ms(), start) # compute time difference >>> delta 6928 >>> print(utime.ticks_add(utime.ticks_ms(), -100)) 1140718 >>> print(utime.ticks_add(0, -1)) 1073741823 ``` 更多内容可参考 [`utime`](http://docs.micropython.org/en/latest/pyboard/library/utime.html#module-utime) 。 ================================================ FILE: docs/std-librarys/uzlib.md ================================================ ## **uzlib** – zlib 解压缩 `uzlib` 模块实现了使用 DEFLATE 算法解压缩二进制数据 (常用的 zlib 库和 gzip 文档)。目前不支持压缩。 ### 函数 #### **uzlib.decompress**(data) 返回解压后的 bytes 数据。 更多内容可参考 [uzlib](http://docs.micropython.org/en/latest/pyboard/library/uzlib.html) 。 ================================================ FILE: docs/tools-mpy-cross.md ================================================ # 生成并运行 .mpy 文件 Python 工程中,.py 文件可以被编译成 .pyc 字节码文件。使用这类预先编译好的二进制文件的优势是一定程度上能够保护源代码,同时可以提高程序的加载速度。在 micropython 中同样地提供了类似的功能,能够将 .py 文件编译成 .mpy 文件,并且能够在 MCU 内直接运行,减少资源消耗的同时提高了程序的加载速度。本篇演示使用潘多拉开发板,以 hello world 工程为例,编译并运行该 demo。 ## 使用 mpy-cross 工具编译 RT-Thread 为开发者提供了便利的开发环境 ,针对 windows 平台提供了可以直接运行的 mpy-cross.exe,无需拖带 Linux 环境,轻量且速度快。 本文将在 windows 环境中,使用软件包中提供的 mpy-cross.exe 软件。 在 RT-Thread/micropython 项目里找到 tools 文件夹,下载 mpy-cross.exe。 这里以 `hellortt.py` 示例: ```python class HelloRtt: def __repr__(self): self.__call__() return "" def __call__(self): print("hello world!!") print("hello RTT") hello = HelloRtt() ``` 首先将需要被编译的 `hellortt.py` 文件拖入到 mpy-cross.exe 所在文件夹中,在按住 shift 键的同时,点击鼠标右键,选择 `在此处打开 Powershell 窗口`( cmd 窗口在这里也是可以使用的)。然后在 Powershell 中,键入 `.\mpy-cross.exe` 后面接上需要编译的工程 `hellortt.py`: ![powershell_mpycross_deploy](assets/powershell_mpycross_deploy.png) 此时如果编译成功将会生成 mpy 文件,上图编译成功后,在文件夹中生成了 `hellortt.mpy` 文件。我们将 `hellortt.mpy` 拷贝到 MCU 的文件系统上,便可以像 .py 文件一样被 import 并调用。 ## 拷贝至 MCU 文件系统中 将生成好的 .mpy 文件,拷贝至 MicroPython IDE 的工程中,并选中它,右键下载到开发板的文件系统中,如图: ![tools-mpy-download](assets/tools-mpy-download.png) 当下载完成之后,便可以在串口命令行中尝试 import 该 mpy 文件,执行该文件的函数,确认能够正常使用: ![mpy-usage-demo](assets/mpy-usage-demo.png) 如果运行时出现 `ValueError: invalid .mpy file` 错误,有可能是 MCU 上 的 micropython 固件与 mpy-cross 版本固件不相符所致,当前提供的 mpy-cross 工具支持 v1.12 版本的固件 。 ================================================ FILE: drivers/bus/qspi.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017-2018 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_DRIVERS_BUS_QSPI_H #define MICROPY_INCLUDED_DRIVERS_BUS_QSPI_H #include "py/mphal.h" enum { MP_QSPI_IOCTL_INIT, MP_QSPI_IOCTL_DEINIT, MP_QSPI_IOCTL_BUS_ACQUIRE, MP_QSPI_IOCTL_BUS_RELEASE, }; typedef struct _mp_qspi_proto_t { int (*ioctl)(void *self, uint32_t cmd); void (*write_cmd_data)(void *self, uint8_t cmd, size_t len, uint32_t data); void (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src); uint32_t (*read_cmd)(void *self, uint8_t cmd, size_t len); void (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest); } mp_qspi_proto_t; typedef struct _mp_soft_qspi_obj_t { mp_hal_pin_obj_t cs; mp_hal_pin_obj_t clk; mp_hal_pin_obj_t io0; mp_hal_pin_obj_t io1; mp_hal_pin_obj_t io2; mp_hal_pin_obj_t io3; } mp_soft_qspi_obj_t; extern const mp_qspi_proto_t mp_soft_qspi_proto; #endif // MICROPY_INCLUDED_DRIVERS_BUS_QSPI_H ================================================ FILE: drivers/bus/softqspi.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017-2018 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 "drivers/bus/qspi.h" #define CS_LOW(self) mp_hal_pin_write(self->cs, 0) #define CS_HIGH(self) mp_hal_pin_write(self->cs, 1) #ifdef MICROPY_HW_SOFTQSPI_SCK_LOW // Use externally provided functions for SCK control and IO reading #define SCK_LOW(self) MICROPY_HW_SOFTQSPI_SCK_LOW(self) #define SCK_HIGH(self) MICROPY_HW_SOFTQSPI_SCK_HIGH(self) #define NIBBLE_READ(self) MICROPY_HW_SOFTQSPI_NIBBLE_READ(self) #else // Use generic pin functions for SCK control and IO reading #define SCK_LOW(self) mp_hal_pin_write(self->clk, 0) #define SCK_HIGH(self) mp_hal_pin_write(self->clk, 1) #define NIBBLE_READ(self) ( \ mp_hal_pin_read(self->io0) \ | (mp_hal_pin_read(self->io1) << 1) \ | (mp_hal_pin_read(self->io2) << 2) \ | (mp_hal_pin_read(self->io3) << 3)) #endif STATIC void nibble_write(mp_soft_qspi_obj_t *self, uint8_t v) { mp_hal_pin_write(self->io0, v & 1); mp_hal_pin_write(self->io1, (v >> 1) & 1); mp_hal_pin_write(self->io2, (v >> 2) & 1); mp_hal_pin_write(self->io3, (v >> 3) & 1); } STATIC int mp_soft_qspi_ioctl(void *self_in, uint32_t cmd) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; switch (cmd) { case MP_QSPI_IOCTL_INIT: mp_hal_pin_high(self->cs); mp_hal_pin_output(self->cs); // Configure pins mp_hal_pin_write(self->clk, 0); mp_hal_pin_output(self->clk); //mp_hal_pin_write(self->clk, 1); mp_hal_pin_output(self->io0); mp_hal_pin_input(self->io1); mp_hal_pin_write(self->io2, 1); mp_hal_pin_output(self->io2); mp_hal_pin_write(self->io3, 1); mp_hal_pin_output(self->io3); break; } return 0; // success } STATIC void mp_soft_qspi_transfer(mp_soft_qspi_obj_t *self, size_t len, const uint8_t *src, uint8_t *dest) { // Will run as fast as possible, limited only by CPU speed and GPIO time mp_hal_pin_input(self->io1); mp_hal_pin_output(self->io0); if (self->io3) { mp_hal_pin_write(self->io2, 1); mp_hal_pin_output(self->io2); mp_hal_pin_write(self->io3, 1); mp_hal_pin_output(self->io3); } if (src) { for (size_t i = 0; i < len; ++i) { uint8_t data_out = src[i]; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { mp_hal_pin_write(self->io0, (data_out >> 7) & 1); mp_hal_pin_write(self->clk, 1); data_in = (data_in << 1) | mp_hal_pin_read(self->io1); mp_hal_pin_write(self->clk, 0); } if (dest != NULL) { dest[i] = data_in; } } } else { for (size_t i = 0; i < len; ++i) { uint8_t data_in = 0; for (int j = 0; j < 8; ++j) { mp_hal_pin_write(self->clk, 1); data_in = (data_in << 1) | mp_hal_pin_read(self->io1); mp_hal_pin_write(self->clk, 0); } if (dest != NULL) { dest[i] = data_in; } } } } STATIC void mp_soft_qspi_qread(mp_soft_qspi_obj_t *self, size_t len, uint8_t *buf) { // Make all IO lines input mp_hal_pin_input(self->io2); mp_hal_pin_input(self->io3); mp_hal_pin_input(self->io0); mp_hal_pin_input(self->io1); // Will run as fast as possible, limited only by CPU speed and GPIO time while (len--) { SCK_HIGH(self); uint8_t data_in = NIBBLE_READ(self); SCK_LOW(self); SCK_HIGH(self); *buf++ = (data_in << 4) | NIBBLE_READ(self); SCK_LOW(self); } } STATIC void mp_soft_qspi_qwrite(mp_soft_qspi_obj_t *self, size_t len, const uint8_t *buf) { // Make all IO lines output mp_hal_pin_output(self->io2); mp_hal_pin_output(self->io3); mp_hal_pin_output(self->io0); mp_hal_pin_output(self->io1); // Will run as fast as possible, limited only by CPU speed and GPIO time for (size_t i = 0; i < len; ++i) { nibble_write(self, buf[i] >> 4); SCK_HIGH(self); SCK_LOW(self); nibble_write(self, buf[i]); SCK_HIGH(self); SCK_LOW(self); } //mp_hal_pin_input(self->io1); } STATIC void mp_soft_qspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; uint32_t cmd_buf = cmd | data << 8; CS_LOW(self); mp_soft_qspi_transfer(self, 1 + len, (uint8_t*)&cmd_buf, NULL); CS_HIGH(self); } STATIC void mp_soft_qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; uint8_t cmd_buf[4] = {cmd, addr >> 16, addr >> 8, addr}; CS_LOW(self); mp_soft_qspi_transfer(self, 4, cmd_buf, NULL); mp_soft_qspi_transfer(self, len, src, NULL); CS_HIGH(self); } STATIC uint32_t mp_soft_qspi_read_cmd(void *self_in, uint8_t cmd, size_t len) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; uint32_t cmd_buf = cmd; CS_LOW(self); mp_soft_qspi_transfer(self, 1 + len, (uint8_t*)&cmd_buf, (uint8_t*)&cmd_buf); CS_HIGH(self); return cmd_buf >> 8; } STATIC void mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; uint8_t cmd_buf[7] = {cmd, addr >> 16, addr >> 8, addr}; CS_LOW(self); mp_soft_qspi_transfer(self, 1, cmd_buf, NULL); mp_soft_qspi_qwrite(self, 6, &cmd_buf[1]); // 3 addr bytes, 1 extra byte (0), 2 dummy bytes (4 dummy cycles) mp_soft_qspi_qread(self, len, dest); CS_HIGH(self); } const mp_qspi_proto_t mp_soft_qspi_proto = { .ioctl = mp_soft_qspi_ioctl, .write_cmd_data = mp_soft_qspi_write_cmd_data, .write_cmd_addr_data = mp_soft_qspi_write_cmd_addr_data, .read_cmd = mp_soft_qspi_read_cmd, .read_cmd_qaddr_qdata = mp_soft_qspi_read_cmd_qaddr_qdata, }; ================================================ FILE: drivers/bus/softspi.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2016-2018 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 "drivers/bus/spi.h" int mp_soft_spi_ioctl(void *self_in, uint32_t cmd) { mp_soft_spi_obj_t *self = (mp_soft_spi_obj_t*)self_in; switch (cmd) { case MP_SPI_IOCTL_INIT: mp_hal_pin_write(self->sck, self->polarity); mp_hal_pin_output(self->sck); mp_hal_pin_output(self->mosi); mp_hal_pin_input(self->miso); break; case MP_SPI_IOCTL_DEINIT: break; } return 0; } void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t *dest) { mp_soft_spi_obj_t *self = (mp_soft_spi_obj_t*)self_in; uint32_t delay_half = self->delay_half; // only MSB transfer is implemented // If a port defines MICROPY_HW_SOFTSPI_MIN_DELAY, and the configured // delay_half is equal to this value, then the software SPI implementation // will run as fast as possible, limited only by CPU speed and GPIO time. #ifdef MICROPY_HW_SOFTSPI_MIN_DELAY if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) { for (size_t i = 0; i < len; ++i) { uint8_t data_out = src[i]; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { mp_hal_pin_write(self->mosi, (data_out >> 7) & 1); mp_hal_pin_write(self->sck, 1 - self->polarity); data_in = (data_in << 1) | mp_hal_pin_read(self->miso); mp_hal_pin_write(self->sck, self->polarity); } if (dest != NULL) { dest[i] = data_in; } } return; } #endif for (size_t i = 0; i < len; ++i) { uint8_t data_out = src[i]; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { mp_hal_pin_write(self->mosi, (data_out >> 7) & 1); if (self->phase == 0) { mp_hal_delay_us_fast(delay_half); mp_hal_pin_write(self->sck, 1 - self->polarity); } else { mp_hal_pin_write(self->sck, 1 - self->polarity); mp_hal_delay_us_fast(delay_half); } data_in = (data_in << 1) | mp_hal_pin_read(self->miso); if (self->phase == 0) { mp_hal_delay_us_fast(delay_half); mp_hal_pin_write(self->sck, self->polarity); } else { mp_hal_pin_write(self->sck, self->polarity); mp_hal_delay_us_fast(delay_half); } } if (dest != NULL) { dest[i] = data_in; } } } const mp_spi_proto_t mp_soft_spi_proto = { .ioctl = mp_soft_spi_ioctl, .transfer = mp_soft_spi_transfer, }; ================================================ FILE: drivers/bus/spi.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2016-2018 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_DRIVERS_BUS_SPI_H #define MICROPY_INCLUDED_DRIVERS_BUS_SPI_H #include "py/mphal.h" enum { MP_SPI_IOCTL_INIT, MP_SPI_IOCTL_DEINIT, }; typedef struct _mp_spi_proto_t { int (*ioctl)(void *self, uint32_t cmd); void (*transfer)(void *self, size_t len, const uint8_t *src, uint8_t *dest); } mp_spi_proto_t; typedef struct _mp_soft_spi_obj_t { uint32_t delay_half; // microsecond delay for half SCK period uint8_t polarity; uint8_t phase; mp_hal_pin_obj_t sck; mp_hal_pin_obj_t mosi; mp_hal_pin_obj_t miso; } mp_soft_spi_obj_t; extern const mp_spi_proto_t mp_soft_spi_proto; int mp_soft_spi_ioctl(void *self, uint32_t cmd); void mp_soft_spi_transfer(void *self, size_t len, const uint8_t *src, uint8_t *dest); #endif // MICROPY_INCLUDED_DRIVERS_BUS_SPI_H ================================================ FILE: extmod/axtls-include/config.h ================================================ /* * Automatically generated header file: don't edit */ #define HAVE_DOT_CONFIG 1 #define CONFIG_PLATFORM_LINUX 1 #undef CONFIG_PLATFORM_CYGWIN #undef CONFIG_PLATFORM_WIN32 /* * General Configuration */ #define PREFIX "/usr/local" #undef CONFIG_DEBUG #undef CONFIG_STRIP_UNWANTED_SECTIONS #undef CONFIG_VISUAL_STUDIO_7_0 #undef CONFIG_VISUAL_STUDIO_8_0 #undef CONFIG_VISUAL_STUDIO_10_0 #define CONFIG_VISUAL_STUDIO_7_0_BASE "" #define CONFIG_VISUAL_STUDIO_8_0_BASE "" #define CONFIG_VISUAL_STUDIO_10_0_BASE "" #define CONFIG_EXTRA_CFLAGS_OPTIONS "" #define CONFIG_EXTRA_LDFLAGS_OPTIONS "" /* * SSL Library */ #undef CONFIG_SSL_SERVER_ONLY #undef CONFIG_SSL_CERT_VERIFICATION #undef CONFIG_SSL_FULL_MODE #define CONFIG_SSL_SKELETON_MODE 1 #define CONFIG_SSL_ENABLE_SERVER 1 #define CONFIG_SSL_ENABLE_CLIENT 1 #undef CONFIG_SSL_DIAGNOSTICS #define CONFIG_SSL_PROT_LOW 1 #undef CONFIG_SSL_PROT_MEDIUM #undef CONFIG_SSL_PROT_HIGH #define CONFIG_SSL_AES 1 #define CONFIG_SSL_USE_DEFAULT_KEY 1 #define CONFIG_SSL_PRIVATE_KEY_LOCATION "" #define CONFIG_SSL_PRIVATE_KEY_PASSWORD "" #define CONFIG_SSL_X509_CERT_LOCATION "" #undef CONFIG_SSL_GENERATE_X509_CERT #define CONFIG_SSL_X509_COMMON_NAME "" #define CONFIG_SSL_X509_ORGANIZATION_NAME "" #define CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME "" #undef CONFIG_SSL_HAS_PEM #undef CONFIG_SSL_USE_PKCS12 #define CONFIG_SSL_EXPIRY_TIME #define CONFIG_X509_MAX_CA_CERTS 0 #define CONFIG_SSL_MAX_CERTS 3 #undef CONFIG_SSL_CTX_MUTEXING #undef CONFIG_USE_DEV_URANDOM #undef CONFIG_WIN32_USE_CRYPTO_LIB #undef CONFIG_OPENSSL_COMPATIBLE #undef CONFIG_PERFORMANCE_TESTING #undef CONFIG_SSL_TEST #undef CONFIG_AXTLSWRAP #undef CONFIG_AXHTTPD #undef CONFIG_HTTP_STATIC_BUILD #define CONFIG_HTTP_PORT #define CONFIG_HTTP_HTTPS_PORT #define CONFIG_HTTP_SESSION_CACHE_SIZE #define CONFIG_HTTP_WEBROOT "" #define CONFIG_HTTP_TIMEOUT #undef CONFIG_HTTP_HAS_CGI #define CONFIG_HTTP_CGI_EXTENSIONS "" #undef CONFIG_HTTP_ENABLE_LUA #define CONFIG_HTTP_LUA_PREFIX "" #undef CONFIG_HTTP_BUILD_LUA #define CONFIG_HTTP_CGI_LAUNCHER "" #undef CONFIG_HTTP_DIRECTORIES #undef CONFIG_HTTP_HAS_AUTHORIZATION #undef CONFIG_HTTP_HAS_IPV6 #undef CONFIG_HTTP_ENABLE_DIFFERENT_USER #define CONFIG_HTTP_USER "" #undef CONFIG_HTTP_VERBOSE #undef CONFIG_HTTP_IS_DAEMON /* * Language Bindings */ #undef CONFIG_BINDINGS #undef CONFIG_CSHARP_BINDINGS #undef CONFIG_VBNET_BINDINGS #define CONFIG_DOT_NET_FRAMEWORK_BASE "" #undef CONFIG_JAVA_BINDINGS #define CONFIG_JAVA_HOME "" #undef CONFIG_PERL_BINDINGS #define CONFIG_PERL_CORE "" #define CONFIG_PERL_LIB "" #undef CONFIG_LUA_BINDINGS #define CONFIG_LUA_CORE "" /* * Samples */ #undef CONFIG_SAMPLES #undef CONFIG_C_SAMPLES #undef CONFIG_CSHARP_SAMPLES #undef CONFIG_VBNET_SAMPLES #undef CONFIG_JAVA_SAMPLES #undef CONFIG_PERL_SAMPLES #undef CONFIG_LUA_SAMPLES #undef CONFIG_BIGINT_CLASSICAL #undef CONFIG_BIGINT_MONTGOMERY #undef CONFIG_BIGINT_BARRETT #undef CONFIG_BIGINT_CRT #undef CONFIG_BIGINT_KARATSUBA #define MUL_KARATSUBA_THRESH #define SQU_KARATSUBA_THRESH #undef CONFIG_BIGINT_SLIDING_WINDOW #undef CONFIG_BIGINT_SQUARE #undef CONFIG_BIGINT_CHECK_ON #undef CONFIG_INTEGER_32BIT #undef CONFIG_INTEGER_16BIT #undef CONFIG_INTEGER_8BIT ================================================ FILE: extmod/axtls-include/version.h ================================================ #define AXTLS_VERSION "(no version)" ================================================ FILE: extmod/crypto-algorithms/sha256.c ================================================ /********************************************************************* * Source: https://github.com/B-Con/crypto-algorithms * Filename: sha256.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: This code is released into the public domain. * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the SHA-256 hashing algorithm. SHA-256 is one of the three algorithms in the SHA2 specification. The others, SHA-384 and SHA-512, are not offered in this implementation. Algorithm specification can be found here: * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf This implementation uses little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include "sha256.h" /****************************** MACROS ******************************/ #define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) #define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) #define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) #define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) #define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) #define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) #define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) /**************************** VARIABLES *****************************/ static const WORD k[64] = { 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 }; /*********************** FUNCTION DEFINITIONS ***********************/ static void sha256_transform(CRYAL_SHA256_CTX *ctx, const BYTE data[]) { WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); for ( ; i < 64; ++i) m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; for (i = 0; i < 64; ++i) { t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; t2 = EP0(a) + MAJ(a,b,c); h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; } ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; } void sha256_init(CRYAL_SHA256_CTX *ctx) { ctx->datalen = 0; ctx->bitlen = 0; ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; } void sha256_update(CRYAL_SHA256_CTX *ctx, const BYTE data[], size_t len) { WORD i; for (i = 0; i < len; ++i) { ctx->data[ctx->datalen] = data[i]; ctx->datalen++; if (ctx->datalen == 64) { sha256_transform(ctx, ctx->data); ctx->bitlen += 512; ctx->datalen = 0; } } } void sha256_final(CRYAL_SHA256_CTX *ctx, BYTE hash[]) { WORD i; i = ctx->datalen; // Pad whatever data is left in the buffer. if (ctx->datalen < 56) { ctx->data[i++] = 0x80; while (i < 56) ctx->data[i++] = 0x00; } else { ctx->data[i++] = 0x80; while (i < 64) ctx->data[i++] = 0x00; sha256_transform(ctx, ctx->data); memset(ctx->data, 0, 56); } // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; sha256_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and SHA uses big endian, // reverse all the bytes when copying the final state to the output hash. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; } } ================================================ FILE: extmod/crypto-algorithms/sha256.h ================================================ /********************************************************************* * Source: https://github.com/B-Con/crypto-algorithms * Filename: sha256.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: This code is released into the public domain. * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding SHA1 implementation. *********************************************************************/ #ifndef SHA256_H #define SHA256_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef struct { BYTE data[64]; WORD datalen; unsigned long long bitlen; WORD state[8]; } CRYAL_SHA256_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void sha256_init(CRYAL_SHA256_CTX *ctx); void sha256_update(CRYAL_SHA256_CTX *ctx, const BYTE data[], size_t len); void sha256_final(CRYAL_SHA256_CTX *ctx, BYTE hash[]); #endif // SHA256_H ================================================ FILE: extmod/lwip-include/arch/cc.h ================================================ #ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H #define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H #include // Generate lwip's internal types from stdint typedef uint8_t u8_t; typedef int8_t s8_t; typedef uint16_t u16_t; typedef int16_t s16_t; typedef uint32_t u32_t; typedef int32_t s32_t; typedef u32_t mem_ptr_t; #define U16_F "hu" #define S16_F "hd" #define X16_F "hx" #define U32_F "u" #define S32_F "d" #define X32_F "x" #define X8_F "02x" #define SZT_F "u" #define BYTE_ORDER LITTLE_ENDIAN #define LWIP_CHKSUM_ALGORITHM 2 #include #define LWIP_PLATFORM_DIAG(x) #define LWIP_PLATFORM_ASSERT(x) { assert(1); } //#define PACK_STRUCT_FIELD(x) x __attribute__((packed)) #define PACK_STRUCT_FIELD(x) x #define PACK_STRUCT_STRUCT __attribute__((packed)) #define PACK_STRUCT_BEGIN #define PACK_STRUCT_END #endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H ================================================ FILE: extmod/lwip-include/arch/perf.h ================================================ #ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H #define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H #define PERF_START /* null definition */ #define PERF_STOP(x) /* null definition */ #endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H ================================================ FILE: extmod/lwip-include/lwipopts.h ================================================ #ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H #define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H #include #include #include // We're running without an OS for this port. We don't provide any services except light protection. #define NO_SYS 1 #define SYS_LIGHTWEIGHT_PROT 1 #include typedef uint32_t sys_prot_t; #define TCP_LISTEN_BACKLOG 1 // We'll put these into a proper ifdef once somebody implements an ethernet driver #define LWIP_ARP 0 #define LWIP_ETHERNET 0 #define LWIP_DNS 1 #define LWIP_NETCONN 0 #define LWIP_SOCKET 0 #ifdef MICROPY_PY_LWIP_SLIP #define LWIP_HAVE_SLIPIF 1 #endif // For now, we can simply define this as a macro for the timer code. But this function isn't // universal and other ports will need to do something else. It may be necessary to move // things like this into a port-provided header file. #define sys_now mp_hal_ticks_ms #endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H ================================================ FILE: extmod/machine_i2c.c ================================================ /* * 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 #include "py/mperrno.h" #include "py/mphal.h" #include "py/runtime.h" #include "extmod/machine_i2c.h" #if MICROPY_PY_MACHINE_I2C typedef mp_machine_soft_i2c_obj_t machine_i2c_obj_t; STATIC void mp_hal_i2c_delay(machine_i2c_obj_t *self) { // We need to use an accurate delay to get acceptable I2C // speeds (eg 1us should be not much more than 1us). mp_hal_delay_us_fast(self->us_delay); } STATIC void mp_hal_i2c_scl_low(machine_i2c_obj_t *self) { mp_hal_pin_od_low(self->scl); } STATIC int mp_hal_i2c_scl_release(machine_i2c_obj_t *self) { uint32_t count = self->us_timeout; mp_hal_pin_od_high(self->scl); mp_hal_i2c_delay(self); // For clock stretching, wait for the SCL pin to be released, with timeout. for (; mp_hal_pin_read(self->scl) == 0 && count; --count) { mp_hal_delay_us_fast(1); } if (count == 0) { return -MP_ETIMEDOUT; } return 0; // success } STATIC void mp_hal_i2c_sda_low(machine_i2c_obj_t *self) { mp_hal_pin_od_low(self->sda); } STATIC void mp_hal_i2c_sda_release(machine_i2c_obj_t *self) { mp_hal_pin_od_high(self->sda); } STATIC int mp_hal_i2c_sda_read(machine_i2c_obj_t *self) { return mp_hal_pin_read(self->sda); } STATIC int mp_hal_i2c_start(machine_i2c_obj_t *self) { mp_hal_i2c_sda_release(self); mp_hal_i2c_delay(self); int ret = mp_hal_i2c_scl_release(self); if (ret != 0) { return ret; } mp_hal_i2c_sda_low(self); mp_hal_i2c_delay(self); return 0; // success } STATIC int mp_hal_i2c_stop(machine_i2c_obj_t *self) { mp_hal_i2c_delay(self); mp_hal_i2c_sda_low(self); mp_hal_i2c_delay(self); int ret = mp_hal_i2c_scl_release(self); mp_hal_i2c_sda_release(self); mp_hal_i2c_delay(self); return ret; } STATIC void mp_hal_i2c_init(machine_i2c_obj_t *self, uint32_t freq) { self->us_delay = 500000 / freq; if (self->us_delay == 0) { self->us_delay = 1; } mp_hal_pin_open_drain(self->scl); mp_hal_pin_open_drain(self->sda); mp_hal_i2c_stop(self); // ignore error } // return value: // 0 - byte written and ack received // 1 - byte written and nack received // <0 - error, with errno being the negative of the return value STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) { mp_hal_i2c_delay(self); mp_hal_i2c_scl_low(self); for (int i = 7; i >= 0; i--) { if ((val >> i) & 1) { mp_hal_i2c_sda_release(self); } else { mp_hal_i2c_sda_low(self); } mp_hal_i2c_delay(self); int ret = mp_hal_i2c_scl_release(self); if (ret != 0) { mp_hal_i2c_sda_release(self); return ret; } mp_hal_i2c_scl_low(self); } mp_hal_i2c_sda_release(self); mp_hal_i2c_delay(self); int ret = mp_hal_i2c_scl_release(self); if (ret != 0) { return ret; } int ack = mp_hal_i2c_sda_read(self); mp_hal_i2c_delay(self); mp_hal_i2c_scl_low(self); return ack; } // return value: // 0 - success // <0 - error, with errno being the negative of the return value STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) { mp_hal_i2c_delay(self); mp_hal_i2c_scl_low(self); mp_hal_i2c_delay(self); uint8_t data = 0; for (int i = 7; i >= 0; i--) { int ret = mp_hal_i2c_scl_release(self); if (ret != 0) { return ret; } data = (data << 1) | mp_hal_i2c_sda_read(self); mp_hal_i2c_scl_low(self); mp_hal_i2c_delay(self); } *val = data; // send ack/nack bit if (!nack) { mp_hal_i2c_sda_low(self); } mp_hal_i2c_delay(self); int ret = mp_hal_i2c_scl_release(self); if (ret != 0) { mp_hal_i2c_sda_release(self); return ret; } mp_hal_i2c_scl_low(self); mp_hal_i2c_sda_release(self); return 0; // success } // return value: // >=0 - success; for read it's 0, for write it's number of acks received // <0 - error, with errno being the negative of the return value int mp_machine_soft_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; // start the I2C transaction int ret = mp_hal_i2c_start(self); if (ret != 0) { return ret; } // write the slave address ret = mp_hal_i2c_write_byte(self, (addr << 1) | (flags & MP_MACHINE_I2C_FLAG_READ)); if (ret < 0) { return ret; } else if (ret != 0) { // nack received, release the bus cleanly mp_hal_i2c_stop(self); return -MP_ENODEV; } int transfer_ret = 0; for (; n--; ++bufs) { size_t len = bufs->len; uint8_t *buf = bufs->buf; if (flags & MP_MACHINE_I2C_FLAG_READ) { // read bytes from the slave into the given buffer(s) while (len--) { ret = mp_hal_i2c_read_byte(self, buf++, (n | len) == 0); if (ret != 0) { return ret; } } } else { // write bytes from the given buffer(s) to the slave while (len--) { ret = mp_hal_i2c_write_byte(self, *buf++); if (ret < 0) { return ret; } else if (ret != 0) { // nack received, stop sending n = 0; break; } ++transfer_ret; // count the number of acks } } } // finish the I2C transaction if (flags & MP_MACHINE_I2C_FLAG_STOP) { ret = mp_hal_i2c_stop(self); if (ret != 0) { return ret; } } return transfer_ret; } /******************************************************************************/ // Generic helper functions // For use by ports that require a single buffer of data for a read/write transfer int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { size_t len; uint8_t *buf; if (n == 1) { // Use given single buffer len = bufs[0].len; buf = bufs[0].buf; } else { // Combine buffers into a single one len = 0; for (size_t i = 0; i < n; ++i) { len += bufs[i].len; } buf = m_new(uint8_t, len); if (!(flags & MP_MACHINE_I2C_FLAG_READ)) { len = 0; for (size_t i = 0; i < n; ++i) { memcpy(buf + len, bufs[i].buf, bufs[i].len); len += bufs[i].len; } } } mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; int ret = i2c_p->transfer_single(self, addr, len, buf, flags); if (n > 1) { if (flags & MP_MACHINE_I2C_FLAG_READ) { // Copy data from single buffer to individual ones len = 0; for (size_t i = 0; i < n; ++i) { memcpy(bufs[i].buf, buf + len, bufs[i].len); len += bufs[i].len; } } m_del(uint8_t, buf, len); } return ret; } STATIC int mp_machine_i2c_readfrom(mp_obj_base_t *self, uint16_t addr, uint8_t *dest, size_t len, bool stop) { mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; mp_machine_i2c_buf_t buf = {.len = len, .buf = dest}; unsigned int flags = MP_MACHINE_I2C_FLAG_READ | (stop ? MP_MACHINE_I2C_FLAG_STOP : 0); return i2c_p->transfer(self, addr, 1, &buf, flags); } STATIC int mp_machine_i2c_writeto(mp_obj_base_t *self, uint16_t addr, const uint8_t *src, size_t len, bool stop) { mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; mp_machine_i2c_buf_t buf = {.len = len, .buf = (uint8_t*)src}; unsigned int flags = stop ? MP_MACHINE_I2C_FLAG_STOP : 0; return i2c_p->transfer(self, addr, 1, &buf, flags); } /******************************************************************************/ // MicroPython bindings for I2C STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 255} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj); self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj); self->us_timeout = args[ARG_timeout].u_int; mp_hal_i2c_init(self, args[ARG_freq].u_int); } STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check the id argument, if given if (n_args > 0) { if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) { #if defined(MICROPY_PY_MACHINE_I2C_MAKE_NEW) // dispatch to port-specific constructor extern mp_obj_t MICROPY_PY_MACHINE_I2C_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); return MICROPY_PY_MACHINE_I2C_MAKE_NEW(type, n_args, n_kw, args); #else mp_raise_ValueError("invalid I2C peripheral"); #endif } --n_args; ++args; } // create new soft I2C object machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t); self->base.type = &machine_i2c_type; mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); machine_i2c_obj_init_helper(self, n_args, args, &kw_args); return MP_OBJ_FROM_PTR(self); } STATIC mp_obj_t machine_i2c_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { machine_i2c_obj_init_helper(MP_OBJ_TO_PTR(args[0]), n_args - 1, args + 1, kw_args); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_init_obj, 1, machine_i2c_obj_init); STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) { mp_obj_base_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t list = mp_obj_new_list(0, NULL); // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved for (int addr = 0x08; addr < 0x78; ++addr) { int ret = mp_machine_i2c_writeto(self, addr, NULL, 0, true); if (ret == 0) { mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr)); } } return list; } MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_scan_obj, machine_i2c_scan); STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; if (i2c_p->start == NULL) { mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); } int ret = i2c_p->start(self); if (ret != 0) { mp_raise_OSError(-ret); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start); STATIC mp_obj_t machine_i2c_stop(mp_obj_t self_in) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; if (i2c_p->stop == NULL) { mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); } int ret = i2c_p->stop(self); if (ret != 0) { mp_raise_OSError(-ret); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_stop_obj, machine_i2c_stop); STATIC mp_obj_t machine_i2c_readinto(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; if (i2c_p->read == NULL) { mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); } // get the buffer to read into mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); // work out if we want to send a nack at the end bool nack = (n_args == 2) ? true : mp_obj_is_true(args[2]); // do the read int ret = i2c_p->read(self, bufinfo.buf, bufinfo.len, nack); if (ret != 0) { mp_raise_OSError(-ret); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readinto_obj, 2, 3, machine_i2c_readinto); STATIC mp_obj_t machine_i2c_write(mp_obj_t self_in, mp_obj_t buf_in) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; if (i2c_p->write == NULL) { mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); } // get the buffer to write from mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); // do the write int ret = i2c_p->write(self, bufinfo.buf, bufinfo.len); if (ret < 0) { mp_raise_OSError(-ret); } // return number of acks received return MP_OBJ_NEW_SMALL_INT(ret); } MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_write_obj, machine_i2c_write); STATIC mp_obj_t machine_i2c_readfrom(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); vstr_t vstr; vstr_init_len(&vstr, mp_obj_get_int(args[2])); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); int ret = mp_machine_i2c_readfrom(self, addr, (uint8_t*)vstr.buf, vstr.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_obj, 3, 4, machine_i2c_readfrom); STATIC mp_obj_t machine_i2c_readfrom_into(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); int ret = mp_machine_i2c_readfrom(self, addr, bufinfo.buf, bufinfo.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_into_obj, 3, 4, machine_i2c_readfrom_into); STATIC mp_obj_t machine_i2c_writeto(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); int ret = mp_machine_i2c_writeto(self, addr, bufinfo.buf, bufinfo.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } // return number of acks received return MP_OBJ_NEW_SMALL_INT(ret); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writeto_obj, 3, 4, machine_i2c_writeto); STATIC mp_obj_t machine_i2c_writevto(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); // Get the list of data buffer(s) to write size_t nitems; const mp_obj_t *items; mp_obj_get_array(args[2], &nitems, (mp_obj_t**)&items); // Get the stop argument bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); // Extract all buffer data, skipping zero-length buffers size_t alloc = nitems == 0 ? 1 : nitems; size_t nbufs = 0; mp_machine_i2c_buf_t *bufs = mp_local_alloc(alloc * sizeof(mp_machine_i2c_buf_t)); for (; nitems--; ++items) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(*items, &bufinfo, MP_BUFFER_READ); if (bufinfo.len > 0) { bufs[nbufs].len = bufinfo.len; bufs[nbufs++].buf = bufinfo.buf; } } // Make sure there is at least one buffer, empty if needed if (nbufs == 0) { bufs[0].len = 0; bufs[0].buf = NULL; nbufs = 1; } // Do the I2C transfer mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; int ret = i2c_p->transfer(self, addr, nbufs, bufs, stop ? MP_MACHINE_I2C_FLAG_STOP : 0); mp_local_free(bufs); if (ret < 0) { mp_raise_OSError(-ret); } // Return number of acks received return MP_OBJ_NEW_SMALL_INT(ret); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writevto_obj, 3, 4, machine_i2c_writevto); STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, uint8_t *buf, size_t len) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); uint8_t memaddr_buf[4]; size_t memaddr_len = 0; for (int16_t i = addrsize - 8; i >= 0; i -= 8) { memaddr_buf[memaddr_len++] = memaddr >> i; } int ret = mp_machine_i2c_writeto(self, addr, memaddr_buf, memaddr_len, false); if (ret != memaddr_len) { // must generate STOP mp_machine_i2c_writeto(self, addr, NULL, 0, true); return ret; } return mp_machine_i2c_readfrom(self, addr, buf, len, true); } STATIC int write_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, const uint8_t *buf, size_t len) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); // Create buffer with memory address size_t memaddr_len = 0; uint8_t memaddr_buf[4]; for (int16_t i = addrsize - 8; i >= 0; i -= 8) { memaddr_buf[memaddr_len++] = memaddr >> i; } // Create partial write buffers mp_machine_i2c_buf_t bufs[2] = { {.len = memaddr_len, .buf = memaddr_buf}, {.len = len, .buf = (uint8_t*)buf}, }; // Do I2C transfer mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; return i2c_p->transfer(self, addr, 2, bufs, MP_MACHINE_I2C_FLAG_STOP); } STATIC const mp_arg_t machine_i2c_mem_allowed_args[] = { { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_arg, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, }; STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_n, ARG_addrsize }; mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); // create the buffer to store data into vstr_t vstr; vstr_init_len(&vstr, mp_obj_get_int(args[ARG_n].u_obj)); // do the transfer int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, (uint8_t*)vstr.buf, vstr.len); if (ret < 0) { mp_raise_OSError(-ret); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom_mem); STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize }; mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); // get the buffer to store data into mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE); // do the transfer int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len); if (ret < 0) { mp_raise_OSError(-ret); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_into_obj, 1, machine_i2c_readfrom_mem_into); STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize }; mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); // get the buffer to write the data from mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ); // do the transfer int ret = write_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len); if (ret < 0) { mp_raise_OSError(-ret); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_writeto_mem_obj, 1, machine_i2c_writeto_mem); STATIC const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2c_init_obj) }, { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&machine_i2c_scan_obj) }, // primitive I2C operations { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&machine_i2c_start_obj) }, { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_i2c_stop_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_i2c_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2c_write_obj) }, // standard bus operations { MP_ROM_QSTR(MP_QSTR_readfrom), MP_ROM_PTR(&machine_i2c_readfrom_obj) }, { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&machine_i2c_readfrom_into_obj) }, { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&machine_i2c_writeto_obj) }, { MP_ROM_QSTR(MP_QSTR_writevto), MP_ROM_PTR(&machine_i2c_writevto_obj) }, // memory operations { MP_ROM_QSTR(MP_QSTR_readfrom_mem), MP_ROM_PTR(&machine_i2c_readfrom_mem_obj) }, { MP_ROM_QSTR(MP_QSTR_readfrom_mem_into), MP_ROM_PTR(&machine_i2c_readfrom_mem_into_obj) }, { MP_ROM_QSTR(MP_QSTR_writeto_mem), MP_ROM_PTR(&machine_i2c_writeto_mem_obj) }, }; MP_DEFINE_CONST_DICT(mp_machine_soft_i2c_locals_dict, machine_i2c_locals_dict_table); int mp_machine_soft_i2c_read(mp_obj_base_t *self_in, uint8_t *dest, size_t len, bool nack) { machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; while (len--) { int ret = mp_hal_i2c_read_byte(self, dest++, nack && (len == 0)); if (ret != 0) { return ret; } } return 0; // success } int mp_machine_soft_i2c_write(mp_obj_base_t *self_in, const uint8_t *src, size_t len) { machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; int num_acks = 0; while (len--) { int ret = mp_hal_i2c_write_byte(self, *src++); if (ret < 0) { return ret; } else if (ret != 0) { // nack received, stop sending break; } ++num_acks; } return num_acks; } STATIC const mp_machine_i2c_p_t mp_machine_soft_i2c_p = { .start = (int(*)(mp_obj_base_t*))mp_hal_i2c_start, .stop = (int(*)(mp_obj_base_t*))mp_hal_i2c_stop, .read = mp_machine_soft_i2c_read, .write = mp_machine_soft_i2c_write, .transfer = mp_machine_soft_i2c_transfer, }; const mp_obj_type_t machine_i2c_type = { { &mp_type_type }, .name = MP_QSTR_I2C, .make_new = machine_i2c_make_new, .protocol = &mp_machine_soft_i2c_p, .locals_dict = (mp_obj_dict_t*)&mp_machine_soft_i2c_locals_dict, }; #endif // MICROPY_PY_MACHINE_I2C ================================================ FILE: extmod/machine_i2c.h ================================================ /* * 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_EXTMOD_MACHINE_I2C_H #define MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H #include "py/obj.h" #define MP_MACHINE_I2C_FLAG_READ (0x01) // if not set then it's a write #define MP_MACHINE_I2C_FLAG_STOP (0x02) typedef struct _mp_machine_i2c_buf_t { size_t len; uint8_t *buf; } mp_machine_i2c_buf_t; // I2C protocol // the first 4 methods can be NULL, meaning operation is not supported // transfer_single only needs to be set if transfer=mp_machine_i2c_transfer_adaptor typedef struct _mp_machine_i2c_p_t { int (*start)(mp_obj_base_t *obj); int (*stop)(mp_obj_base_t *obj); int (*read)(mp_obj_base_t *obj, uint8_t *dest, size_t len, bool nack); int (*write)(mp_obj_base_t *obj, const uint8_t *src, size_t len); int (*transfer)(mp_obj_base_t *obj, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); int (*transfer_single)(mp_obj_base_t *obj, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags); } mp_machine_i2c_p_t; typedef struct _mp_machine_soft_i2c_obj_t { mp_obj_base_t base; uint32_t us_delay; uint32_t us_timeout; mp_hal_pin_obj_t scl; mp_hal_pin_obj_t sda; } mp_machine_soft_i2c_obj_t; extern const mp_obj_type_t machine_i2c_type; extern const mp_obj_dict_t mp_machine_soft_i2c_locals_dict; int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); int mp_machine_soft_i2c_transfer(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); #endif // MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H ================================================ FILE: extmod/machine_mem.c ================================================ /* * 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" #include "extmod/machine_mem.h" #if MICROPY_PY_MACHINE // If you wish to override the functions for mapping the machine_mem read/write // address, then add a #define for MICROPY_MACHINE_MEM_GET_READ_ADDR and/or // MICROPY_MACHINE_MEM_GET_WRITE_ADDR in your mpconfigport.h. Since the // prototypes are identical, it is allowable for both of the macros to evaluate // the to same function. // // It is expected that the modmachine.c file for a given port will provide the // implementations, if the default implementation isn't used. #if !defined(MICROPY_MACHINE_MEM_GET_READ_ADDR) || !defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR) STATIC uintptr_t machine_mem_get_addr(mp_obj_t addr_o, uint align) { uintptr_t addr = mp_obj_int_get_truncated(addr_o); if ((addr & (align - 1)) != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "address %08x is not aligned to %d bytes", addr, align)); } return addr; } #if !defined(MICROPY_MACHINE_MEM_GET_READ_ADDR) #define MICROPY_MACHINE_MEM_GET_READ_ADDR machine_mem_get_addr #endif #if !defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR) #define MICROPY_MACHINE_MEM_GET_WRITE_ADDR machine_mem_get_addr #endif #endif STATIC void machine_mem_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; machine_mem_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "<%u-bit memory>", 8 * self->elem_size); } STATIC mp_obj_t machine_mem_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { // TODO support slice index to read/write multiple values at once machine_mem_obj_t *self = MP_OBJ_TO_PTR(self_in); if (value == MP_OBJ_NULL) { // delete return MP_OBJ_NULL; // op not supported } else if (value == MP_OBJ_SENTINEL) { // load uintptr_t addr = MICROPY_MACHINE_MEM_GET_READ_ADDR(index, self->elem_size); uint32_t val; switch (self->elem_size) { case 1: val = (*(uint8_t*)addr); break; case 2: val = (*(uint16_t*)addr); break; default: val = (*(uint32_t*)addr); break; } return mp_obj_new_int(val); } else { // store uintptr_t addr = MICROPY_MACHINE_MEM_GET_WRITE_ADDR(index, self->elem_size); uint32_t val = mp_obj_get_int_truncated(value); switch (self->elem_size) { case 1: (*(uint8_t*)addr) = val; break; case 2: (*(uint16_t*)addr) = val; break; default: (*(uint32_t*)addr) = val; break; } return mp_const_none; } } const mp_obj_type_t machine_mem_type = { { &mp_type_type }, .name = MP_QSTR_mem, .print = machine_mem_print, .subscr = machine_mem_subscr, }; const machine_mem_obj_t machine_mem8_obj = {{&machine_mem_type}, 1}; const machine_mem_obj_t machine_mem16_obj = {{&machine_mem_type}, 2}; const machine_mem_obj_t machine_mem32_obj = {{&machine_mem_type}, 4}; #endif // MICROPY_PY_MACHINE ================================================ FILE: extmod/machine_mem.h ================================================ /* * 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_EXTMOD_MACHINE_MEM_H #define MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H #include "py/obj.h" typedef struct _machine_mem_obj_t { mp_obj_base_t base; unsigned elem_size; // in bytes } machine_mem_obj_t; extern const mp_obj_type_t machine_mem_type; extern const machine_mem_obj_t machine_mem8_obj; extern const machine_mem_obj_t machine_mem16_obj; extern const machine_mem_obj_t machine_mem32_obj; #if defined(MICROPY_MACHINE_MEM_GET_READ_ADDR) uintptr_t MICROPY_MACHINE_MEM_GET_READ_ADDR(mp_obj_t addr_o, uint align); #endif #if defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR) uintptr_t MICROPY_MACHINE_MEM_GET_WRITE_ADDR(mp_obj_t addr_o, uint align); #endif #endif // MICROPY_INCLUDED_EXTMOD_MACHINE_MEM_H ================================================ FILE: extmod/machine_pinbase.c ================================================ /* * 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 "py/mpconfig.h" #if MICROPY_PY_MACHINE #include "py/obj.h" #include "py/runtime.h" #include "extmod/virtpin.h" #include "extmod/machine_pinbase.h" // PinBase class // As this is abstract class, its instance is null. // But there should be an instance, as the rest of instance code // expects that there will be concrete object for inheritance. typedef struct _mp_pinbase_t { mp_obj_base_t base; } mp_pinbase_t; STATIC const mp_pinbase_t pinbase_singleton = { .base = { &machine_pinbase_type }, }; STATIC mp_obj_t pinbase_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(&pinbase_singleton); } mp_uint_t pinbase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); mp_uint_t pinbase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) { (void)errcode; switch (request) { case MP_PIN_READ: { mp_obj_t dest[2]; mp_load_method(obj, MP_QSTR_value, dest); return mp_obj_get_int(mp_call_method_n_kw(0, 0, dest)); } case MP_PIN_WRITE: { mp_obj_t dest[3]; mp_load_method(obj, MP_QSTR_value, dest); dest[2] = (arg == 0 ? mp_const_false : mp_const_true); mp_call_method_n_kw(1, 0, dest); return 0; } } return -1; } STATIC const mp_pin_p_t pinbase_pin_p = { .ioctl = pinbase_ioctl, }; const mp_obj_type_t machine_pinbase_type = { { &mp_type_type }, .name = MP_QSTR_PinBase, .make_new = pinbase_make_new, .protocol = &pinbase_pin_p, }; #endif // MICROPY_PY_MACHINE ================================================ FILE: extmod/machine_pinbase.h ================================================ /* * 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_EXTMOD_MACHINE_PINBASE_H #define MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H #include "py/obj.h" extern const mp_obj_type_t machine_pinbase_type; #endif // MICROPY_INCLUDED_EXTMOD_MACHINE_PINBASE_H ================================================ FILE: extmod/machine_pulse.c ================================================ /* * 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 "py/runtime.h" #include "py/mperrno.h" #include "extmod/machine_pulse.h" #if MICROPY_PY_MACHINE_PULSE MP_WEAK mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) { mp_uint_t start = mp_hal_ticks_us(); while (mp_hal_pin_read(pin) != pulse_level) { if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) { return (mp_uint_t)-2; } } start = mp_hal_ticks_us(); while (mp_hal_pin_read(pin) == pulse_level) { if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) { return (mp_uint_t)-1; } } return mp_hal_ticks_us() - start; } STATIC mp_obj_t machine_time_pulse_us_(size_t n_args, const mp_obj_t *args) { mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(args[0]); int level = 0; if (mp_obj_is_true(args[1])) { level = 1; } mp_uint_t timeout_us = 1000000; if (n_args > 2) { timeout_us = mp_obj_get_int(args[2]); } mp_uint_t us = machine_time_pulse_us(pin, level, timeout_us); // May return -1 or -2 in case of timeout return mp_obj_new_int(us); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj, 2, 3, machine_time_pulse_us_); #endif ================================================ FILE: extmod/machine_pulse.h ================================================ /* * 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_EXTMOD_MACHINE_PULSE_H #define MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H #include "py/obj.h" #include "py/mphal.h" mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj); #endif // MICROPY_INCLUDED_EXTMOD_MACHINE_PULSE_H ================================================ FILE: extmod/machine_signal.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 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/mpconfig.h" #if MICROPY_PY_MACHINE #include #include "py/obj.h" #include "py/runtime.h" #include "extmod/virtpin.h" #include "extmod/machine_signal.h" // Signal class typedef struct _machine_signal_t { mp_obj_base_t base; mp_obj_t pin; bool invert; } machine_signal_t; STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_obj_t pin; bool invert = false; #if defined(MICROPY_PY_MACHINE_PIN_MAKE_NEW) mp_pin_p_t *pin_p = NULL; if (n_args > 0 && mp_obj_is_obj(args[0])) { mp_obj_base_t *pin_base = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); pin_p = (mp_pin_p_t*)pin_base->type->protocol; } if (pin_p == NULL) { // If first argument isn't a Pin-like object, we filter out "invert" // from keyword arguments and pass them all to the exported Pin // constructor to create one. mp_obj_t *pin_args = mp_local_alloc((n_args + n_kw * 2) * sizeof(mp_obj_t)); memcpy(pin_args, args, n_args * sizeof(mp_obj_t)); const mp_obj_t *src = args + n_args; mp_obj_t *dst = pin_args + n_args; mp_obj_t *sig_value = NULL; for (size_t cnt = n_kw; cnt; cnt--) { if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) { invert = mp_obj_is_true(src[1]); n_kw--; } else { *dst++ = *src; *dst++ = src[1]; } if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_value)) { // Value is pertained to Signal, so we should invert // it for Pin if needed, and we should do it only when // inversion status is guaranteedly known. sig_value = dst - 1; } src += 2; } if (invert && sig_value != NULL) { *sig_value = mp_obj_is_true(*sig_value) ? MP_OBJ_NEW_SMALL_INT(0) : MP_OBJ_NEW_SMALL_INT(1); } // Here we pass NULL as a type, hoping that mp_pin_make_new() // will just ignore it as set a concrete type. If not, we'd need // to expose port's "default" pin type too. pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args); mp_local_free(pin_args); } else #endif // Otherwise there should be 1 or 2 args { if (n_args == 1) { pin = args[0]; if (n_kw == 0) { } else if (n_kw == 1 && args[1] == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) { invert = mp_obj_is_true(args[2]); } else { goto error; } } else { error: mp_raise_TypeError(NULL); } } machine_signal_t *o = m_new_obj(machine_signal_t); o->base.type = type; o->pin = pin; o->invert = invert; return MP_OBJ_FROM_PTR(o); } STATIC mp_uint_t signal_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { (void)errcode; machine_signal_t *self = MP_OBJ_TO_PTR(self_in); switch (request) { case MP_PIN_READ: { return mp_virtual_pin_read(self->pin) ^ self->invert; } case MP_PIN_WRITE: { mp_virtual_pin_write(self->pin, arg ^ self->invert); return 0; } } return -1; } // fast method for getting/setting signal value STATIC mp_obj_t signal_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, 1, false); if (n_args == 0) { // get pin return MP_OBJ_NEW_SMALL_INT(mp_virtual_pin_read(self_in)); } else { // set pin mp_virtual_pin_write(self_in, mp_obj_is_true(args[0])); return mp_const_none; } } STATIC mp_obj_t signal_value(size_t n_args, const mp_obj_t *args) { return signal_call(args[0], n_args - 1, 0, args + 1); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_value_obj, 1, 2, signal_value); STATIC mp_obj_t signal_on(mp_obj_t self_in) { mp_virtual_pin_write(self_in, 1); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_on_obj, signal_on); STATIC mp_obj_t signal_off(mp_obj_t self_in) { mp_virtual_pin_write(self_in, 0); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_off_obj, signal_off); STATIC const mp_rom_map_elem_t signal_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&signal_value_obj) }, { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&signal_on_obj) }, { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&signal_off_obj) }, }; STATIC MP_DEFINE_CONST_DICT(signal_locals_dict, signal_locals_dict_table); STATIC const mp_pin_p_t signal_pin_p = { .ioctl = signal_ioctl, }; const mp_obj_type_t machine_signal_type = { { &mp_type_type }, .name = MP_QSTR_Signal, .make_new = signal_make_new, .call = signal_call, .protocol = &signal_pin_p, .locals_dict = (void*)&signal_locals_dict, }; #endif // MICROPY_PY_MACHINE ================================================ FILE: extmod/machine_signal.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 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_EXTMOD_MACHINE_SIGNAL_H #define MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H #include "py/obj.h" extern const mp_obj_type_t machine_signal_type; #endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H ================================================ FILE: extmod/machine_spi.c ================================================ /* * 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" #include "extmod/machine_spi.h" #if MICROPY_PY_MACHINE_SPI // if a port didn't define MSB/LSB constants then provide them #ifndef MICROPY_PY_MACHINE_SPI_MSB #define MICROPY_PY_MACHINE_SPI_MSB (0) #define MICROPY_PY_MACHINE_SPI_LSB (1) #endif /******************************************************************************/ // MicroPython bindings for generic machine.SPI STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check the id argument, if given if (n_args > 0) { if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) { #if defined(MICROPY_PY_MACHINE_SPI_MAKE_NEW) // dispatch to port-specific constructor extern mp_obj_t MICROPY_PY_MACHINE_SPI_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); return MICROPY_PY_MACHINE_SPI_MAKE_NEW(type, n_args, n_kw, args); #else mp_raise_ValueError("invalid SPI peripheral"); #endif } --n_args; ++args; } // software SPI return mp_machine_soft_spi_make_new(type, n_args, n_kw, args); } STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; spi_p->init(s, n_args - 1, args + 1, kw_args); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init); STATIC mp_obj_t machine_spi_deinit(mp_obj_t self) { mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self); mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; if (spi_p->deinit != NULL) { spi_p->deinit(s); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit); STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) { mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self); mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; spi_p->transfer(s, len, src, dest); } STATIC mp_obj_t mp_machine_spi_read(size_t n_args, const mp_obj_t *args) { vstr_t vstr; vstr_init_len(&vstr, mp_obj_get_int(args[1])); memset(vstr.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, vstr.len); mp_machine_spi_transfer(args[0], vstr.len, vstr.buf, vstr.buf); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_spi_read); STATIC mp_obj_t mp_machine_spi_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); memset(bufinfo.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, bufinfo.len); mp_machine_spi_transfer(args[0], bufinfo.len, bufinfo.buf, bufinfo.buf); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto); STATIC mp_obj_t mp_machine_spi_write(mp_obj_t self, mp_obj_t wr_buf) { mp_buffer_info_t src; mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ); mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, NULL); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj, mp_machine_spi_write); STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp_obj_t rd_buf) { mp_buffer_info_t src; mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ); mp_buffer_info_t dest; mp_get_buffer_raise(rd_buf, &dest, MP_BUFFER_WRITE); if (src.len != dest.len) { mp_raise_ValueError("buffers must be the same length"); } mp_machine_spi_transfer(self, src.len, src.buf, dest.buf); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto); STATIC const mp_rom_map_elem_t machine_spi_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_spi_init_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_spi_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_machine_spi_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_machine_spi_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_MSB) }, { MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_LSB) }, }; MP_DEFINE_CONST_DICT(mp_machine_spi_locals_dict, machine_spi_locals_dict_table); /******************************************************************************/ // Implementation of soft SPI STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) { #ifdef MICROPY_HW_SOFTSPI_MIN_DELAY if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) { return MICROPY_HW_SOFTSPI_MAX_BAUDRATE; } else #endif { return 500000 / delay_half; } } STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) { #ifdef MICROPY_HW_SOFTSPI_MIN_DELAY if (baudrate >= MICROPY_HW_SOFTSPI_MAX_BAUDRATE) { return MICROPY_HW_SOFTSPI_MIN_DELAY; } else #endif { uint32_t delay_half = 500000 / baudrate; // round delay_half up so that: actual_baudrate <= requested_baudrate if (500000 % baudrate != 0) { delay_half += 1; } return delay_half; } } STATIC void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u," " sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")", baudrate_from_delay_half(self->spi.delay_half), self->spi.polarity, self->spi.phase, mp_hal_pin_name(self->spi.sck), mp_hal_pin_name(self->spi.mosi), mp_hal_pin_name(self->spi.miso)); } STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} }, { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // create new object mp_machine_soft_spi_obj_t *self = m_new_obj(mp_machine_soft_spi_obj_t); self->base.type = &mp_machine_soft_spi_type; // set parameters self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int); self->spi.polarity = args[ARG_polarity].u_int; self->spi.phase = args[ARG_phase].u_int; if (args[ARG_bits].u_int != 8) { mp_raise_ValueError("bits must be 8"); } if (args[ARG_firstbit].u_int != MICROPY_PY_MACHINE_SPI_MSB) { mp_raise_ValueError("firstbit must be MSB"); } if (args[ARG_sck].u_obj == MP_OBJ_NULL || args[ARG_mosi].u_obj == MP_OBJ_NULL || args[ARG_miso].u_obj == MP_OBJ_NULL) { mp_raise_ValueError("must specify all of sck/mosi/miso"); } self->spi.sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj); self->spi.mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj); self->spi.miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj); // configure bus mp_soft_spi_ioctl(&self->spi, MP_SPI_IOCTL_INIT); return MP_OBJ_FROM_PTR(self); } STATIC void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in; enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_polarity, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_phase, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if (args[ARG_baudrate].u_int != -1) { self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int); } if (args[ARG_polarity].u_int != -1) { self->spi.polarity = args[ARG_polarity].u_int; } if (args[ARG_phase].u_int != -1) { self->spi.phase = args[ARG_phase].u_int; } if (args[ARG_sck].u_obj != MP_OBJ_NULL) { self->spi.sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj); } if (args[ARG_mosi].u_obj != MP_OBJ_NULL) { self->spi.mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj); } if (args[ARG_miso].u_obj != MP_OBJ_NULL) { self->spi.miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj); } // configure bus mp_soft_spi_ioctl(&self->spi, MP_SPI_IOCTL_INIT); } STATIC void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in; mp_soft_spi_transfer(&self->spi, len, src, dest); } const mp_machine_spi_p_t mp_machine_soft_spi_p = { .init = mp_machine_soft_spi_init, .deinit = NULL, .transfer = mp_machine_soft_spi_transfer, }; const mp_obj_type_t mp_machine_soft_spi_type = { { &mp_type_type }, .name = MP_QSTR_SoftSPI, .print = mp_machine_soft_spi_print, .make_new = mp_machine_spi_make_new, // delegate to master constructor .protocol = &mp_machine_soft_spi_p, .locals_dict = (mp_obj_dict_t*)&mp_machine_spi_locals_dict, }; #endif // MICROPY_PY_MACHINE_SPI ================================================ FILE: extmod/machine_spi.h ================================================ /* * 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_EXTMOD_MACHINE_SPI_H #define MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H #include "py/obj.h" #include "py/mphal.h" #include "drivers/bus/spi.h" // SPI protocol typedef struct _mp_machine_spi_p_t { void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); void (*deinit)(mp_obj_base_t *obj); // can be NULL void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest); } mp_machine_spi_p_t; typedef struct _mp_machine_soft_spi_obj_t { mp_obj_base_t base; mp_soft_spi_obj_t spi; } mp_machine_soft_spi_obj_t; extern const mp_machine_spi_p_t mp_machine_soft_spi_p; extern const mp_obj_type_t mp_machine_soft_spi_type; extern const mp_obj_dict_t mp_machine_spi_locals_dict; mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj); MP_DECLARE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj); #endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H ================================================ FILE: extmod/misc.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2014-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_MISC_H #define MICROPY_INCLUDED_EXTMOD_MISC_H // This file contains cumulative declarations for extmod/ . #include #include "py/runtime.h" MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj); #if MICROPY_PY_OS_DUPTERM bool mp_uos_dupterm_is_builtin_stream(mp_const_obj_t stream); uintptr_t mp_uos_dupterm_poll(uintptr_t poll_flags); int mp_uos_dupterm_rx_chr(void); void mp_uos_dupterm_tx_strn(const char *str, size_t len); void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc); #else #define mp_uos_dupterm_tx_strn(s, l) #endif #endif // MICROPY_INCLUDED_EXTMOD_MISC_H ================================================ FILE: extmod/modbtree.c ================================================ /* * 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 // for declaration of global errno variable #include #include "py/runtime.h" #include "py/stream.h" #if MICROPY_PY_BTREE #include #include <../../btree/btree.h> typedef struct _mp_obj_btree_t { mp_obj_base_t base; DB *db; mp_obj_t start_key; mp_obj_t end_key; #define FLAG_END_KEY_INCL 1 #define FLAG_DESC 2 #define FLAG_ITER_TYPE_MASK 0xc0 #define FLAG_ITER_KEYS 0x40 #define FLAG_ITER_VALUES 0x80 #define FLAG_ITER_ITEMS 0xc0 byte flags; byte next_flags; } mp_obj_btree_t; #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_obj_type_t btree_type; #endif #define CHECK_ERROR(res) \ if (res == RET_ERROR) { \ mp_raise_OSError(errno); \ } void __dbpanic(DB *db) { mp_printf(&mp_plat_print, "__dbpanic(%p)\n", db); } STATIC mp_obj_btree_t *btree_new(DB *db) { mp_obj_btree_t *o = m_new_obj(mp_obj_btree_t); o->base.type = &btree_type; o->db = db; o->start_key = mp_const_none; o->end_key = mp_const_none; o->next_flags = 0; return o; } STATIC void btree_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", self->db); } STATIC mp_obj_t btree_flush(mp_obj_t self_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); return MP_OBJ_NEW_SMALL_INT(__bt_sync(self->db, 0)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(btree_flush_obj, btree_flush); STATIC mp_obj_t btree_close(mp_obj_t self_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); return MP_OBJ_NEW_SMALL_INT(__bt_close(self->db)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(btree_close_obj, btree_close); STATIC mp_obj_t btree_put(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]); DBT key, val; key.data = (void*)mp_obj_str_get_data(args[1], &key.size); val.data = (void*)mp_obj_str_get_data(args[2], &val.size); return MP_OBJ_NEW_SMALL_INT(__bt_put(self->db, &key, &val, 0)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_put_obj, 3, 4, btree_put); STATIC mp_obj_t btree_get(size_t n_args, const mp_obj_t *args) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]); DBT key, val; key.data = (void*)mp_obj_str_get_data(args[1], &key.size); int res = __bt_get(self->db, &key, &val, 0); if (res == RET_SPECIAL) { if (n_args > 2) { return args[2]; } else { return mp_const_none; } } CHECK_ERROR(res); return mp_obj_new_bytes(val.data, val.size); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_get_obj, 2, 3, btree_get); STATIC mp_obj_t btree_seq(size_t n_args, const mp_obj_t *args) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]); int flags = MP_OBJ_SMALL_INT_VALUE(args[1]); DBT key, val; if (n_args > 2) { key.data = (void*)mp_obj_str_get_data(args[2], &key.size); } int res = __bt_seq(self->db, &key, &val, flags); CHECK_ERROR(res); if (res == RET_SPECIAL) { return mp_const_none; } mp_obj_t pair_o = mp_obj_new_tuple(2, NULL); mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(pair_o); pair->items[0] = mp_obj_new_bytes(key.data, key.size); pair->items[1] = mp_obj_new_bytes(val.data, val.size); return pair_o; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_seq_obj, 2, 4, btree_seq); STATIC mp_obj_t btree_init_iter(size_t n_args, const mp_obj_t *args, byte type) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]); self->next_flags = type; self->start_key = mp_const_none; self->end_key = mp_const_none; if (n_args > 1) { self->start_key = args[1]; if (n_args > 2) { self->end_key = args[2]; if (n_args > 3) { self->next_flags = type | MP_OBJ_SMALL_INT_VALUE(args[3]); } } } return args[0]; } STATIC mp_obj_t btree_keys(size_t n_args, const mp_obj_t *args) { return btree_init_iter(n_args, args, FLAG_ITER_KEYS); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_keys_obj, 1, 4, btree_keys); STATIC mp_obj_t btree_values(size_t n_args, const mp_obj_t *args) { return btree_init_iter(n_args, args, FLAG_ITER_VALUES); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_values_obj, 1, 4, btree_values); STATIC mp_obj_t btree_items(size_t n_args, const mp_obj_t *args) { return btree_init_iter(n_args, args, FLAG_ITER_ITEMS); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_items_obj, 1, 4, btree_items); STATIC mp_obj_t btree_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { (void)iter_buf; mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); if (self->next_flags != 0) { // If we're called immediately after keys(), values(), or items(), // use their setup for iteration. self->flags = self->next_flags; self->next_flags = 0; } else { // Otherwise, iterate over all keys. self->flags = FLAG_ITER_KEYS; self->start_key = mp_const_none; self->end_key = mp_const_none; } return self_in; } STATIC mp_obj_t btree_iternext(mp_obj_t self_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); DBT key, val; int res; bool desc = self->flags & FLAG_DESC; if (self->start_key != MP_OBJ_NULL) { int flags = R_FIRST; if (self->start_key != mp_const_none) { key.data = (void*)mp_obj_str_get_data(self->start_key, &key.size); flags = R_CURSOR; } else if (desc) { flags = R_LAST; } res = __bt_seq(self->db, &key, &val, flags); self->start_key = MP_OBJ_NULL; } else { res = __bt_seq(self->db, &key, &val, desc ? R_PREV : R_NEXT); } if (res == RET_SPECIAL) { return MP_OBJ_STOP_ITERATION; } CHECK_ERROR(res); if (self->end_key != mp_const_none) { DBT end_key; end_key.data = (void*)mp_obj_str_get_data(self->end_key, &end_key.size); BTREE *t = self->db->internal; int cmp = t->bt_cmp(&key, &end_key); if (desc) { cmp = -cmp; } if (self->flags & FLAG_END_KEY_INCL) { cmp--; } if (cmp >= 0) { self->end_key = MP_OBJ_NULL; return MP_OBJ_STOP_ITERATION; } } switch (self->flags & FLAG_ITER_TYPE_MASK) { case FLAG_ITER_KEYS: return mp_obj_new_bytes(key.data, key.size); case FLAG_ITER_VALUES: return mp_obj_new_bytes(val.data, val.size); default: { mp_obj_t pair_o = mp_obj_new_tuple(2, NULL); mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(pair_o); pair->items[0] = mp_obj_new_bytes(key.data, key.size); pair->items[1] = mp_obj_new_bytes(val.data, val.size); return pair_o; } } } STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); if (value == MP_OBJ_NULL) { // delete DBT key; key.data = (void*)mp_obj_str_get_data(index, &key.size); int res = __bt_delete(self->db, &key, 0); if (res == RET_SPECIAL) { nlr_raise(mp_obj_new_exception(&mp_type_KeyError)); } CHECK_ERROR(res); return mp_const_none; } else if (value == MP_OBJ_SENTINEL) { // load DBT key, val; key.data = (void*)mp_obj_str_get_data(index, &key.size); int res = __bt_get(self->db, &key, &val, 0); if (res == RET_SPECIAL) { nlr_raise(mp_obj_new_exception(&mp_type_KeyError)); } CHECK_ERROR(res); return mp_obj_new_bytes(val.data, val.size); } else { // store DBT key, val; key.data = (void*)mp_obj_str_get_data(index, &key.size); val.data = (void*)mp_obj_str_get_data(value, &val.size); int res = __bt_put(self->db, &key, &val, 0); CHECK_ERROR(res); return mp_const_none; } } STATIC mp_obj_t btree_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(lhs_in); switch (op) { case MP_BINARY_OP_CONTAINS: { DBT key, val; key.data = (void*)mp_obj_str_get_data(rhs_in, &key.size); int res = __bt_get(self->db, &key, &val, 0); CHECK_ERROR(res); return mp_obj_new_bool(res != RET_SPECIAL); } default: // op not supported return MP_OBJ_NULL; } } #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t btree_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&btree_close_obj) }, { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&btree_flush_obj) }, { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&btree_get_obj) }, { MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&btree_put_obj) }, { MP_ROM_QSTR(MP_QSTR_seq), MP_ROM_PTR(&btree_seq_obj) }, { MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&btree_keys_obj) }, { MP_ROM_QSTR(MP_QSTR_values), MP_ROM_PTR(&btree_values_obj) }, { MP_ROM_QSTR(MP_QSTR_items), MP_ROM_PTR(&btree_items_obj) }, }; STATIC MP_DEFINE_CONST_DICT(btree_locals_dict, btree_locals_dict_table); STATIC const mp_obj_type_t btree_type = { { &mp_type_type }, // Save on qstr's, reuse same as for module .name = MP_QSTR_btree, .print = btree_print, .getiter = btree_getiter, .iternext = btree_iternext, .binary_op = btree_binary_op, .subscr = btree_subscr, .locals_dict = (void*)&btree_locals_dict, }; #endif STATIC const FILEVTABLE btree_stream_fvtable = { mp_stream_posix_read, mp_stream_posix_write, mp_stream_posix_lseek, mp_stream_posix_fsync }; #if !MICROPY_ENABLE_DYNRUNTIME STATIC mp_obj_t mod_btree_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_flags, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_cachesize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_pagesize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_minkeypage, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; // Make sure we got a stream object mp_get_stream_raise(pos_args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); struct { mp_arg_val_t flags; mp_arg_val_t cachesize; mp_arg_val_t pagesize; mp_arg_val_t minkeypage; } 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); BTREEINFO openinfo = {0}; openinfo.flags = args.flags.u_int; openinfo.cachesize = args.cachesize.u_int; openinfo.psize = args.pagesize.u_int; openinfo.minkeypage = args.minkeypage.u_int; DB *db = __bt_open(MP_OBJ_TO_PTR(pos_args[0]), &btree_stream_fvtable, &openinfo, /*dflags*/0); if (db == NULL) { mp_raise_OSError(errno); } return MP_OBJ_FROM_PTR(btree_new(db)); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_btree_open_obj, 1, mod_btree_open); STATIC const mp_rom_map_elem_t mp_module_btree_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_btree) }, { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_btree_open_obj) }, { MP_ROM_QSTR(MP_QSTR_INCL), MP_ROM_INT(FLAG_END_KEY_INCL) }, { MP_ROM_QSTR(MP_QSTR_DESC), MP_ROM_INT(FLAG_DESC) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_btree_globals, mp_module_btree_globals_table); const mp_obj_module_t mp_module_btree = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_btree_globals, }; #endif #endif // MICROPY_PY_BTREE ================================================ FILE: extmod/modframebuf.c ================================================ /* * 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" #if MICROPY_PY_FRAMEBUF #include "ports/stm32/font_petme128_8x8.h" typedef struct _mp_obj_framebuf_t { mp_obj_base_t base; mp_obj_t buf_obj; // need to store this to prevent GC from reclaiming buf void *buf; uint16_t width, height, stride; uint8_t format; } mp_obj_framebuf_t; typedef void (*setpixel_t)(const mp_obj_framebuf_t*, int, int, uint32_t); typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t*, int, int); typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t); typedef struct _mp_framebuf_p_t { setpixel_t setpixel; getpixel_t getpixel; fill_rect_t fill_rect; } mp_framebuf_p_t; // constants for formats #define FRAMEBUF_MVLSB (0) #define FRAMEBUF_RGB565 (1) #define FRAMEBUF_GS2_HMSB (5) #define FRAMEBUF_GS4_HMSB (2) #define FRAMEBUF_GS8 (6) #define FRAMEBUF_MHLSB (3) #define FRAMEBUF_MHMSB (4) // Functions for MHLSB and MHMSB STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { size_t index = (x + y * fb->stride) >> 3; int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); ((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset); } STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { size_t index = (x + y * fb->stride) >> 3; int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); return (((uint8_t*)fb->buf)[index] >> (offset)) & 0x01; } STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { int reverse = fb->format == FRAMEBUF_MHMSB; int advance = fb->stride >> 3; while (w--) { uint8_t *b = &((uint8_t*)fb->buf)[(x >> 3) + y * advance]; int offset = reverse ? x & 7 : 7 - (x & 7); for (int hh = h; hh; --hh) { *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); b += advance; } ++x; } } // Functions for MVLSB format STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { size_t index = (y >> 3) * fb->stride + x; uint8_t offset = y & 0x07; ((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset); } STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return (((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01; } STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { while (h--) { uint8_t *b = &((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x]; uint8_t offset = y & 0x07; for (int ww = w; ww; --ww) { *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); ++b; } ++y; } } // Functions for RGB565 format STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { ((uint16_t*)fb->buf)[x + y * fb->stride] = col; } STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return ((uint16_t*)fb->buf)[x + y * fb->stride]; } STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { uint16_t *b = &((uint16_t*)fb->buf)[x + y * fb->stride]; while (h--) { for (int ww = w; ww; --ww) { *b++ = col; } b += fb->stride - w; } } // Functions for GS2_HMSB format STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2]; uint8_t shift = (x & 0x3) << 1; uint8_t mask = 0x3 << shift; uint8_t color = (col & 0x3) << shift; *pixel = color | (*pixel & (~mask)); } STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { uint8_t pixel = ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2]; uint8_t shift = (x & 0x3) << 1; return (pixel >> shift) & 0x3; } STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { for (int xx=x; xx < x+w; xx++) { for (int yy=y; yy < y+h; yy++) { gs2_hmsb_setpixel(fb, xx, yy, col); } } } // Functions for GS4_HMSB format STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1]; if (x % 2) { *pixel = ((uint8_t)col & 0x0f) | (*pixel & 0xf0); } else { *pixel = ((uint8_t)col << 4) | (*pixel & 0x0f); } } STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { if (x % 2) { return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f; } return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] >> 4; } STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { col &= 0x0f; uint8_t *pixel_pair = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1]; uint8_t col_shifted_left = col << 4; uint8_t col_pixel_pair = col_shifted_left | col; int pixel_count_till_next_line = (fb->stride - w) >> 1; bool odd_x = (x % 2 == 1); while (h--) { int ww = w; if (odd_x && ww > 0) { *pixel_pair = (*pixel_pair & 0xf0) | col; pixel_pair++; ww--; } memset(pixel_pair, col_pixel_pair, ww >> 1); pixel_pair += ww >> 1; if (ww % 2) { *pixel_pair = col_shifted_left | (*pixel_pair & 0x0f); if (!odd_x) { pixel_pair++; } } pixel_pair += pixel_count_till_next_line; } } // Functions for GS8 format STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)]; *pixel = col & 0xff; } STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return ((uint8_t*)fb->buf)[(x + y * fb->stride)]; } STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)]; while (h--) { memset(pixel, col, w); pixel += fb->stride; } } STATIC mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, }; static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { formats[fb->format].setpixel(fb, x, y, col); } static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return formats[fb->format].getpixel(fb, x, y); } STATIC void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { // No operation needed. return; } // clip to the framebuffer int xend = MIN(fb->width, x + w); int yend = MIN(fb->height, y + h); x = MAX(x, 0); y = MAX(y, 0); formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); } STATIC mp_obj_t framebuf_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, 4, 5, false); mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t); o->base.type = type; o->buf_obj = args[0]; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE); o->buf = bufinfo.buf; o->width = mp_obj_get_int(args[1]); o->height = mp_obj_get_int(args[2]); o->format = mp_obj_get_int(args[3]); if (n_args >= 5) { o->stride = mp_obj_get_int(args[4]); } else { o->stride = o->width; } switch (o->format) { case FRAMEBUF_MVLSB: case FRAMEBUF_RGB565: break; case FRAMEBUF_MHLSB: case FRAMEBUF_MHMSB: o->stride = (o->stride + 7) & ~7; break; case FRAMEBUF_GS2_HMSB: o->stride = (o->stride + 3) & ~3; break; case FRAMEBUF_GS4_HMSB: o->stride = (o->stride + 1) & ~1; break; case FRAMEBUF_GS8: break; default: mp_raise_ValueError("invalid format"); } return MP_OBJ_FROM_PTR(o); } STATIC mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { (void)flags; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); bufinfo->buf = self->buf; bufinfo->len = self->stride * self->height * (self->format == FRAMEBUF_RGB565 ? 2 : 1); bufinfo->typecode = 'B'; // view framebuf as bytes return 0; } STATIC mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t col = mp_obj_get_int(col_in); formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(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 width = mp_obj_get_int(args[3]); mp_int_t height = mp_obj_get_int(args[4]); mp_int_t col = mp_obj_get_int(args[5]); fill_rect(self, x, y, width, height, col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect); STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); mp_int_t x = mp_obj_get_int(args[1]); mp_int_t y = mp_obj_get_int(args[2]); if (0 <= x && x < self->width && 0 <= y && y < self->height) { if (n_args == 3) { // get return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set setpixel(self, x, y, mp_obj_get_int(args[3])); } } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel); STATIC mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(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 w = mp_obj_get_int(args[3]); mp_int_t col = mp_obj_get_int(args[4]); fill_rect(self, x, y, w, 1, col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline); STATIC mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(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 = mp_obj_get_int(args[3]); mp_int_t col = mp_obj_get_int(args[4]); fill_rect(self, x, y, 1, h, col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline); STATIC mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(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 w = mp_obj_get_int(args[3]); mp_int_t h = mp_obj_get_int(args[4]); mp_int_t col = mp_obj_get_int(args[5]); fill_rect(self, x, y, w, 1, col); fill_rect(self, x, y + h- 1, w, 1, col); fill_rect(self, x, y, 1, h, col); fill_rect(self, x + w- 1, y, 1, h, col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 6, framebuf_rect); STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); mp_int_t x1 = mp_obj_get_int(args[1]); mp_int_t y1 = mp_obj_get_int(args[2]); mp_int_t x2 = mp_obj_get_int(args[3]); mp_int_t y2 = mp_obj_get_int(args[4]); mp_int_t col = mp_obj_get_int(args[5]); mp_int_t dx = x2 - x1; mp_int_t sx; if (dx > 0) { sx = 1; } else { dx = -dx; sx = -1; } mp_int_t dy = y2 - y1; mp_int_t sy; if (dy > 0) { sy = 1; } else { dy = -dy; sy = -1; } bool steep; if (dy > dx) { mp_int_t temp; temp = x1; x1 = y1; y1 = temp; temp = dx; dx = dy; dy = temp; temp = sx; sx = sy; sy = temp; steep = true; } else { steep = false; } mp_int_t e = 2 * dy - dx; for (mp_int_t i = 0; i < dx; ++i) { if (steep) { if (0 <= y1 && y1 < self->width && 0 <= x1 && x1 < self->height) { setpixel(self, y1, x1, col); } } else { if (0 <= x1 && x1 < self->width && 0 <= y1 && y1 < self->height) { setpixel(self, x1, y1, col); } } while (e >= 0) { y1 += sy; e -= 2 * dx; } x1 += sx; e += 2 * dy; } if (0 <= x2 && x2 < self->width && 0 <= y2 && y2 < self->height) { setpixel(self, x2, y2, col); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line); STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(args[1]); mp_int_t x = mp_obj_get_int(args[2]); mp_int_t y = mp_obj_get_int(args[3]); mp_int_t key = -1; if (n_args > 4) { key = mp_obj_get_int(args[4]); } if ( (x >= self->width) || (y >= self->height) || (-x >= source->width) || (-y >= source->height) ) { // Out of bounds, no-op. return mp_const_none; } // Clip. int x0 = MAX(0, x); int y0 = MAX(0, y); int x1 = MAX(0, -x); int y1 = MAX(0, -y); int x0end = MIN(self->width, x + source->width); int y0end = MIN(self->height, y + source->height); for (; y0 < y0end; ++y0) { int cx1 = x1; for (int cx0 = x0; cx0 < x0end; ++cx0) { uint32_t col = getpixel(source, cx1, y1); if (col != (uint32_t)key) { setpixel(self, cx0, y0, col); } ++cx1; } ++y1; } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit); STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t xstep = mp_obj_get_int(xstep_in); mp_int_t ystep = mp_obj_get_int(ystep_in); int sx, y, xend, yend, dx, dy; if (xstep < 0) { sx = 0; xend = self->width + xstep; dx = 1; } else { sx = self->width - 1; xend = xstep - 1; dx = -1; } if (ystep < 0) { y = 0; yend = self->height + ystep; dy = 1; } else { y = self->height - 1; yend = ystep - 1; dy = -1; } for (; y != yend; y += dy) { for (int x = sx; x != xend; x += dx) { setpixel(self, x, y, getpixel(self, x - xstep, y - ystep)); } } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll); STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) { // extract arguments mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); const char *str = mp_obj_str_get_str(args[1]); mp_int_t x0 = mp_obj_get_int(args[2]); mp_int_t y0 = mp_obj_get_int(args[3]); mp_int_t col = 1; if (n_args >= 5) { col = mp_obj_get_int(args[4]); } // loop over chars for (; *str; ++str) { // get char and make sure its in range of font int chr = *(uint8_t*)str; if (chr < 32 || chr > 127) { chr = 127; } // get char data const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8]; // loop over char data for (int j = 0; j < 8; j++, x0++) { if (0 <= x0 && x0 < self->width) { // clip x uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column if (vline_data & 1) { // only draw if pixel set if (0 <= y && y < self->height) { // clip y setpixel(self, x0, y, col); } } } } } } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text); #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) }, { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) }, { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) }, { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&framebuf_hline_obj) }, { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) }, { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) }, { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) }, { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) }, { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) }, { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) }, }; STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table); STATIC const mp_obj_type_t mp_type_framebuf = { { &mp_type_type }, .name = MP_QSTR_FrameBuffer, .make_new = framebuf_make_new, .buffer_p = { .get_buffer = framebuf_get_buffer }, .locals_dict = (mp_obj_dict_t*)&framebuf_locals_dict, }; #endif // this factory function is provided for backwards compatibility with old FrameBuffer1 class STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) { mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t); o->base.type = &mp_type_framebuf; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE); o->buf = bufinfo.buf; o->width = mp_obj_get_int(args[1]); o->height = mp_obj_get_int(args[2]); o->format = FRAMEBUF_MVLSB; if (n_args >= 4) { o->stride = mp_obj_get_int(args[3]); } else { o->stride = o->width; } return MP_OBJ_FROM_PTR(o); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1); #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) }, { MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) }, { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) }, { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) }, }; STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table); const mp_obj_module_t mp_module_framebuf = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&framebuf_module_globals, }; #endif #endif // MICROPY_PY_FRAMEBUF ================================================ FILE: extmod/modonewire.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2015-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 "py/obj.h" #include "py/mphal.h" /******************************************************************************/ // Low-level 1-Wire routines #define TIMING_RESET1 (480) #define TIMING_RESET2 (70) #define TIMING_RESET3 (410) #define TIMING_READ1 (5) #define TIMING_READ2 (5) #define TIMING_READ3 (40) #define TIMING_WRITE1 (10) #define TIMING_WRITE2 (50) #define TIMING_WRITE3 (10) STATIC int onewire_bus_reset(mp_hal_pin_obj_t pin) { mp_hal_pin_write(pin, 0); mp_hal_delay_us(TIMING_RESET1); uint32_t i = mp_hal_quiet_timing_enter(); mp_hal_pin_write(pin, 1); mp_hal_delay_us_fast(TIMING_RESET2); int status = !mp_hal_pin_read(pin); mp_hal_quiet_timing_exit(i); mp_hal_delay_us(TIMING_RESET3); return status; } STATIC int onewire_bus_readbit(mp_hal_pin_obj_t pin) { mp_hal_pin_write(pin, 1); uint32_t i = mp_hal_quiet_timing_enter(); mp_hal_pin_write(pin, 0); mp_hal_delay_us_fast(TIMING_READ1); mp_hal_pin_write(pin, 1); mp_hal_delay_us_fast(TIMING_READ2); int value = mp_hal_pin_read(pin); mp_hal_quiet_timing_exit(i); mp_hal_delay_us_fast(TIMING_READ3); return value; } STATIC void onewire_bus_writebit(mp_hal_pin_obj_t pin, int value) { uint32_t i = mp_hal_quiet_timing_enter(); mp_hal_pin_write(pin, 0); mp_hal_delay_us_fast(TIMING_WRITE1); if (value) { mp_hal_pin_write(pin, 1); } mp_hal_delay_us_fast(TIMING_WRITE2); mp_hal_pin_write(pin, 1); mp_hal_delay_us_fast(TIMING_WRITE3); mp_hal_quiet_timing_exit(i); } /******************************************************************************/ // MicroPython bindings STATIC mp_obj_t onewire_reset(mp_obj_t pin_in) { return mp_obj_new_bool(onewire_bus_reset(mp_hal_get_pin_obj(pin_in))); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_reset_obj, onewire_reset); STATIC mp_obj_t onewire_readbit(mp_obj_t pin_in) { return MP_OBJ_NEW_SMALL_INT(onewire_bus_readbit(mp_hal_get_pin_obj(pin_in))); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbit_obj, onewire_readbit); STATIC mp_obj_t onewire_readbyte(mp_obj_t pin_in) { mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(pin_in); uint8_t value = 0; for (int i = 0; i < 8; ++i) { value |= onewire_bus_readbit(pin) << i; } return MP_OBJ_NEW_SMALL_INT(value); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbyte_obj, onewire_readbyte); STATIC mp_obj_t onewire_writebit(mp_obj_t pin_in, mp_obj_t value_in) { onewire_bus_writebit(mp_hal_get_pin_obj(pin_in), mp_obj_get_int(value_in)); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebit_obj, onewire_writebit); STATIC mp_obj_t onewire_writebyte(mp_obj_t pin_in, mp_obj_t value_in) { mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(pin_in); int value = mp_obj_get_int(value_in); for (int i = 0; i < 8; ++i) { onewire_bus_writebit(pin, value & 1); value >>= 1; } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebyte_obj, onewire_writebyte); STATIC mp_obj_t onewire_crc8(mp_obj_t data) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); uint8_t crc = 0; for (size_t i = 0; i < bufinfo.len; ++i) { uint8_t byte = ((uint8_t*)bufinfo.buf)[i]; for (int b = 0; b < 8; ++b) { uint8_t fb_bit = (crc ^ byte) & 0x01; if (fb_bit == 0x01) { crc = crc ^ 0x18; } crc = (crc >> 1) & 0x7f; if (fb_bit == 0x01) { crc = crc | 0x80; } byte = byte >> 1; } } return MP_OBJ_NEW_SMALL_INT(crc); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_crc8_obj, onewire_crc8); STATIC const mp_rom_map_elem_t onewire_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_onewire) }, { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&onewire_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_readbit), MP_ROM_PTR(&onewire_readbit_obj) }, { MP_ROM_QSTR(MP_QSTR_readbyte), MP_ROM_PTR(&onewire_readbyte_obj) }, { MP_ROM_QSTR(MP_QSTR_writebit), MP_ROM_PTR(&onewire_writebit_obj) }, { MP_ROM_QSTR(MP_QSTR_writebyte), MP_ROM_PTR(&onewire_writebyte_obj) }, { MP_ROM_QSTR(MP_QSTR_crc8), MP_ROM_PTR(&onewire_crc8_obj) }, }; STATIC MP_DEFINE_CONST_DICT(onewire_module_globals, onewire_module_globals_table); const mp_obj_module_t mp_module_onewire = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&onewire_module_globals, }; ================================================ FILE: extmod/modubinascii.c ================================================ /* * 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. */ #include #include #include #include "py/runtime.h" #include "py/binary.h" #include "extmod/modubinascii.h" mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) { // Second argument is for an extension to allow a 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_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj, 1, 2, mod_binascii_hexlify); mp_obj_t mod_binascii_unhexlify(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("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("non-hex digit found"); } if (i & 1) { hex_byte <<= 4; } else { *out++ = hex_byte; hex_byte = 0; } } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj, mod_binascii_unhexlify); // If ch is a character in the base64 alphabet, and is not a pad character, then // the corresponding integer between 0 and 63, inclusively, is returned. // Otherwise, -1 is returned. static int mod_binascii_sextet(byte ch) { if (ch >= 'A' && ch <= 'Z') { return ch - 'A'; } else if (ch >= 'a' && ch <= 'z') { return ch - 'a' + 26; } else if (ch >= '0' && ch <= '9') { return ch - '0' + 52; } else if (ch == '+') { return 62; } else if (ch == '/') { return 63; } else { return -1; } } mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); byte *in = bufinfo.buf; vstr_t vstr; vstr_init(&vstr, (bufinfo.len / 4) * 3 + 1); // Potentially over-allocate byte *out = (byte *)vstr.buf; uint shift = 0; int nbits = 0; // Number of meaningful bits in shift bool hadpad = false; // Had a pad character since last valid character for (size_t i = 0; i < bufinfo.len; i++) { if (in[i] == '=') { if ((nbits == 2) || ((nbits == 4) && hadpad)) { nbits = 0; break; } hadpad = true; } int sextet = mod_binascii_sextet(in[i]); if (sextet == -1) { continue; } hadpad = false; shift = (shift << 6) | sextet; nbits += 6; if (nbits >= 8) { nbits -= 8; out[vstr.len++] = (shift >> nbits) & 0xFF; } } if (nbits) { mp_raise_ValueError("incorrect padding"); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64); mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); vstr_t vstr; vstr_init_len(&vstr, ((bufinfo.len != 0) ? (((bufinfo.len - 1) / 3) + 1) * 4 : 0) + 1); // First pass, we convert input buffer to numeric base 64 values byte *in = bufinfo.buf, *out = (byte*)vstr.buf; mp_uint_t i; for (i = bufinfo.len; i >= 3; i -= 3) { *out++ = (in[0] & 0xFC) >> 2; *out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4; *out++ = (in[1] & 0x0F) << 2 | (in[2] & 0xC0) >> 6; *out++ = in[2] & 0x3F; in += 3; } if (i != 0) { *out++ = (in[0] & 0xFC) >> 2; if (i == 2) { *out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4; *out++ = (in[1] & 0x0F) << 2; } else { *out++ = (in[0] & 0x03) << 4; *out++ = 64; } *out = 64; } // Second pass, we convert number base 64 values to actual base64 ascii encoding out = (byte*)vstr.buf; for (mp_uint_t j = vstr.len - 1; j--;) { if (*out < 26) { *out += 'A'; } else if (*out < 52) { *out += 'a' - 26; } else if (*out < 62) { *out += '0' - 52; } else if (*out == 62) { *out ='+'; } else if (*out == 63) { *out = '/'; } else { *out = '='; } out++; } *out = '\n'; return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64); #if MICROPY_PY_UBINASCII_CRC32 #include "uzlib/tinf.h" mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); uint32_t crc = (n_args > 1) ? mp_obj_get_int_truncated(args[1]) : 0; crc = uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff); return mp_obj_new_int_from_uint(crc ^ 0xffffffff); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32); #endif #if MICROPY_PY_UBINASCII STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubinascii) }, { MP_ROM_QSTR(MP_QSTR_hexlify), MP_ROM_PTR(&mod_binascii_hexlify_obj) }, { MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&mod_binascii_unhexlify_obj) }, { MP_ROM_QSTR(MP_QSTR_a2b_base64), MP_ROM_PTR(&mod_binascii_a2b_base64_obj) }, { MP_ROM_QSTR(MP_QSTR_b2a_base64), MP_ROM_PTR(&mod_binascii_b2a_base64_obj) }, #if MICROPY_PY_UBINASCII_CRC32 { MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&mod_binascii_crc32_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_binascii_globals, mp_module_binascii_globals_table); const mp_obj_module_t mp_module_ubinascii = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_binascii_globals, }; #endif //MICROPY_PY_UBINASCII ================================================ FILE: extmod/modubinascii.h ================================================ /* * 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_EXTMOD_MODUBINASCII_H #define MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H extern mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args); extern mp_obj_t mod_binascii_unhexlify(mp_obj_t data); extern mp_obj_t mod_binascii_a2b_base64(mp_obj_t data); extern mp_obj_t mod_binascii_b2a_base64(mp_obj_t data); extern mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj); MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj); MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj); MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj); #endif // MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H ================================================ FILE: extmod/moducryptolib.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017-2018 Paul Sokolovsky * Copyright (c) 2018 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/mpconfig.h" #if MICROPY_PY_UCRYPTOLIB #include #include #include "py/runtime.h" // This module implements crypto ciphers API, roughly following // https://www.python.org/dev/peps/pep-0272/ . Exact implementation // of PEP 272 can be made with a simple wrapper which adds all the // needed boilerplate. // values follow PEP 272 enum { UCRYPTOLIB_MODE_ECB = 1, UCRYPTOLIB_MODE_CBC = 2, UCRYPTOLIB_MODE_CTR = 6, }; struct ctr_params { // counter is the IV of the AES context. size_t offset; // in encrypted_counter // encrypted counter uint8_t encrypted_counter[16]; }; #if MICROPY_SSL_AXTLS #include "lib/axtls/crypto/crypto.h" #define AES_CTX_IMPL AES_CTX #endif #if MICROPY_SSL_MBEDTLS #include // we can't run mbedtls AES key schedule until we know whether we're used for encrypt or decrypt. // therefore, we store the key & keysize and on the first call to encrypt/decrypt we override them // with the mbedtls_aes_context, as they are not longer required. (this is done to save space) struct mbedtls_aes_ctx_with_key { union { mbedtls_aes_context mbedtls_ctx; struct { uint8_t key[32]; uint8_t keysize; } init_data; } u; unsigned char iv[16]; }; #define AES_CTX_IMPL struct mbedtls_aes_ctx_with_key #endif typedef struct _mp_obj_aes_t { mp_obj_base_t base; AES_CTX_IMPL ctx; uint8_t block_mode: 6; #define AES_KEYTYPE_NONE 0 #define AES_KEYTYPE_ENC 1 #define AES_KEYTYPE_DEC 2 uint8_t key_type: 2; } mp_obj_aes_t; static inline bool is_ctr_mode(int block_mode) { #if MICROPY_PY_UCRYPTOLIB_CTR return block_mode == UCRYPTOLIB_MODE_CTR; #else return false; #endif } static inline struct ctr_params *ctr_params_from_aes(mp_obj_aes_t *o) { // ctr_params follows aes object struct return (struct ctr_params*)&o[1]; } #if MICROPY_SSL_AXTLS STATIC void aes_initial_set_key_impl(AES_CTX_IMPL *ctx, const uint8_t *key, size_t keysize, const uint8_t iv[16]) { assert(16 == keysize || 32 == keysize); AES_set_key(ctx, key, iv, (16 == keysize) ? AES_MODE_128 : AES_MODE_256); } STATIC void aes_final_set_key_impl(AES_CTX_IMPL *ctx, bool encrypt) { if (!encrypt) { AES_convert_key(ctx); } } STATIC void aes_process_ecb_impl(AES_CTX_IMPL *ctx, const uint8_t in[16], uint8_t out[16], bool encrypt) { memcpy(out, in, 16); // We assume that out (vstr.buf or given output buffer) is uint32_t aligned uint32_t *p = (uint32_t*)out; // axTLS likes it weird and complicated with byteswaps for (int i = 0; i < 4; i++) { p[i] = MP_HTOBE32(p[i]); } if (encrypt) { AES_encrypt(ctx, p); } else { AES_decrypt(ctx, p); } for (int i = 0; i < 4; i++) { p[i] = MP_BE32TOH(p[i]); } } STATIC void aes_process_cbc_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, bool encrypt) { if (encrypt) { AES_cbc_encrypt(ctx, in, out, in_len); } else { AES_cbc_decrypt(ctx, in, out, in_len); } } #if MICROPY_PY_UCRYPTOLIB_CTR // axTLS doesn't have CTR support out of the box. This implements the counter part using the ECB primitive. STATIC void aes_process_ctr_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, struct ctr_params *ctr_params) { size_t n = ctr_params->offset; uint8_t *const counter = ctx->iv; while (in_len--) { if (n == 0) { aes_process_ecb_impl(ctx, counter, ctr_params->encrypted_counter, true); // increment the 128-bit counter for (int i = 15; i >= 0; --i) { if (++counter[i] != 0) { break; } } } *out++ = *in++ ^ ctr_params->encrypted_counter[n]; n = (n + 1) & 0xf; } ctr_params->offset = n; } #endif #endif #if MICROPY_SSL_MBEDTLS STATIC void aes_initial_set_key_impl(AES_CTX_IMPL *ctx, const uint8_t *key, size_t keysize, const uint8_t iv[16]) { ctx->u.init_data.keysize = keysize; memcpy(ctx->u.init_data.key, key, keysize); if (NULL != iv) { memcpy(ctx->iv, iv, sizeof(ctx->iv)); } } STATIC void aes_final_set_key_impl(AES_CTX_IMPL *ctx, bool encrypt) { // first, copy key aside uint8_t key[32]; uint8_t keysize = ctx->u.init_data.keysize; memcpy(key, ctx->u.init_data.key, keysize); // now, override key with the mbedtls context object mbedtls_aes_init(&ctx->u.mbedtls_ctx); // setkey call will succeed, we've already checked the keysize earlier. assert(16 == keysize || 32 == keysize); if (encrypt) { mbedtls_aes_setkey_enc(&ctx->u.mbedtls_ctx, key, keysize * 8); } else { mbedtls_aes_setkey_dec(&ctx->u.mbedtls_ctx, key, keysize * 8); } } STATIC void aes_process_ecb_impl(AES_CTX_IMPL *ctx, const uint8_t in[16], uint8_t out[16], bool encrypt) { mbedtls_aes_crypt_ecb(&ctx->u.mbedtls_ctx, encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT, in, out); } STATIC void aes_process_cbc_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, bool encrypt) { mbedtls_aes_crypt_cbc(&ctx->u.mbedtls_ctx, encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT, in_len, ctx->iv, in, out); } #if MICROPY_PY_UCRYPTOLIB_CTR STATIC void aes_process_ctr_impl(AES_CTX_IMPL *ctx, const uint8_t *in, uint8_t *out, size_t in_len, struct ctr_params *ctr_params) { mbedtls_aes_crypt_ctr(&ctx->u.mbedtls_ctx, in_len, &ctr_params->offset, ctx->iv, ctr_params->encrypted_counter, in, out); } #endif #endif STATIC mp_obj_t ucryptolib_aes_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); const mp_int_t block_mode = mp_obj_get_int(args[1]); switch (block_mode) { case UCRYPTOLIB_MODE_ECB: case UCRYPTOLIB_MODE_CBC: #if MICROPY_PY_UCRYPTOLIB_CTR case UCRYPTOLIB_MODE_CTR: #endif break; default: mp_raise_ValueError("mode"); } mp_obj_aes_t *o = m_new_obj_var(mp_obj_aes_t, struct ctr_params, !!is_ctr_mode(block_mode)); o->base.type = type; o->block_mode = block_mode; o->key_type = AES_KEYTYPE_NONE; mp_buffer_info_t keyinfo; mp_get_buffer_raise(args[0], &keyinfo, MP_BUFFER_READ); if (32 != keyinfo.len && 16 != keyinfo.len) { mp_raise_ValueError("key"); } mp_buffer_info_t ivinfo; ivinfo.buf = NULL; if (n_args > 2 && args[2] != mp_const_none) { mp_get_buffer_raise(args[2], &ivinfo, MP_BUFFER_READ); if (16 != ivinfo.len) { mp_raise_ValueError("IV"); } } else if (o->block_mode == UCRYPTOLIB_MODE_CBC || is_ctr_mode(o->block_mode)) { mp_raise_ValueError("IV"); } if (is_ctr_mode(block_mode)) { ctr_params_from_aes(o)->offset = 0; } aes_initial_set_key_impl(&o->ctx, keyinfo.buf, keyinfo.len, ivinfo.buf); return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t aes_process(size_t n_args, const mp_obj_t *args, bool encrypt) { mp_obj_aes_t *self = MP_OBJ_TO_PTR(args[0]); mp_obj_t in_buf = args[1]; mp_obj_t out_buf = MP_OBJ_NULL; if (n_args > 2) { out_buf = args[2]; } mp_buffer_info_t in_bufinfo; mp_get_buffer_raise(in_buf, &in_bufinfo, MP_BUFFER_READ); if (!is_ctr_mode(self->block_mode) && in_bufinfo.len % 16 != 0) { mp_raise_ValueError("blksize % 16"); } vstr_t vstr; mp_buffer_info_t out_bufinfo; uint8_t *out_buf_ptr; if (out_buf != MP_OBJ_NULL) { mp_get_buffer_raise(out_buf, &out_bufinfo, MP_BUFFER_WRITE); if (out_bufinfo.len < in_bufinfo.len) { mp_raise_ValueError("output too small"); } out_buf_ptr = out_bufinfo.buf; } else { vstr_init_len(&vstr, in_bufinfo.len); out_buf_ptr = (uint8_t*)vstr.buf; } if (AES_KEYTYPE_NONE == self->key_type) { // always set key for encryption if CTR mode. const bool encrypt_mode = encrypt || is_ctr_mode(self->block_mode); aes_final_set_key_impl(&self->ctx, encrypt_mode); self->key_type = encrypt ? AES_KEYTYPE_ENC : AES_KEYTYPE_DEC; } else { if ((encrypt && self->key_type == AES_KEYTYPE_DEC) || (!encrypt && self->key_type == AES_KEYTYPE_ENC)) { mp_raise_ValueError("can't encrypt & decrypt"); } } switch (self->block_mode) { case UCRYPTOLIB_MODE_ECB: { uint8_t *in = in_bufinfo.buf, *out = out_buf_ptr; uint8_t *top = in + in_bufinfo.len; for (; in < top; in += 16, out += 16) { aes_process_ecb_impl(&self->ctx, in, out, encrypt); } break; } case UCRYPTOLIB_MODE_CBC: aes_process_cbc_impl(&self->ctx, in_bufinfo.buf, out_buf_ptr, in_bufinfo.len, encrypt); break; #if MICROPY_PY_UCRYPTOLIB_CTR case UCRYPTOLIB_MODE_CTR: aes_process_ctr_impl(&self->ctx, in_bufinfo.buf, out_buf_ptr, in_bufinfo.len, ctr_params_from_aes(self)); break; #endif } if (out_buf != MP_OBJ_NULL) { return out_buf; } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC mp_obj_t ucryptolib_aes_encrypt(size_t n_args, const mp_obj_t *args) { return aes_process(n_args, args, true); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ucryptolib_aes_encrypt_obj, 2, 3, ucryptolib_aes_encrypt); STATIC mp_obj_t ucryptolib_aes_decrypt(size_t n_args, const mp_obj_t *args) { return aes_process(n_args, args, false); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ucryptolib_aes_decrypt_obj, 2, 3, ucryptolib_aes_decrypt); STATIC const mp_rom_map_elem_t ucryptolib_aes_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_encrypt), MP_ROM_PTR(&ucryptolib_aes_encrypt_obj) }, { MP_ROM_QSTR(MP_QSTR_decrypt), MP_ROM_PTR(&ucryptolib_aes_decrypt_obj) }, }; STATIC MP_DEFINE_CONST_DICT(ucryptolib_aes_locals_dict, ucryptolib_aes_locals_dict_table); STATIC const mp_obj_type_t ucryptolib_aes_type = { { &mp_type_type }, .name = MP_QSTR_aes, .make_new = ucryptolib_aes_make_new, .locals_dict = (void*)&ucryptolib_aes_locals_dict, }; STATIC const mp_rom_map_elem_t mp_module_ucryptolib_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ucryptolib) }, { MP_ROM_QSTR(MP_QSTR_aes), MP_ROM_PTR(&ucryptolib_aes_type) }, #if MICROPY_PY_UCRYPTOLIB_CONSTS { MP_ROM_QSTR(MP_QSTR_MODE_ECB), MP_ROM_INT(UCRYPTOLIB_MODE_ECB) }, { MP_ROM_QSTR(MP_QSTR_MODE_CBC), MP_ROM_INT(UCRYPTOLIB_MODE_CBC) }, #if MICROPY_PY_UCRYPTOLIB_CTR { MP_ROM_QSTR(MP_QSTR_MODE_CTR), MP_ROM_INT(UCRYPTOLIB_MODE_CTR) }, #endif #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_ucryptolib_globals, mp_module_ucryptolib_globals_table); const mp_obj_module_t mp_module_ucryptolib = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ucryptolib_globals, }; #endif //MICROPY_PY_UCRYPTOLIB ================================================ FILE: extmod/moductypes.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * 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 "py/runtime.h" #include "py/objtuple.h" #include "py/binary.h" #if MICROPY_PY_UCTYPES /// \module uctypes - Access data structures in memory /// /// The module allows to define layout of raw data structure (using terms /// of C language), and then access memory buffers using this definition. /// The module also provides convenience functions to access memory buffers /// contained in Python objects or wrap memory buffers in Python objects. /// \constant UINT8_1 - uint8_t value type /// \class struct - C-like structure /// /// Encapsulalation of in-memory data structure. This class doesn't define /// any methods, only attribute access (for structure fields) and /// indexing (for pointer and array fields). /// /// Usage: /// /// # Define layout of a structure with 2 fields /// # 0 and 4 are byte offsets of fields from the beginning of struct /// # they are logically ORed with field type /// FOO_STRUCT = {"a": 0 | uctypes.UINT32, "b": 4 | uctypes.UINT8} /// /// # Example memory buffer to access (contained in bytes object) /// buf = b"\x64\0\0\0\0x14" /// /// # Create structure object referring to address of /// # the data in the buffer above /// s = uctypes.struct(FOO_STRUCT, uctypes.addressof(buf)) /// /// # Access fields /// print(s.a, s.b) /// # Result: /// # 100, 20 #define LAYOUT_LITTLE_ENDIAN (0) #define LAYOUT_BIG_ENDIAN (1) #define LAYOUT_NATIVE (2) #define VAL_TYPE_BITS 4 #define BITF_LEN_BITS 5 #define BITF_OFF_BITS 5 #define OFFSET_BITS 17 #if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 31 #error Invalid encoding field length #endif enum { UINT8, INT8, UINT16, INT16, UINT32, INT32, UINT64, INT64, BFUINT8, BFINT8, BFUINT16, BFINT16, BFUINT32, BFINT32, FLOAT32, FLOAT64, }; #define AGG_TYPE_BITS 2 enum { STRUCT, PTR, ARRAY, }; // Here we need to set sign bit right #define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 1) #define GET_TYPE(x, nbits) (((x) >> (31 - nbits)) & ((1 << nbits) - 1)) // Bit 0 is "is_signed" #define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1)) #define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits) #define IS_SCALAR_ARRAY(tuple_desc) ((tuple_desc)->len == 2) // We cannot apply the below to INT8, as their range [-128, 127] #define IS_SCALAR_ARRAY_OF_BYTES(tuple_desc) (GET_TYPE(MP_OBJ_SMALL_INT_VALUE((tuple_desc)->items[1]), VAL_TYPE_BITS) == UINT8) // "struct" in uctypes context means "structural", i.e. aggregate, type. STATIC const mp_obj_type_t uctypes_struct_type; typedef struct _mp_obj_uctypes_struct_t { mp_obj_base_t base; mp_obj_t desc; byte *addr; uint32_t flags; } mp_obj_uctypes_struct_t; STATIC NORETURN void syntax_error(void) { mp_raise_TypeError("syntax error in uctypes descriptor"); } STATIC mp_obj_t uctypes_struct_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); mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t); o->base.type = type; o->addr = (void*)(uintptr_t)mp_obj_int_get_truncated(args[0]); o->desc = args[1]; o->flags = LAYOUT_NATIVE; if (n_args == 3) { o->flags = mp_obj_get_int(args[2]); } return MP_OBJ_FROM_PTR(o); } STATIC void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); const char *typen = "unk"; if (mp_obj_is_type(self->desc, &mp_type_dict) #if MICROPY_PY_COLLECTIONS_ORDEREDDICT || mp_obj_is_type(self->desc, &mp_type_ordereddict) #endif ) { typen = "STRUCT"; } else if (mp_obj_is_type(self->desc, &mp_type_tuple)) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc); mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]); uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS); switch (agg_type) { case PTR: typen = "PTR"; break; case ARRAY: typen = "ARRAY"; break; } } else { typen = "ERROR"; } mp_printf(print, "", typen, self->addr); } // Get size of any type descriptor STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size); // Get size of scalar type descriptor static inline mp_uint_t uctypes_struct_scalar_size(int val_type) { if (val_type == FLOAT32) { return 4; } else { return GET_SCALAR_SIZE(val_type & 7); } } // Get size of aggregate type descriptor STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_uint_t *max_field_size) { mp_uint_t total_size = 0; mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]); mp_uint_t agg_type = GET_TYPE(offset_, AGG_TYPE_BITS); switch (agg_type) { case STRUCT: return uctypes_struct_size(t->items[1], layout_type, max_field_size); case PTR: if (sizeof(void*) > *max_field_size) { *max_field_size = sizeof(void*); } return sizeof(void*); case ARRAY: { mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]); uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS); arr_sz &= VALUE_MASK(VAL_TYPE_BITS); mp_uint_t item_s; if (t->len == 2) { // Elements of array are scalar item_s = GET_SCALAR_SIZE(val_type); if (item_s > *max_field_size) { *max_field_size = item_s; } } else { // Elements of array are aggregates item_s = uctypes_struct_size(t->items[2], layout_type, max_field_size); } return item_s * arr_sz; } default: assert(0); } return total_size; } STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size) { if (!mp_obj_is_type(desc_in, &mp_type_dict) #if MICROPY_PY_COLLECTIONS_ORDEREDDICT && !mp_obj_is_type(desc_in, &mp_type_ordereddict) #endif ) { if (mp_obj_is_type(desc_in, &mp_type_tuple)) { return uctypes_struct_agg_size((mp_obj_tuple_t*)MP_OBJ_TO_PTR(desc_in), layout_type, max_field_size); } else if (mp_obj_is_small_int(desc_in)) { // We allow sizeof on both type definitions and structures/structure fields, // but scalar structure field is lowered into native Python int, so all // type info is lost. So, we cannot say if it's scalar type description, // or such lowered scalar. mp_raise_TypeError("Cannot unambiguously get sizeof scalar"); } syntax_error(); } mp_obj_dict_t *d = MP_OBJ_TO_PTR(desc_in); mp_uint_t total_size = 0; for (mp_uint_t i = 0; i < d->map.alloc; i++) { if (mp_map_slot_is_filled(&d->map, i)) { mp_obj_t v = d->map.table[i].value; if (mp_obj_is_small_int(v)) { mp_uint_t offset = MP_OBJ_SMALL_INT_VALUE(v); mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS); offset &= VALUE_MASK(VAL_TYPE_BITS); if (val_type >= BFUINT8 && val_type <= BFINT32) { offset &= (1 << OFFSET_BITS) - 1; } mp_uint_t s = uctypes_struct_scalar_size(val_type); if (s > *max_field_size) { *max_field_size = s; } if (offset + s > total_size) { total_size = offset + s; } } else { if (!mp_obj_is_type(v, &mp_type_tuple)) { syntax_error(); } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(v); mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]); offset &= VALUE_MASK(AGG_TYPE_BITS); mp_uint_t s = uctypes_struct_agg_size(t, layout_type, max_field_size); if (offset + s > total_size) { total_size = offset + s; } } } } // Round size up to alignment of biggest field if (layout_type == LAYOUT_NATIVE) { total_size = (total_size + *max_field_size - 1) & ~(*max_field_size - 1); } return total_size; } STATIC mp_obj_t uctypes_struct_sizeof(size_t n_args, const mp_obj_t *args) { mp_obj_t obj_in = args[0]; mp_uint_t max_field_size = 0; if (mp_obj_is_type(obj_in, &mp_type_bytearray)) { return mp_obj_len(obj_in); } int layout_type = LAYOUT_NATIVE; // We can apply sizeof either to structure definition (a dict) // or to instantiated structure if (mp_obj_is_type(obj_in, &uctypes_struct_type)) { if (n_args != 1) { mp_raise_TypeError(NULL); } // Extract structure definition mp_obj_uctypes_struct_t *obj = MP_OBJ_TO_PTR(obj_in); obj_in = obj->desc; layout_type = obj->flags; } else { if (n_args == 2) { layout_type = mp_obj_get_int(args[1]); } } mp_uint_t size = uctypes_struct_size(obj_in, layout_type, &max_field_size); return MP_OBJ_NEW_SMALL_INT(size); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, uctypes_struct_sizeof); static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) { char struct_type = big_endian ? '>' : '<'; static const char type2char[16] = "BbHhIiQq------fd"; return mp_binary_get_val(struct_type, type2char[val_type], p, &p); } static inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) { char struct_type = big_endian ? '>' : '<'; static const char type2char[16] = "BbHhIiQq------fd"; mp_binary_set_val(struct_type, type2char[val_type], val, p, &p); } static inline mp_uint_t get_aligned_basic(uint val_type, void *p) { switch (val_type) { case UINT8: return *(uint8_t*)p; case UINT16: return *(uint16_t*)p; case UINT32: return *(uint32_t*)p; } assert(0); return 0; } static inline void set_aligned_basic(uint val_type, void *p, mp_uint_t v) { switch (val_type) { case UINT8: *(uint8_t*)p = (uint8_t)v; return; case UINT16: *(uint16_t*)p = (uint16_t)v; return; case UINT32: *(uint32_t*)p = (uint32_t)v; return; } assert(0); } STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) { switch (val_type) { case UINT8: return MP_OBJ_NEW_SMALL_INT(((uint8_t*)p)[index]); case INT8: return MP_OBJ_NEW_SMALL_INT(((int8_t*)p)[index]); case UINT16: return MP_OBJ_NEW_SMALL_INT(((uint16_t*)p)[index]); case INT16: return MP_OBJ_NEW_SMALL_INT(((int16_t*)p)[index]); case UINT32: return mp_obj_new_int_from_uint(((uint32_t*)p)[index]); case INT32: return mp_obj_new_int(((int32_t*)p)[index]); case UINT64: return mp_obj_new_int_from_ull(((uint64_t*)p)[index]); case INT64: return mp_obj_new_int_from_ll(((int64_t*)p)[index]); #if MICROPY_PY_BUILTINS_FLOAT case FLOAT32: return mp_obj_new_float(((float*)p)[index]); case FLOAT64: return mp_obj_new_float(((double*)p)[index]); #endif default: assert(0); return MP_OBJ_NULL; } } STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) { #if MICROPY_PY_BUILTINS_FLOAT if (val_type == FLOAT32 || val_type == FLOAT64) { mp_float_t v = mp_obj_get_float(val); if (val_type == FLOAT32) { ((float*)p)[index] = v; } else { ((double*)p)[index] = v; } return; } #endif mp_int_t v = mp_obj_get_int_truncated(val); switch (val_type) { case UINT8: ((uint8_t*)p)[index] = (uint8_t)v; return; case INT8: ((int8_t*)p)[index] = (int8_t)v; return; case UINT16: ((uint16_t*)p)[index] = (uint16_t)v; return; case INT16: ((int16_t*)p)[index] = (int16_t)v; return; case UINT32: ((uint32_t*)p)[index] = (uint32_t)v; return; case INT32: ((int32_t*)p)[index] = (int32_t)v; return; case INT64: case UINT64: if (sizeof(mp_int_t) == 8) { ((uint64_t*)p)[index] = (uint64_t)v; } else { // TODO: Doesn't offer atomic store semantics, but should at least try set_unaligned(val_type, p, MP_ENDIANNESS_BIG, val); } return; default: assert(0); } } STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val) { mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); if (!mp_obj_is_type(self->desc, &mp_type_dict) #if MICROPY_PY_COLLECTIONS_ORDEREDDICT && !mp_obj_is_type(self->desc, &mp_type_ordereddict) #endif ) { mp_raise_TypeError("struct: no fields"); } mp_obj_t deref = mp_obj_dict_get(self->desc, MP_OBJ_NEW_QSTR(attr)); if (mp_obj_is_small_int(deref)) { mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(deref); mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS); offset &= VALUE_MASK(VAL_TYPE_BITS); //printf("scalar type=%d offset=%x\n", val_type, offset); if (val_type <= INT64 || val_type == FLOAT32 || val_type == FLOAT64) { // printf("size=%d\n", GET_SCALAR_SIZE(val_type)); if (self->flags == LAYOUT_NATIVE) { if (set_val == MP_OBJ_NULL) { return get_aligned(val_type, self->addr + offset, 0); } else { set_aligned(val_type, self->addr + offset, 0, set_val); return set_val; // just !MP_OBJ_NULL } } else { if (set_val == MP_OBJ_NULL) { return get_unaligned(val_type, self->addr + offset, self->flags); } else { set_unaligned(val_type, self->addr + offset, self->flags, set_val); return set_val; // just !MP_OBJ_NULL } } } else if (val_type >= BFUINT8 && val_type <= BFINT32) { uint bit_offset = (offset >> 17) & 31; uint bit_len = (offset >> 22) & 31; offset &= (1 << 17) - 1; mp_uint_t val; if (self->flags == LAYOUT_NATIVE) { val = get_aligned_basic(val_type & 6, self->addr + offset); } else { val = mp_binary_get_int(GET_SCALAR_SIZE(val_type & 7), val_type & 1, self->flags, self->addr + offset); } if (set_val == MP_OBJ_NULL) { val >>= bit_offset; val &= (1 << bit_len) - 1; // TODO: signed assert((val_type & 1) == 0); return mp_obj_new_int(val); } else { mp_uint_t set_val_int = (mp_uint_t)mp_obj_get_int(set_val); mp_uint_t mask = (1 << bit_len) - 1; set_val_int &= mask; set_val_int <<= bit_offset; mask <<= bit_offset; val = (val & ~mask) | set_val_int; if (self->flags == LAYOUT_NATIVE) { set_aligned_basic(val_type & 6, self->addr + offset, val); } else { mp_binary_set_int(GET_SCALAR_SIZE(val_type & 7), self->flags == LAYOUT_BIG_ENDIAN, self->addr + offset, val); } return set_val; // just !MP_OBJ_NULL } } assert(0); return MP_OBJ_NULL; } if (!mp_obj_is_type(deref, &mp_type_tuple)) { syntax_error(); } if (set_val != MP_OBJ_NULL) { // Cannot assign to aggregate syntax_error(); } mp_obj_tuple_t *sub = MP_OBJ_TO_PTR(deref); mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[0]); mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS); offset &= VALUE_MASK(AGG_TYPE_BITS); //printf("agg type=%d offset=%x\n", agg_type, offset); switch (agg_type) { case STRUCT: { mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t); o->base.type = &uctypes_struct_type; o->desc = sub->items[1]; o->addr = self->addr + offset; o->flags = self->flags; return MP_OBJ_FROM_PTR(o); } case ARRAY: { mp_uint_t dummy; if (IS_SCALAR_ARRAY(sub) && IS_SCALAR_ARRAY_OF_BYTES(sub)) { return mp_obj_new_bytearray_by_ref(uctypes_struct_agg_size(sub, self->flags, &dummy), self->addr + offset); } // Fall thru to return uctypes struct object } case PTR: { mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t); o->base.type = &uctypes_struct_type; o->desc = MP_OBJ_FROM_PTR(sub); o->addr = self->addr + offset; o->flags = self->flags; //printf("PTR/ARR base addr=%p\n", o->addr); return MP_OBJ_FROM_PTR(o); } } // Should be unreachable once all cases are handled return MP_OBJ_NULL; } STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] == MP_OBJ_NULL) { // load attribute mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL); dest[0] = val; } else { // delete/store attribute if (uctypes_struct_attr_op(self_in, attr, dest[1]) != MP_OBJ_NULL) { dest[0] = MP_OBJ_NULL; // indicate success } } } STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); if (value == MP_OBJ_NULL) { // delete return MP_OBJ_NULL; // op not supported } else { // load / store if (!mp_obj_is_type(self->desc, &mp_type_tuple)) { mp_raise_TypeError("struct: cannot index"); } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc); mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]); uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS); mp_int_t index = MP_OBJ_SMALL_INT_VALUE(index_in); if (agg_type == ARRAY) { mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]); uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS); arr_sz &= VALUE_MASK(VAL_TYPE_BITS); if (index >= arr_sz) { mp_raise_msg(&mp_type_IndexError, "struct: index out of range"); } if (t->len == 2) { // array of scalars if (self->flags == LAYOUT_NATIVE) { if (value == MP_OBJ_SENTINEL) { return get_aligned(val_type, self->addr, index); } else { set_aligned(val_type, self->addr, index, value); return value; // just !MP_OBJ_NULL } } else { byte *p = self->addr + GET_SCALAR_SIZE(val_type) * index; if (value == MP_OBJ_SENTINEL) { return get_unaligned(val_type, p, self->flags); } else { set_unaligned(val_type, p, self->flags, value); return value; // just !MP_OBJ_NULL } } } else if (value == MP_OBJ_SENTINEL) { mp_uint_t dummy = 0; mp_uint_t size = uctypes_struct_size(t->items[2], self->flags, &dummy); mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t); o->base.type = &uctypes_struct_type; o->desc = t->items[2]; o->addr = self->addr + size * index; o->flags = self->flags; return MP_OBJ_FROM_PTR(o); } else { return MP_OBJ_NULL; // op not supported } } else if (agg_type == PTR) { byte *p = *(void**)self->addr; if (mp_obj_is_small_int(t->items[1])) { uint val_type = GET_TYPE(MP_OBJ_SMALL_INT_VALUE(t->items[1]), VAL_TYPE_BITS); return get_aligned(val_type, p, index); } else { mp_uint_t dummy = 0; mp_uint_t size = uctypes_struct_size(t->items[1], self->flags, &dummy); mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t); o->base.type = &uctypes_struct_type; o->desc = t->items[1]; o->addr = p + size * index; o->flags = self->flags; return MP_OBJ_FROM_PTR(o); } } assert(0); return MP_OBJ_NULL; } } STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) { mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); switch (op) { case MP_UNARY_OP_INT: if (mp_obj_is_type(self->desc, &mp_type_tuple)) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc); mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]); uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS); if (agg_type == PTR) { byte *p = *(void**)self->addr; return mp_obj_new_int((mp_int_t)(uintptr_t)p); } } /* fallthru */ default: return MP_OBJ_NULL; // op not supported } } STATIC mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { (void)flags; mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); mp_uint_t max_field_size = 0; mp_uint_t size = uctypes_struct_size(self->desc, self->flags, &max_field_size); bufinfo->buf = self->addr; bufinfo->len = size; bufinfo->typecode = BYTEARRAY_TYPECODE; return 0; } /// \function addressof() /// Return address of object's data (applies to object providing buffer /// interface). STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); return mp_obj_new_int((mp_int_t)(uintptr_t)bufinfo.buf); } MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_addressof_obj, uctypes_struct_addressof); /// \function bytearray_at() /// Capture memory at given address of given size as bytearray. Memory is /// captured by reference (and thus memory pointed by bytearray may change /// or become invalid at later time). Use bytes_at() to capture by value. STATIC mp_obj_t uctypes_struct_bytearray_at(mp_obj_t ptr, mp_obj_t size) { return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void*)(uintptr_t)mp_obj_int_get_truncated(ptr)); } MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytearray_at_obj, uctypes_struct_bytearray_at); /// \function bytes_at() /// Capture memory at given address of given size as bytes. Memory is /// captured by value, i.e. copied. Use bytearray_at() to capture by reference /// ("zero copy"). STATIC mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) { return mp_obj_new_bytes((void*)(uintptr_t)mp_obj_int_get_truncated(ptr), mp_obj_int_get_truncated(size)); } MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at); STATIC const mp_obj_type_t uctypes_struct_type = { { &mp_type_type }, .name = MP_QSTR_struct, .print = uctypes_struct_print, .make_new = uctypes_struct_make_new, .attr = uctypes_struct_attr, .subscr = uctypes_struct_subscr, .unary_op = uctypes_struct_unary_op, .buffer_p = { .get_buffer = uctypes_get_buffer }, }; STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uctypes) }, { MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&uctypes_struct_type) }, { MP_ROM_QSTR(MP_QSTR_sizeof), MP_ROM_PTR(&uctypes_struct_sizeof_obj) }, { MP_ROM_QSTR(MP_QSTR_addressof), MP_ROM_PTR(&uctypes_struct_addressof_obj) }, { MP_ROM_QSTR(MP_QSTR_bytes_at), MP_ROM_PTR(&uctypes_struct_bytes_at_obj) }, { MP_ROM_QSTR(MP_QSTR_bytearray_at), MP_ROM_PTR(&uctypes_struct_bytearray_at_obj) }, /// \moduleref uctypes /// \constant NATIVE - Native structure layout - native endianness, /// platform-specific field alignment { MP_ROM_QSTR(MP_QSTR_NATIVE), MP_ROM_INT(LAYOUT_NATIVE) }, /// \constant LITTLE_ENDIAN - Little-endian structure layout, tightly packed /// (no alignment constraints) { MP_ROM_QSTR(MP_QSTR_LITTLE_ENDIAN), MP_ROM_INT(LAYOUT_LITTLE_ENDIAN) }, /// \constant BIG_ENDIAN - Big-endian structure layout, tightly packed /// (no alignment constraints) { MP_ROM_QSTR(MP_QSTR_BIG_ENDIAN), MP_ROM_INT(LAYOUT_BIG_ENDIAN) }, /// \constant VOID - void value type, may be used only as pointer target type. { MP_ROM_QSTR(MP_QSTR_VOID), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) }, /// \constant UINT8 - uint8_t value type { MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, 4)) }, /// \constant INT8 - int8_t value type { MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, 4)) }, /// \constant UINT16 - uint16_t value type { MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) }, /// \constant INT16 - int16_t value type { MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) }, /// \constant UINT32 - uint32_t value type { MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) }, /// \constant INT32 - int32_t value type { MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) }, /// \constant UINT64 - uint64_t value type { MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) }, /// \constant INT64 - int64_t value type { MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) }, { MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, 4)) }, { MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, 4)) }, { MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, 4)) }, { MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, 4)) }, { MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, 4)) }, { MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, 4)) }, { MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(17) }, { MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(22) }, #if MICROPY_PY_BUILTINS_FLOAT { MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, 4)) }, { MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) }, #endif #if MICROPY_PY_UCTYPES_NATIVE_C_TYPES // C native type aliases. These depend on GCC-compatible predefined // preprocessor macros. #if __SIZEOF_SHORT__ == 2 { MP_ROM_QSTR(MP_QSTR_SHORT), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) }, { MP_ROM_QSTR(MP_QSTR_USHORT), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) }, #endif #if __SIZEOF_INT__ == 4 { MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) }, { MP_ROM_QSTR(MP_QSTR_UINT), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) }, #endif #if __SIZEOF_LONG__ == 4 { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) }, { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) }, #elif __SIZEOF_LONG__ == 8 { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) }, { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) }, #endif #if __SIZEOF_LONG_LONG__ == 8 { MP_ROM_QSTR(MP_QSTR_LONGLONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) }, { MP_ROM_QSTR(MP_QSTR_ULONGLONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) }, #endif #endif // MICROPY_PY_UCTYPES_NATIVE_C_TYPES { MP_ROM_QSTR(MP_QSTR_PTR), MP_ROM_INT(TYPE2SMALLINT(PTR, AGG_TYPE_BITS)) }, { MP_ROM_QSTR(MP_QSTR_ARRAY), MP_ROM_INT(TYPE2SMALLINT(ARRAY, AGG_TYPE_BITS)) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_uctypes_globals, mp_module_uctypes_globals_table); const mp_obj_module_t mp_module_uctypes = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_uctypes_globals, }; #endif ================================================ FILE: extmod/moduhashlib.c ================================================ /* * 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. */ #include #include #include "py/runtime.h" #if MICROPY_PY_UHASHLIB #if MICROPY_SSL_MBEDTLS #include "mbedtls/version.h" #endif #if MICROPY_PY_UHASHLIB_SHA256 #if MICROPY_SSL_MBEDTLS #include "mbedtls/sha256.h" #else #include "crypto-algorithms/sha256.h" #endif #endif #if MICROPY_PY_UHASHLIB_SHA1 || MICROPY_PY_UHASHLIB_MD5 #if MICROPY_SSL_AXTLS #include "lib/axtls/crypto/crypto.h" #endif #if MICROPY_SSL_MBEDTLS #include "mbedtls/md5.h" #include "mbedtls/sha1.h" #endif #endif typedef struct _mp_obj_hash_t { mp_obj_base_t base; char state[0]; } mp_obj_hash_t; #if MICROPY_PY_UHASHLIB_SHA256 STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg); #if MICROPY_SSL_MBEDTLS #if MBEDTLS_VERSION_NUMBER < 0x02070000 #define mbedtls_sha256_starts_ret mbedtls_sha256_starts #define mbedtls_sha256_update_ret mbedtls_sha256_update #define mbedtls_sha256_finish_ret mbedtls_sha256_finish #endif STATIC mp_obj_t uhashlib_sha256_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); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha256_context)); o->base.type = type; mbedtls_sha256_init((mbedtls_sha256_context*)&o->state); mbedtls_sha256_starts_ret((mbedtls_sha256_context*)&o->state, 0); if (n_args == 1) { uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]); } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); mbedtls_sha256_update_ret((mbedtls_sha256_context*)&self->state, bufinfo.buf, bufinfo.len); return mp_const_none; } STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); vstr_t vstr; vstr_init_len(&vstr, 32); mbedtls_sha256_finish_ret((mbedtls_sha256_context*)&self->state, (unsigned char *)vstr.buf); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } #else #include "crypto-algorithms/sha256.c" STATIC mp_obj_t uhashlib_sha256_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); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(CRYAL_SHA256_CTX)); o->base.type = type; sha256_init((CRYAL_SHA256_CTX*)o->state); if (n_args == 1) { uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]); } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); sha256_update((CRYAL_SHA256_CTX*)self->state, bufinfo.buf, bufinfo.len); return mp_const_none; } STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); vstr_t vstr; vstr_init_len(&vstr, SHA256_BLOCK_SIZE); sha256_final((CRYAL_SHA256_CTX*)self->state, (byte*)vstr.buf); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } #endif STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_sha256_update_obj, uhashlib_sha256_update); STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_sha256_digest_obj, uhashlib_sha256_digest); STATIC const mp_rom_map_elem_t uhashlib_sha256_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_sha256_update_obj) }, { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_sha256_digest_obj) }, }; STATIC MP_DEFINE_CONST_DICT(uhashlib_sha256_locals_dict, uhashlib_sha256_locals_dict_table); STATIC const mp_obj_type_t uhashlib_sha256_type = { { &mp_type_type }, .name = MP_QSTR_sha256, .make_new = uhashlib_sha256_make_new, .locals_dict = (void*)&uhashlib_sha256_locals_dict, }; #endif #if MICROPY_PY_UHASHLIB_SHA1 STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg); #if MICROPY_SSL_AXTLS STATIC mp_obj_t uhashlib_sha1_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); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(SHA1_CTX)); o->base.type = type; SHA1_Init((SHA1_CTX*)o->state); if (n_args == 1) { uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]); } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); SHA1_Update((SHA1_CTX*)self->state, bufinfo.buf, bufinfo.len); return mp_const_none; } STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); vstr_t vstr; vstr_init_len(&vstr, SHA1_SIZE); SHA1_Final((byte*)vstr.buf, (SHA1_CTX*)self->state); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } #endif #if MICROPY_SSL_MBEDTLS #if MBEDTLS_VERSION_NUMBER < 0x02070000 #define mbedtls_sha1_starts_ret mbedtls_sha1_starts #define mbedtls_sha1_update_ret mbedtls_sha1_update #define mbedtls_sha1_finish_ret mbedtls_sha1_finish #endif STATIC mp_obj_t uhashlib_sha1_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); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha1_context)); o->base.type = type; mbedtls_sha1_init((mbedtls_sha1_context*)o->state); mbedtls_sha1_starts_ret((mbedtls_sha1_context*)o->state); if (n_args == 1) { uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]); } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); mbedtls_sha1_update_ret((mbedtls_sha1_context*)self->state, bufinfo.buf, bufinfo.len); return mp_const_none; } STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); vstr_t vstr; vstr_init_len(&vstr, 20); mbedtls_sha1_finish_ret((mbedtls_sha1_context*)self->state, (byte*)vstr.buf); mbedtls_sha1_free((mbedtls_sha1_context*)self->state); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } #endif STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_sha1_update_obj, uhashlib_sha1_update); STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_sha1_digest_obj, uhashlib_sha1_digest); STATIC const mp_rom_map_elem_t uhashlib_sha1_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_sha1_update_obj) }, { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_sha1_digest_obj) }, }; STATIC MP_DEFINE_CONST_DICT(uhashlib_sha1_locals_dict, uhashlib_sha1_locals_dict_table); STATIC const mp_obj_type_t uhashlib_sha1_type = { { &mp_type_type }, .name = MP_QSTR_sha1, .make_new = uhashlib_sha1_make_new, .locals_dict = (void*)&uhashlib_sha1_locals_dict, }; #endif #if MICROPY_PY_UHASHLIB_MD5 STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg); #if MICROPY_SSL_AXTLS STATIC mp_obj_t uhashlib_md5_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); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(MD5_CTX)); o->base.type = type; MD5_Init((MD5_CTX*)o->state); if (n_args == 1) { uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]); } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); MD5_Update((MD5_CTX*)self->state, bufinfo.buf, bufinfo.len); return mp_const_none; } STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); vstr_t vstr; vstr_init_len(&vstr, MD5_SIZE); MD5_Final((byte*)vstr.buf, (MD5_CTX*)self->state); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } #endif // MICROPY_SSL_AXTLS #if MICROPY_SSL_MBEDTLS #if MBEDTLS_VERSION_NUMBER < 0x02070000 #define mbedtls_md5_starts_ret mbedtls_md5_starts #define mbedtls_md5_update_ret mbedtls_md5_update #define mbedtls_md5_finish_ret mbedtls_md5_finish #endif STATIC mp_obj_t uhashlib_md5_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); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_md5_context)); o->base.type = type; mbedtls_md5_init((mbedtls_md5_context*)o->state); mbedtls_md5_starts_ret((mbedtls_md5_context*)o->state); if (n_args == 1) { uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]); } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); mbedtls_md5_update_ret((mbedtls_md5_context*)self->state, bufinfo.buf, bufinfo.len); return mp_const_none; } STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); vstr_t vstr; vstr_init_len(&vstr, 16); mbedtls_md5_finish_ret((mbedtls_md5_context*)self->state, (byte*)vstr.buf); mbedtls_md5_free((mbedtls_md5_context*)self->state); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } #endif // MICROPY_SSL_MBEDTLS STATIC MP_DEFINE_CONST_FUN_OBJ_2(uhashlib_md5_update_obj, uhashlib_md5_update); STATIC MP_DEFINE_CONST_FUN_OBJ_1(uhashlib_md5_digest_obj, uhashlib_md5_digest); STATIC const mp_rom_map_elem_t uhashlib_md5_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&uhashlib_md5_update_obj) }, { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&uhashlib_md5_digest_obj) }, }; STATIC MP_DEFINE_CONST_DICT(uhashlib_md5_locals_dict, uhashlib_md5_locals_dict_table); STATIC const mp_obj_type_t uhashlib_md5_type = { { &mp_type_type }, .name = MP_QSTR_md5, .make_new = uhashlib_md5_make_new, .locals_dict = (void*)&uhashlib_md5_locals_dict, }; #endif // MICROPY_PY_UHASHLIB_MD5 STATIC const mp_rom_map_elem_t mp_module_uhashlib_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uhashlib) }, #if MICROPY_PY_UHASHLIB_SHA256 { MP_ROM_QSTR(MP_QSTR_sha256), MP_ROM_PTR(&uhashlib_sha256_type) }, #endif #if MICROPY_PY_UHASHLIB_SHA1 { MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&uhashlib_sha1_type) }, #endif #if MICROPY_PY_UHASHLIB_MD5 { MP_ROM_QSTR(MP_QSTR_md5), MP_ROM_PTR(&uhashlib_md5_type) }, #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_uhashlib_globals, mp_module_uhashlib_globals_table); const mp_obj_module_t mp_module_uhashlib = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_uhashlib_globals, }; #endif //MICROPY_PY_UHASHLIB ================================================ FILE: extmod/moduheapq.c ================================================ /* * 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/objlist.h" #include "py/runtime.h" #if MICROPY_PY_UHEAPQ // the algorithm here is modelled on CPython's heapq.py STATIC mp_obj_list_t *uheapq_get_heap(mp_obj_t heap_in) { if (!mp_obj_is_type(heap_in, &mp_type_list)) { mp_raise_TypeError("heap must be a list"); } return MP_OBJ_TO_PTR(heap_in); } STATIC void uheapq_heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) { mp_obj_t item = heap->items[pos]; while (pos > start_pos) { mp_uint_t parent_pos = (pos - 1) >> 1; mp_obj_t parent = heap->items[parent_pos]; if (mp_binary_op(MP_BINARY_OP_LESS, item, parent) == mp_const_true) { heap->items[pos] = parent; pos = parent_pos; } else { break; } } heap->items[pos] = item; } STATIC void uheapq_heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) { mp_uint_t start_pos = pos; mp_uint_t end_pos = heap->len; mp_obj_t item = heap->items[pos]; for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) { // choose right child if it's <= left child if (child_pos + 1 < end_pos && mp_binary_op(MP_BINARY_OP_LESS, heap->items[child_pos], heap->items[child_pos + 1]) == mp_const_false) { child_pos += 1; } // bubble up the smaller child heap->items[pos] = heap->items[child_pos]; pos = child_pos; } heap->items[pos] = item; uheapq_heap_siftdown(heap, start_pos, pos); } STATIC mp_obj_t mod_uheapq_heappush(mp_obj_t heap_in, mp_obj_t item) { mp_obj_list_t *heap = uheapq_get_heap(heap_in); mp_obj_list_append(heap_in, item); uheapq_heap_siftdown(heap, 0, heap->len - 1); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_uheapq_heappush_obj, mod_uheapq_heappush); STATIC mp_obj_t mod_uheapq_heappop(mp_obj_t heap_in) { mp_obj_list_t *heap = uheapq_get_heap(heap_in); if (heap->len == 0) { mp_raise_msg(&mp_type_IndexError, "empty heap"); } mp_obj_t item = heap->items[0]; heap->len -= 1; heap->items[0] = heap->items[heap->len]; heap->items[heap->len] = MP_OBJ_NULL; // so we don't retain a pointer if (heap->len) { uheapq_heap_siftup(heap, 0); } return item; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heappop_obj, mod_uheapq_heappop); STATIC mp_obj_t mod_uheapq_heapify(mp_obj_t heap_in) { mp_obj_list_t *heap = uheapq_get_heap(heap_in); for (mp_uint_t i = heap->len / 2; i > 0;) { uheapq_heap_siftup(heap, --i); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heapify_obj, mod_uheapq_heapify); #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_uheapq_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uheapq) }, { MP_ROM_QSTR(MP_QSTR_heappush), MP_ROM_PTR(&mod_uheapq_heappush_obj) }, { MP_ROM_QSTR(MP_QSTR_heappop), MP_ROM_PTR(&mod_uheapq_heappop_obj) }, { MP_ROM_QSTR(MP_QSTR_heapify), MP_ROM_PTR(&mod_uheapq_heapify_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_uheapq_globals, mp_module_uheapq_globals_table); const mp_obj_module_t mp_module_uheapq = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_uheapq_globals, }; #endif #endif //MICROPY_PY_UHEAPQ ================================================ FILE: extmod/modujson.c ================================================ /* * 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_UJSON STATIC mp_obj_t mod_ujson_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_ujson_dump_obj, mod_ujson_dump); STATIC mp_obj_t mod_ujson_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_vstr(&mp_type_str, &vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps); // 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 _ujson_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; } ujson_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) (ujson_stream_next(&(s))) STATIC byte ujson_stream_next(ujson_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_ujson_load(mp_obj_t stream_obj) { const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ); ujson_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; 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_decimal(vstr.buf, vstr.len, false, 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("syntax error in JSON"); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_load_obj, mod_ujson_load); STATIC mp_obj_t mod_ujson_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_ujson_load(MP_OBJ_FROM_PTR(&sio)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_loads_obj, mod_ujson_loads); STATIC const mp_rom_map_elem_t mp_module_ujson_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ujson) }, { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_ujson_dump_obj) }, { MP_ROM_QSTR(MP_QSTR_dumps), MP_ROM_PTR(&mod_ujson_dumps_obj) }, { MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&mod_ujson_load_obj) }, { MP_ROM_QSTR(MP_QSTR_loads), MP_ROM_PTR(&mod_ujson_loads_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_ujson_globals, mp_module_ujson_globals_table); const mp_obj_module_t mp_module_ujson = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ujson_globals, }; #endif //MICROPY_PY_UJSON ================================================ FILE: extmod/modurandom.c ================================================ /* * 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_URANDOM // Yasmarang random number generator // by Ilya Levin // http://www.literatecode.com/yasmarang // Public Domain #if !MICROPY_ENABLE_DYNRUNTIME STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233; STATIC uint8_t yasmarang_dat = 0; #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_URANDOM_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_urandom_getrandbits(mp_obj_t num_in) { int n = mp_obj_get_int(num_in); if (n > 32 || n == 0) { mp_raise_ValueError(NULL); } 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_urandom_getrandbits_obj, mod_urandom_getrandbits); STATIC mp_obj_t mod_urandom_seed(mp_obj_t seed_in) { mp_uint_t seed = mp_obj_get_int_truncated(seed_in); yasmarang_pad = seed; yasmarang_n = 69; yasmarang_d = 233; yasmarang_dat = 0; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_seed_obj, mod_urandom_seed); #if MICROPY_PY_URANDOM_EXTRA_FUNCS STATIC mp_obj_t mod_urandom_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(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(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(n)); } else { goto error; } } } error: mp_raise_ValueError(NULL); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_urandom_randrange_obj, 1, 3, mod_urandom_randrange); STATIC mp_obj_t mod_urandom_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(b - a + 1)); } else { mp_raise_ValueError(NULL); } } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_randint_obj, mod_urandom_randint); STATIC mp_obj_t mod_urandom_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(len)), MP_OBJ_SENTINEL); } else { nlr_raise(mp_obj_new_exception(&mp_type_IndexError)); } } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_choice_obj, mod_urandom_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) { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE typedef uint64_t mp_float_int_t; #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT typedef uint32_t mp_float_int_t; #endif union { mp_float_t f; #if MP_ENDIANNESS_LITTLE struct { mp_float_int_t frc:MP_FLOAT_FRAC_BITS, exp:MP_FLOAT_EXP_BITS, sgn:1; } p; #else struct { mp_float_int_t sgn:1, exp:MP_FLOAT_EXP_BITS, frc:MP_FLOAT_FRAC_BITS; } p; #endif } 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_urandom_random(void) { return mp_obj_new_float(yasmarang_float()); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom_random_obj, mod_urandom_random); STATIC mp_obj_t mod_urandom_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_urandom_uniform_obj, mod_urandom_uniform); #endif #endif // MICROPY_PY_URANDOM_EXTRA_FUNCS #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC STATIC mp_obj_t mod_urandom___init__() { mod_urandom_seed(MP_OBJ_NEW_SMALL_INT(MICROPY_PY_URANDOM_SEED_INIT_FUNC)); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__); #endif #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_urandom) }, #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&mod_urandom___init___obj) }, #endif { MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&mod_urandom_getrandbits_obj) }, { MP_ROM_QSTR(MP_QSTR_seed), MP_ROM_PTR(&mod_urandom_seed_obj) }, #if MICROPY_PY_URANDOM_EXTRA_FUNCS { MP_ROM_QSTR(MP_QSTR_randrange), MP_ROM_PTR(&mod_urandom_randrange_obj) }, { MP_ROM_QSTR(MP_QSTR_randint), MP_ROM_PTR(&mod_urandom_randint_obj) }, { MP_ROM_QSTR(MP_QSTR_choice), MP_ROM_PTR(&mod_urandom_choice_obj) }, #if MICROPY_PY_BUILTINS_FLOAT { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mod_urandom_random_obj) }, { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&mod_urandom_uniform_obj) }, #endif #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_urandom_globals, mp_module_urandom_globals_table); const mp_obj_module_t mp_module_urandom = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_urandom_globals, }; #endif #endif //MICROPY_PY_URANDOM ================================================ FILE: extmod/modure.c ================================================ /* * 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. */ #include #include #include #include "py/runtime.h" #include "py/binary.h" #include "py/objstr.h" #include "py/stackctrl.h" #if MICROPY_PY_URE #define re1_5_stack_chk() MP_STACK_CHECK() #include "re1.5/re1.5.h" #define FLAG_DEBUG 0x1000 typedef struct _mp_obj_re_t { mp_obj_base_t base; ByteProg re; } mp_obj_re_t; typedef struct _mp_obj_match_t { mp_obj_base_t base; int num_matches; mp_obj_t str; const char *caps[0]; } mp_obj_match_t; STATIC void match_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", self->num_matches); } STATIC mp_obj_t match_group(mp_obj_t self_in, mp_obj_t no_in) { mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t no = mp_obj_get_int(no_in); if (no < 0 || no >= self->num_matches) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, no_in)); } const char *start = self->caps[no * 2]; if (start == NULL) { // no match for this group return mp_const_none; } return mp_obj_new_str_of_type(mp_obj_get_type(self->str), (const byte*)start, self->caps[no * 2 + 1] - start); } MP_DEFINE_CONST_FUN_OBJ_2(match_group_obj, match_group); #if MICROPY_PY_URE_MATCH_GROUPS STATIC mp_obj_t match_groups(mp_obj_t self_in) { mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in); if (self->num_matches <= 1) { return mp_const_empty_tuple; } mp_obj_tuple_t *groups = MP_OBJ_TO_PTR(mp_obj_new_tuple(self->num_matches - 1, NULL)); for (int i = 1; i < self->num_matches; ++i) { groups->items[i - 1] = match_group(self_in, MP_OBJ_NEW_SMALL_INT(i)); } return MP_OBJ_FROM_PTR(groups); } MP_DEFINE_CONST_FUN_OBJ_1(match_groups_obj, match_groups); #endif #if MICROPY_PY_URE_MATCH_SPAN_START_END STATIC void match_span_helper(size_t n_args, const mp_obj_t *args, mp_obj_t span[2]) { mp_obj_match_t *self = MP_OBJ_TO_PTR(args[0]); mp_int_t no = 0; if (n_args == 2) { no = mp_obj_get_int(args[1]); if (no < 0 || no >= self->num_matches) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, args[1])); } } mp_int_t s = -1; mp_int_t e = -1; const char *start = self->caps[no * 2]; if (start != NULL) { // have a match for this group const char *begin = mp_obj_str_get_str(self->str); s = start - begin; e = self->caps[no * 2 + 1] - begin; } span[0] = mp_obj_new_int(s); span[1] = mp_obj_new_int(e); } STATIC mp_obj_t match_span(size_t n_args, const mp_obj_t *args) { mp_obj_t span[2]; match_span_helper(n_args, args, span); return mp_obj_new_tuple(2, span); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_span_obj, 1, 2, match_span); STATIC mp_obj_t match_start(size_t n_args, const mp_obj_t *args) { mp_obj_t span[2]; match_span_helper(n_args, args, span); return span[0]; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_start_obj, 1, 2, match_start); STATIC mp_obj_t match_end(size_t n_args, const mp_obj_t *args) { mp_obj_t span[2]; match_span_helper(n_args, args, span); return span[1]; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_end_obj, 1, 2, match_end); #endif #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t match_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_group), MP_ROM_PTR(&match_group_obj) }, #if MICROPY_PY_URE_MATCH_GROUPS { MP_ROM_QSTR(MP_QSTR_groups), MP_ROM_PTR(&match_groups_obj) }, #endif #if MICROPY_PY_URE_MATCH_SPAN_START_END { MP_ROM_QSTR(MP_QSTR_span), MP_ROM_PTR(&match_span_obj) }, { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&match_start_obj) }, { MP_ROM_QSTR(MP_QSTR_end), MP_ROM_PTR(&match_end_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(match_locals_dict, match_locals_dict_table); STATIC const mp_obj_type_t match_type = { { &mp_type_type }, .name = MP_QSTR_match, .print = match_print, .locals_dict = (void*)&match_locals_dict, }; #endif STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", self); } STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { (void)n_args; mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]); Subject subj; size_t len; subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; int caps_num = (self->re.sub + 1) * 2; mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, char*, caps_num); // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char memset((char*)match->caps, 0, caps_num * sizeof(char*)); int res = re1_5_recursiveloopprog(&self->re, &subj, match->caps, caps_num, is_anchored); if (res == 0) { m_del_var(mp_obj_match_t, char*, caps_num, match); return mp_const_none; } match->base.type = &match_type; match->num_matches = caps_num / 2; // caps_num counts start and end pointers match->str = args[1]; return MP_OBJ_FROM_PTR(match); } STATIC mp_obj_t re_match(size_t n_args, const mp_obj_t *args) { return ure_exec(true, n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_match_obj, 2, 4, re_match); STATIC mp_obj_t re_search(size_t n_args, const mp_obj_t *args) { return ure_exec(false, n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_search_obj, 2, 4, re_search); STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) { mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]); Subject subj; size_t len; const mp_obj_type_t *str_type = mp_obj_get_type(args[1]); subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; int caps_num = (self->re.sub + 1) * 2; int maxsplit = 0; if (n_args > 2) { maxsplit = mp_obj_get_int(args[2]); } mp_obj_t retval = mp_obj_new_list(0, NULL); const char **caps = mp_local_alloc(caps_num * sizeof(char*)); while (true) { // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char memset((char**)caps, 0, caps_num * sizeof(char*)); int res = re1_5_recursiveloopprog(&self->re, &subj, caps, caps_num, false); // if we didn't have a match, or had an empty match, it's time to stop if (!res || caps[0] == caps[1]) { break; } mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, caps[0] - subj.begin); mp_obj_list_append(retval, s); if (self->re.sub > 0) { mp_raise_NotImplementedError("Splitting with sub-captures"); } subj.begin = caps[1]; if (maxsplit > 0 && --maxsplit == 0) { break; } } // cast is a workaround for a bug in msvc (see above) mp_local_free((char**)caps); mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, subj.end - subj.begin); mp_obj_list_append(retval, s); return retval; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split); #if MICROPY_PY_URE_SUB STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *args) { mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t replace = args[1]; mp_obj_t where = args[2]; mp_int_t count = 0; if (n_args > 3) { count = mp_obj_get_int(args[3]); // Note: flags are currently ignored } size_t where_len; const char *where_str = mp_obj_str_get_data(where, &where_len); Subject subj; subj.begin = where_str; subj.end = subj.begin + where_len; int caps_num = (self->re.sub + 1) * 2; vstr_t vstr_return; vstr_return.buf = NULL; // We'll init the vstr after the first match mp_obj_match_t *match = mp_local_alloc(sizeof(mp_obj_match_t) + caps_num * sizeof(char*)); match->base.type = &match_type; match->num_matches = caps_num / 2; // caps_num counts start and end pointers match->str = where; for (;;) { // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char memset((char*)match->caps, 0, caps_num * sizeof(char*)); int res = re1_5_recursiveloopprog(&self->re, &subj, match->caps, caps_num, false); // If we didn't have a match, or had an empty match, it's time to stop if (!res || match->caps[0] == match->caps[1]) { break; } // Initialise the vstr if it's not already if (vstr_return.buf == NULL) { vstr_init(&vstr_return, match->caps[0] - subj.begin); } // Add pre-match string vstr_add_strn(&vstr_return, subj.begin, match->caps[0] - subj.begin); // Get replacement string const char* repl = mp_obj_str_get_str((mp_obj_is_callable(replace) ? mp_call_function_1(replace, MP_OBJ_FROM_PTR(match)) : replace)); // Append replacement string to result, substituting any regex groups while (*repl != '\0') { if (*repl == '\\') { ++repl; bool is_g_format = false; if (*repl == 'g' && repl[1] == '<') { // Group specified with syntax "\g" repl += 2; is_g_format = true; } if ('0' <= *repl && *repl <= '9') { // Group specified with syntax "\g" or "\number" unsigned int match_no = 0; do { match_no = match_no * 10 + (*repl++ - '0'); } while ('0' <= *repl && *repl <= '9'); if (is_g_format && *repl == '>') { ++repl; } if (match_no >= (unsigned int)match->num_matches) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, MP_OBJ_NEW_SMALL_INT(match_no))); } const char *start_match = match->caps[match_no * 2]; if (start_match != NULL) { // Add the substring matched by group const char *end_match = match->caps[match_no * 2 + 1]; vstr_add_strn(&vstr_return, start_match, end_match - start_match); } } } else { // Just add the current byte from the replacement string vstr_add_byte(&vstr_return, *repl++); } } // Move start pointer to end of last match subj.begin = match->caps[1]; // Stop substitutions if count was given and gets to 0 if (count > 0 && --count == 0) { break; } } mp_local_free(match); if (vstr_return.buf == NULL) { // Optimisation for case of no substitutions return where; } // Add post-match string vstr_add_strn(&vstr_return, subj.begin, subj.end - subj.begin); return mp_obj_new_str_from_vstr(mp_obj_get_type(where), &vstr_return); } STATIC mp_obj_t re_sub(size_t n_args, const mp_obj_t *args) { return re_sub_helper(args[0], n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub); #endif #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t re_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&re_match_obj) }, { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&re_search_obj) }, { MP_ROM_QSTR(MP_QSTR_split), MP_ROM_PTR(&re_split_obj) }, #if MICROPY_PY_URE_SUB { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&re_sub_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(re_locals_dict, re_locals_dict_table); STATIC const mp_obj_type_t re_type = { { &mp_type_type }, .name = MP_QSTR_ure, .print = re_print, .locals_dict = (void*)&re_locals_dict, }; #endif STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { (void)n_args; const char *re_str = mp_obj_str_get_str(args[0]); int size = re1_5_sizecode(re_str); if (size == -1) { goto error; } mp_obj_re_t *o = m_new_obj_var(mp_obj_re_t, char, size); o->base.type = &re_type; #if MICROPY_PY_URE_DEBUG int flags = 0; if (n_args > 1) { flags = mp_obj_get_int(args[1]); } #endif int error = re1_5_compilecode(&o->re, re_str); if (error != 0) { error: mp_raise_ValueError("Error in regex"); } #if MICROPY_PY_URE_DEBUG if (flags & FLAG_DEBUG) { re1_5_dumpcode(&o->re); } #endif return MP_OBJ_FROM_PTR(o); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_compile_obj, 1, 2, mod_re_compile); STATIC mp_obj_t mod_re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { (void)n_args; mp_obj_t self = mod_re_compile(1, args); const mp_obj_t args2[] = {self, args[1]}; mp_obj_t match = ure_exec(is_anchored, 2, args2); return match; } STATIC mp_obj_t mod_re_match(size_t n_args, const mp_obj_t *args) { return mod_re_exec(true, n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_match_obj, 2, 4, mod_re_match); STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) { return mod_re_exec(false, n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_search_obj, 2, 4, mod_re_search); #if MICROPY_PY_URE_SUB STATIC mp_obj_t mod_re_sub(size_t n_args, const mp_obj_t *args) { mp_obj_t self = mod_re_compile(1, args); return re_sub_helper(self, n_args, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_sub_obj, 3, 5, mod_re_sub); #endif #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ure) }, { MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mod_re_compile_obj) }, { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&mod_re_match_obj) }, { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&mod_re_search_obj) }, #if MICROPY_PY_URE_SUB { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&mod_re_sub_obj) }, #endif #if MICROPY_PY_URE_DEBUG { MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) }, #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_re_globals, mp_module_re_globals_table); const mp_obj_module_t mp_module_ure = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_re_globals, }; #endif // Source files #include'd here to make sure they're compiled in // only if module is enabled by config setting. #define re1_5_fatal(x) assert(!x) #include "re1.5/compilecode.c" #if MICROPY_PY_URE_DEBUG #include "re1.5/dumpcode.c" #endif #include "re1.5/recursiveloop.c" #include "re1.5/charclass.c" #endif //MICROPY_PY_URE ================================================ FILE: extmod/moduselect.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2014 Damien P. George * Copyright (c) 2015-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/mpconfig.h" #if MICROPY_PY_USELECT #include #include "py/runtime.h" #include "py/obj.h" #include "py/objlist.h" #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" // Flags for poll() #define FLAG_ONESHOT (1) /// \module select - Provides select function to wait for events on a stream /// /// This module provides the select function. typedef struct _poll_obj_t { mp_obj_t obj; mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); mp_uint_t flags; mp_uint_t flags_ret; } poll_obj_t; STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) { for (mp_uint_t i = 0; i < obj_len; i++) { mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); if (elem->value == MP_OBJ_NULL) { // object not found; get its ioctl and add it to the poll list const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL); poll_obj_t *poll_obj = m_new_obj(poll_obj_t); poll_obj->obj = obj[i]; poll_obj->ioctl = stream_p->ioctl; poll_obj->flags = flags; poll_obj->flags_ret = 0; elem->value = MP_OBJ_FROM_PTR(poll_obj); } else { // object exists; update its flags if (or_flags) { ((poll_obj_t*)MP_OBJ_TO_PTR(elem->value))->flags |= flags; } else { ((poll_obj_t*)MP_OBJ_TO_PTR(elem->value))->flags = flags; } } } } // poll each object in the map STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, size_t *rwx_num) { mp_uint_t n_ready = 0; for (mp_uint_t i = 0; i < poll_map->alloc; ++i) { if (!mp_map_slot_is_filled(poll_map, i)) { continue; } poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_map->table[i].value); int errcode; mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_STREAM_POLL, poll_obj->flags, &errcode); poll_obj->flags_ret = ret; if (ret == -1) { // error doing ioctl mp_raise_OSError(errcode); } if (ret != 0) { // object is ready n_ready += 1; if (rwx_num != NULL) { if (ret & MP_STREAM_POLL_RD) { rwx_num[0] += 1; } if (ret & MP_STREAM_POLL_WR) { rwx_num[1] += 1; } if ((ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) { rwx_num[2] += 1; } } } } return n_ready; } /// \function select(rlist, wlist, xlist[, timeout]) STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { // get array data from tuple/list arguments size_t rwx_len[3]; mp_obj_t *r_array, *w_array, *x_array; mp_obj_get_array(args[0], &rwx_len[0], &r_array); mp_obj_get_array(args[1], &rwx_len[1], &w_array); mp_obj_get_array(args[2], &rwx_len[2], &x_array); // get timeout mp_uint_t timeout = -1; if (n_args == 4) { if (args[3] != mp_const_none) { #if MICROPY_PY_BUILTINS_FLOAT float timeout_f = mp_obj_get_float(args[3]); if (timeout_f >= 0) { timeout = (mp_uint_t)(timeout_f * 1000); } #else timeout = mp_obj_get_int(args[3]) * 1000; #endif } } // merge separate lists and get the ioctl function for each object mp_map_t poll_map; mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]); poll_map_add(&poll_map, r_array, rwx_len[0], MP_STREAM_POLL_RD, true); poll_map_add(&poll_map, w_array, rwx_len[1], MP_STREAM_POLL_WR, true); poll_map_add(&poll_map, x_array, rwx_len[2], MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, true); mp_uint_t start_tick = mp_hal_ticks_ms(); rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; for (;;) { // poll the objects mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len); if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { // one or more objects are ready, or we had a timeout mp_obj_t list_array[3]; list_array[0] = mp_obj_new_list(rwx_len[0], NULL); list_array[1] = mp_obj_new_list(rwx_len[1], NULL); list_array[2] = mp_obj_new_list(rwx_len[2], NULL); rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; for (mp_uint_t i = 0; i < poll_map.alloc; ++i) { if (!mp_map_slot_is_filled(&poll_map, i)) { continue; } poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_map.table[i].value); if (poll_obj->flags_ret & MP_STREAM_POLL_RD) { ((mp_obj_list_t*)MP_OBJ_TO_PTR(list_array[0]))->items[rwx_len[0]++] = poll_obj->obj; } if (poll_obj->flags_ret & MP_STREAM_POLL_WR) { ((mp_obj_list_t*)MP_OBJ_TO_PTR(list_array[1]))->items[rwx_len[1]++] = poll_obj->obj; } if ((poll_obj->flags_ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) { ((mp_obj_list_t*)MP_OBJ_TO_PTR(list_array[2]))->items[rwx_len[2]++] = poll_obj->obj; } } mp_map_deinit(&poll_map); return mp_obj_new_tuple(3, list_array); } MICROPY_EVENT_POLL_HOOK } } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); /// \class Poll - poll class typedef struct _mp_obj_poll_t { mp_obj_base_t base; mp_map_t poll_map; short iter_cnt; short iter_idx; int flags; // callee-owned tuple mp_obj_t ret_tuple; } mp_obj_poll_t; /// \method register(obj[, eventmask]) STATIC mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]); mp_uint_t flags; if (n_args == 3) { flags = mp_obj_get_int(args[2]); } else { flags = MP_STREAM_POLL_RD | MP_STREAM_POLL_WR; } poll_map_add(&self->poll_map, &args[1], 1, flags, false); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register); /// \method unregister(obj) STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in); mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND); // TODO raise KeyError if obj didn't exist in map return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister); /// \method modify(obj, eventmask) STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in); mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP); if (elem == NULL) { mp_raise_OSError(MP_ENOENT); } ((poll_obj_t*)MP_OBJ_TO_PTR(elem->value))->flags = mp_obj_get_int(eventmask_in); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify); STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]); // work out timeout (its given already in ms) mp_uint_t timeout = -1; int flags = 0; if (n_args >= 2) { if (args[1] != mp_const_none) { mp_int_t timeout_i = mp_obj_get_int(args[1]); if (timeout_i >= 0) { timeout = timeout_i; } } if (n_args >= 3) { flags = mp_obj_get_int(args[2]); } } self->flags = flags; mp_uint_t start_tick = mp_hal_ticks_ms(); mp_uint_t n_ready; for (;;) { // poll the objects n_ready = poll_map_poll(&self->poll_map, NULL); if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { break; } MICROPY_EVENT_POLL_HOOK } return n_ready; } STATIC mp_obj_t poll_poll(size_t n_args, const mp_obj_t *args) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]); mp_uint_t n_ready = poll_poll_internal(n_args, args); // one or more objects are ready, or we had a timeout mp_obj_list_t *ret_list = MP_OBJ_TO_PTR(mp_obj_new_list(n_ready, NULL)); n_ready = 0; for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) { if (!mp_map_slot_is_filled(&self->poll_map, i)) { continue; } poll_obj_t *poll_obj = MP_OBJ_TO_PTR(self->poll_map.table[i].value); if (poll_obj->flags_ret != 0) { mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)}; ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple); if (self->flags & FLAG_ONESHOT) { // Don't poll next time, until new event flags will be set explicitly poll_obj->flags = 0; } } } return MP_OBJ_FROM_PTR(ret_list); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll); STATIC mp_obj_t poll_ipoll(size_t n_args, const mp_obj_t *args) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]); if (self->ret_tuple == MP_OBJ_NULL) { self->ret_tuple = mp_obj_new_tuple(2, NULL); } int n_ready = poll_poll_internal(n_args, args); self->iter_cnt = n_ready; self->iter_idx = 0; return args[0]; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll); STATIC mp_obj_t poll_iternext(mp_obj_t self_in) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in); if (self->iter_cnt == 0) { return MP_OBJ_STOP_ITERATION; } self->iter_cnt--; for (mp_uint_t i = self->iter_idx; i < self->poll_map.alloc; ++i) { self->iter_idx++; if (!mp_map_slot_is_filled(&self->poll_map, i)) { continue; } poll_obj_t *poll_obj = MP_OBJ_TO_PTR(self->poll_map.table[i].value); if (poll_obj->flags_ret != 0) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->ret_tuple); t->items[0] = poll_obj->obj; t->items[1] = MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret); if (self->flags & FLAG_ONESHOT) { // Don't poll next time, until new event flags will be set explicitly poll_obj->flags = 0; } return MP_OBJ_FROM_PTR(t); } } assert(!"inconsistent number of poll active entries"); self->iter_cnt = 0; return MP_OBJ_STOP_ITERATION; } STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) }, { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) }, { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) }, { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) }, { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) }, }; STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); STATIC const mp_obj_type_t mp_type_poll = { { &mp_type_type }, .name = MP_QSTR_poll, .getiter = mp_identity_getiter, .iternext = poll_iternext, .locals_dict = (void*)&poll_locals_dict, }; /// \function poll() STATIC mp_obj_t select_poll(void) { mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t); poll->base.type = &mp_type_poll; mp_map_init(&poll->poll_map, 0); poll->iter_cnt = 0; poll->ret_tuple = MP_OBJ_NULL; return MP_OBJ_FROM_PTR(poll); } MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll); STATIC const mp_rom_map_elem_t mp_module_select_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uselect) }, { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_select_select_obj) }, { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) }, { MP_ROM_QSTR(MP_QSTR_POLLIN), MP_ROM_INT(MP_STREAM_POLL_RD) }, { MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_ROM_INT(MP_STREAM_POLL_WR) }, { MP_ROM_QSTR(MP_QSTR_POLLERR), MP_ROM_INT(MP_STREAM_POLL_ERR) }, { MP_ROM_QSTR(MP_QSTR_POLLHUP), MP_ROM_INT(MP_STREAM_POLL_HUP) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table); const mp_obj_module_t mp_module_uselect = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_select_globals, }; #endif // MICROPY_PY_USELECT ================================================ FILE: extmod/modussl_axtls.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2015-2019 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/stream.h" #include "py/objstr.h" #if MICROPY_PY_USSL && MICROPY_SSL_AXTLS #include "ssl.h" typedef struct _mp_obj_ssl_socket_t { mp_obj_base_t base; mp_obj_t sock; SSL_CTX *ssl_ctx; SSL *ssl_sock; byte *buf; uint32_t bytes_left; bool blocking; } mp_obj_ssl_socket_t; struct ssl_args { mp_arg_val_t key; mp_arg_val_t cert; mp_arg_val_t server_side; mp_arg_val_t server_hostname; mp_arg_val_t do_handshake; }; STATIC const mp_obj_type_t ussl_socket_type; // Table of error strings corresponding to SSL_xxx error codes. STATIC const char *const ssl_error_tab1[] = { "NOT_OK", "DEAD", "CLOSE_NOTIFY", "EAGAIN", }; STATIC const char *const ssl_error_tab2[] = { "CONN_LOST", "RECORD_OVERFLOW", "SOCK_SETUP_FAILURE", NULL, "INVALID_HANDSHAKE", "INVALID_PROT_MSG", "INVALID_HMAC", "INVALID_VERSION", "UNSUPPORTED_EXTENSION", "INVALID_SESSION", "NO_CIPHER", "INVALID_CERT_HASH_ALG", "BAD_CERTIFICATE", "INVALID_KEY", NULL, "FINISHED_INVALID", "NO_CERT_DEFINED", "NO_CLIENT_RENOG", "NOT_SUPPORTED", }; STATIC NORETURN void ussl_raise_error(int err) { MP_STATIC_ASSERT(SSL_NOT_OK - 3 == SSL_EAGAIN); MP_STATIC_ASSERT(SSL_ERROR_CONN_LOST - 18 == SSL_ERROR_NOT_SUPPORTED); // Check if err corresponds to something in one of the error string tables. const char *errstr = NULL; if (SSL_NOT_OK >= err && err >= SSL_EAGAIN) { errstr = ssl_error_tab1[SSL_NOT_OK - err]; } else if (SSL_ERROR_CONN_LOST >= err && err >= SSL_ERROR_NOT_SUPPORTED) { errstr = ssl_error_tab2[SSL_ERROR_CONN_LOST - err]; } // Unknown error, just raise the error code. if (errstr == NULL) { mp_raise_OSError(err); } // Construct string object. mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); if (o_str == NULL) { mp_raise_OSError(err); } o_str->base.type = &mp_type_str; o_str->data = (const byte *)errstr; o_str->len = strlen((char *)o_str->data); o_str->hash = qstr_compute_hash(o_str->data, o_str->len); // Raise OSError(err, str). mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)}; nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); } STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args) { #if MICROPY_PY_USSL_FINALISER mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); #else mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t); #endif o->base.type = &ussl_socket_type; o->buf = NULL; o->bytes_left = 0; o->sock = sock; o->blocking = true; uint32_t options = SSL_SERVER_VERIFY_LATER; if (!args->do_handshake.u_bool) { options |= SSL_CONNECT_IN_PARTS; } if (args->key.u_obj != mp_const_none) { options |= SSL_NO_DEFAULT_KEY; } if ((o->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) { mp_raise_OSError(MP_EINVAL); } if (args->key.u_obj != mp_const_none) { size_t len; const byte *data = (const byte *)mp_obj_str_get_data(args->key.u_obj, &len); int res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_RSA_KEY, data, len, NULL); if (res != SSL_OK) { mp_raise_ValueError(MP_ERROR_TEXT("invalid key")); } data = (const byte *)mp_obj_str_get_data(args->cert.u_obj, &len); res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_X509_CERT, data, len, NULL); if (res != SSL_OK) { mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); } } if (args->server_side.u_bool) { o->ssl_sock = ssl_server_new(o->ssl_ctx, (long)sock); } else { SSL_EXTENSIONS *ext = ssl_ext_new(); if (args->server_hostname.u_obj != mp_const_none) { ext->host_name = (char *)mp_obj_str_get_str(args->server_hostname.u_obj); } o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0, ext); if (args->do_handshake.u_bool) { int res = ssl_handshake_status(o->ssl_sock); if (res != SSL_OK) { ussl_raise_error(res); } } } return o; } STATIC void ussl_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "<_SSLSocket %p>", self->ssl_sock); } STATIC mp_uint_t ussl_socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); if (o->ssl_sock == NULL) { *errcode = EBADF; return MP_STREAM_ERROR; } while (o->bytes_left == 0) { mp_int_t r = ssl_read(o->ssl_sock, &o->buf); if (r == SSL_OK) { // SSL_OK from ssl_read() means "everything is ok, but there's // no user data yet". It may happen e.g. if handshake is not // finished yet. The best way we can treat it is by returning // EAGAIN. This may be a bit unexpected in blocking mode, but // default is to perform complete handshake in constructor, so // this should not happen in blocking mode. On the other hand, // in nonblocking mode EAGAIN (comparing to the alternative of // looping) is really preferrable. if (o->blocking) { continue; } else { goto eagain; } } if (r < 0) { if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) { // EOF return 0; } if (r == SSL_EAGAIN) { eagain: r = MP_EAGAIN; } *errcode = r; return MP_STREAM_ERROR; } o->bytes_left = r; } if (size > o->bytes_left) { size = o->bytes_left; } memcpy(buf, o->buf, size); o->buf += size; o->bytes_left -= size; return size; } STATIC mp_uint_t ussl_socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); if (o->ssl_sock == NULL) { *errcode = EBADF; return MP_STREAM_ERROR; } mp_int_t r = ssl_write(o->ssl_sock, buf, size); if (r < 0) { *errcode = r; return MP_STREAM_ERROR; } return r; } STATIC mp_uint_t ussl_socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in); if (request == MP_STREAM_CLOSE && self->ssl_sock != NULL) { ssl_free(self->ssl_sock); ssl_ctx_free(self->ssl_ctx); self->ssl_sock = NULL; } // Pass all requests down to the underlying socket return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode); } STATIC mp_obj_t ussl_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in); mp_obj_t sock = o->sock; mp_obj_t dest[3]; mp_load_method(sock, MP_QSTR_setblocking, dest); dest[2] = flag_in; mp_obj_t res = mp_call_method_n_kw(1, 0, dest); o->blocking = mp_obj_is_true(flag_in); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(ussl_socket_setblocking_obj, ussl_socket_setblocking); STATIC const mp_rom_map_elem_t ussl_socket_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_setblocking), MP_ROM_PTR(&ussl_socket_setblocking_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, #if MICROPY_PY_USSL_FINALISER { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table); STATIC const mp_stream_p_t ussl_socket_stream_p = { .read = ussl_socket_read, .write = ussl_socket_write, .ioctl = ussl_socket_ioctl, }; STATIC const mp_obj_type_t ussl_socket_type = { { &mp_type_type }, // Save on qstr's, reuse same as for module .name = MP_QSTR_ussl, .print = ussl_socket_print, .getiter = NULL, .iternext = NULL, .protocol = &ussl_socket_stream_p, .locals_dict = (void *)&ussl_socket_locals_dict, }; STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // TODO: Implement more 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_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_do_handshake, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, }; // TODO: Check that sock implements stream protocol mp_obj_t sock = pos_args[0]; struct ssl_args 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); return MP_OBJ_FROM_PTR(ussl_socket_new(sock, &args)); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket); STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ussl) }, { MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table); const mp_obj_module_t mp_module_ussl = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&mp_module_ssl_globals, }; #endif // MICROPY_PY_USSL ================================================ FILE: extmod/modussl_mbedtls.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2016 Linaro Ltd. * Copyright (c) 2019 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/mpconfig.h" #if MICROPY_PY_USSL && MICROPY_SSL_MBEDTLS #include #include #include // needed because mp_is_nonblocking_error uses system error codes #include "py/runtime.h" #include "py/stream.h" #include "py/objstr.h" // mbedtls_time_t #include "mbedtls/platform.h" #include "mbedtls/ssl.h" #include "mbedtls/x509_crt.h" #include "mbedtls/pk.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/debug.h" #include "mbedtls/error.h" typedef struct _mp_obj_ssl_socket_t { mp_obj_base_t base; mp_obj_t sock; mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ssl_context ssl; mbedtls_ssl_config conf; mbedtls_x509_crt cacert; mbedtls_x509_crt cert; mbedtls_pk_context pkey; } mp_obj_ssl_socket_t; struct ssl_args { mp_arg_val_t key; mp_arg_val_t cert; mp_arg_val_t server_side; mp_arg_val_t server_hostname; mp_arg_val_t do_handshake; }; STATIC const mp_obj_type_t ussl_socket_type; #ifdef MBEDTLS_DEBUG_C STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) { (void)ctx; (void)level; printf("DBG:%s:%04d: %s\n", file, line, str); } #endif STATIC NORETURN void mbedtls_raise_error(int err) { // _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the // underlying socket into negative codes to pass them through mbedtls. Here we turn them // positive again so they get interpreted as the OSError they really are. The // cut-off of -256 is a bit hacky, sigh. if (err < 0 && err > -256) { mp_raise_OSError(-err); } #if defined(MBEDTLS_ERROR_C) // Including mbedtls_strerror takes about 1.5KB due to the error strings. // MBEDTLS_ERROR_C is the define used by mbedtls to conditionally include mbedtls_strerror. // It is set/unset in the MBEDTLS_CONFIG_FILE which is defined in the Makefile. // Try to allocate memory for the message #define ERR_STR_MAX 80 // mbedtls_strerror truncates if it doesn't fit mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); byte *o_str_buf = m_new_maybe(byte, ERR_STR_MAX); if (o_str == NULL || o_str_buf == NULL) { mp_raise_OSError(err); } // print the error message into the allocated buffer mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_MAX); size_t len = strlen((char *)o_str_buf); // Put the exception object together o_str->base.type = &mp_type_str; o_str->data = o_str_buf; o_str->len = len; o_str->hash = qstr_compute_hash(o_str->data, o_str->len); // raise mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)}; nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); #else // mbedtls is compiled without error strings so we simply return the err number mp_raise_OSError(err); // err is typically a large negative number #endif } STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { mp_obj_t sock = *(mp_obj_t *)ctx; const mp_stream_p_t *sock_stream = mp_get_stream(sock); int err; mp_uint_t out_sz = sock_stream->write(sock, buf, len, &err); if (out_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(err)) { return MBEDTLS_ERR_SSL_WANT_WRITE; } return -err; // convert an MP_ERRNO to something mbedtls passes through as error } else { return out_sz; } } STATIC int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { mp_obj_t sock = *(mp_obj_t *)ctx; const mp_stream_p_t *sock_stream = mp_get_stream(sock); int err; mp_uint_t out_sz = sock_stream->read(sock, buf, len, &err); if (out_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(err)) { return MBEDTLS_ERR_SSL_WANT_READ; } return -err; } else { return out_sz; } } STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { // Verify the socket object has the full stream protocol mp_get_stream_raise(sock, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); #if MICROPY_PY_USSL_FINALISER mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); #else mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t); #endif o->base.type = &ussl_socket_type; o->sock = sock; int ret; mbedtls_ssl_init(&o->ssl); mbedtls_ssl_config_init(&o->conf); mbedtls_x509_crt_init(&o->cacert); mbedtls_x509_crt_init(&o->cert); mbedtls_pk_init(&o->pkey); mbedtls_ctr_drbg_init(&o->ctr_drbg); #ifdef MBEDTLS_DEBUG_C // Debug level (0-4) mbedtls_debug_set_threshold(0); #endif mbedtls_entropy_init(&o->entropy); const byte seed[] = "upy"; ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, mbedtls_entropy_func, &o->entropy, seed, sizeof(seed)); if (ret != 0) { goto cleanup; } ret = mbedtls_ssl_config_defaults(&o->conf, args->server_side.u_bool ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret != 0) { goto cleanup; } mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_rng(&o->conf, mbedtls_ctr_drbg_random, &o->ctr_drbg); #ifdef MBEDTLS_DEBUG_C mbedtls_ssl_conf_dbg(&o->conf, mbedtls_debug, NULL); #endif ret = mbedtls_ssl_setup(&o->ssl, &o->conf); if (ret != 0) { goto cleanup; } if (args->server_hostname.u_obj != mp_const_none) { const char *sni = mp_obj_str_get_str(args->server_hostname.u_obj); ret = mbedtls_ssl_set_hostname(&o->ssl, sni); if (ret != 0) { goto cleanup; } } mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL); if (args->key.u_obj != mp_const_none) { size_t key_len; const byte *key = (const byte *)mp_obj_str_get_data(args->key.u_obj, &key_len); // len should include terminating null ret = mbedtls_pk_parse_key(&o->pkey, key, key_len + 1, NULL, 0); if (ret != 0) { ret = MBEDTLS_ERR_PK_BAD_INPUT_DATA; // use general error for all key errors goto cleanup; } size_t cert_len; const byte *cert = (const byte *)mp_obj_str_get_data(args->cert.u_obj, &cert_len); // len should include terminating null ret = mbedtls_x509_crt_parse(&o->cert, cert, cert_len + 1); if (ret != 0) { ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; // use general error for all cert errors goto cleanup; } ret = mbedtls_ssl_conf_own_cert(&o->conf, &o->cert, &o->pkey); if (ret != 0) { goto cleanup; } } if (args->do_handshake.u_bool) { while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { goto cleanup; } } } return o; cleanup: mbedtls_pk_free(&o->pkey); mbedtls_x509_crt_free(&o->cert); mbedtls_x509_crt_free(&o->cacert); mbedtls_ssl_free(&o->ssl); mbedtls_ssl_config_free(&o->conf); mbedtls_ctr_drbg_free(&o->ctr_drbg); mbedtls_entropy_free(&o->entropy); if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { mp_raise_OSError(MP_ENOMEM); } else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) { mp_raise_ValueError(MP_ERROR_TEXT("invalid key")); } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); } else { mbedtls_raise_error(ret); } } STATIC mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); if (!mp_obj_is_true(binary_form)) { mp_raise_NotImplementedError(NULL); } const mbedtls_x509_crt *peer_cert = mbedtls_ssl_get_peer_cert(&o->ssl); if (peer_cert == NULL) { return mp_const_none; } return mp_obj_new_bytes(peer_cert->raw.p, peer_cert->raw.len); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ssl_getpeercert_obj, mod_ssl_getpeercert); STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "<_SSLSocket %p>", self); } STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); int ret = mbedtls_ssl_read(&o->ssl, buf, size); if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { // end of stream return 0; } if (ret >= 0) { return ret; } if (ret == MBEDTLS_ERR_SSL_WANT_READ) { ret = MP_EWOULDBLOCK; } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { // If handshake is not finished, read attempt may end up in protocol // wanting to write next handshake message. The same may happen with // renegotation. ret = MP_EWOULDBLOCK; } *errcode = ret; return MP_STREAM_ERROR; } STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); int ret = mbedtls_ssl_write(&o->ssl, buf, size); if (ret >= 0) { return ret; } if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = MP_EWOULDBLOCK; } else if (ret == MBEDTLS_ERR_SSL_WANT_READ) { // If handshake is not finished, write attempt may end up in protocol // wanting to read next handshake message. The same may happen with // renegotation. ret = MP_EWOULDBLOCK; } *errcode = ret; return MP_STREAM_ERROR; } STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in); mp_obj_t sock = o->sock; mp_obj_t dest[3]; mp_load_method(sock, MP_QSTR_setblocking, dest); dest[2] = flag_in; return mp_call_method_n_kw(1, 0, dest); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in); if (request == MP_STREAM_CLOSE) { mbedtls_pk_free(&self->pkey); mbedtls_x509_crt_free(&self->cert); mbedtls_x509_crt_free(&self->cacert); mbedtls_ssl_free(&self->ssl); mbedtls_ssl_config_free(&self->conf); mbedtls_ctr_drbg_free(&self->ctr_drbg); mbedtls_entropy_free(&self->entropy); } // Pass all requests down to the underlying socket return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode); } STATIC const mp_rom_map_elem_t ussl_socket_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_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, #if MICROPY_PY_USSL_FINALISER { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) }, }; STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table); STATIC const mp_stream_p_t ussl_socket_stream_p = { .read = socket_read, .write = socket_write, .ioctl = socket_ioctl, }; STATIC const mp_obj_type_t ussl_socket_type = { { &mp_type_type }, // Save on qstr's, reuse same as for module .name = MP_QSTR_ussl, .print = socket_print, .getiter = NULL, .iternext = NULL, .protocol = &ussl_socket_stream_p, .locals_dict = (void *)&ussl_socket_locals_dict, }; STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // TODO: Implement more 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_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_do_handshake, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, }; // TODO: Check that sock implements stream protocol mp_obj_t sock = pos_args[0]; struct ssl_args 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); return MP_OBJ_FROM_PTR(socket_new(sock, &args)); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket); STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ussl) }, { MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table); const mp_obj_module_t mp_module_ussl = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&mp_module_ssl_globals, }; #endif // MICROPY_PY_USSL ================================================ FILE: extmod/modutimeq.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2014 Damien P. George * Copyright (c) 2016-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 "py/objlist.h" #include "py/runtime.h" #include "py/smallint.h" #if MICROPY_PY_UTIMEQ #define MODULO MICROPY_PY_UTIME_TICKS_PERIOD #define DEBUG 0 // the algorithm here is modelled on CPython's heapq.py struct qentry { mp_uint_t time; mp_uint_t id; mp_obj_t callback; mp_obj_t args; }; typedef struct _mp_obj_utimeq_t { mp_obj_base_t base; mp_uint_t alloc; mp_uint_t len; struct qentry items[]; } mp_obj_utimeq_t; STATIC mp_uint_t utimeq_id; STATIC mp_obj_utimeq_t *utimeq_get_heap(mp_obj_t heap_in) { return MP_OBJ_TO_PTR(heap_in); } STATIC bool time_less_than(struct qentry *item, struct qentry *parent) { mp_uint_t item_tm = item->time; mp_uint_t parent_tm = parent->time; mp_uint_t res = parent_tm - item_tm; if (res == 0) { // TODO: This actually should use the same "ring" logic // as for time, to avoid artifacts when id's overflow. return item->id < parent->id; } if ((mp_int_t)res < 0) { res += MODULO; } return res && res < (MODULO / 2); } STATIC mp_obj_t utimeq_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); mp_uint_t alloc = mp_obj_get_int(args[0]); mp_obj_utimeq_t *o = m_new_obj_var(mp_obj_utimeq_t, struct qentry, alloc); o->base.type = type; memset(o->items, 0, sizeof(*o->items) * alloc); o->alloc = alloc; o->len = 0; return MP_OBJ_FROM_PTR(o); } STATIC void utimeq_heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) { struct qentry item = heap->items[pos]; while (pos > start_pos) { mp_uint_t parent_pos = (pos - 1) >> 1; struct qentry *parent = &heap->items[parent_pos]; bool lessthan = time_less_than(&item, parent); if (lessthan) { heap->items[pos] = *parent; pos = parent_pos; } else { break; } } heap->items[pos] = item; } STATIC void utimeq_heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { mp_uint_t start_pos = pos; mp_uint_t end_pos = heap->len; struct qentry item = heap->items[pos]; for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) { // choose right child if it's <= left child if (child_pos + 1 < end_pos) { bool lessthan = time_less_than(&heap->items[child_pos], &heap->items[child_pos + 1]); if (!lessthan) { child_pos += 1; } } // bubble up the smaller child heap->items[pos] = heap->items[child_pos]; pos = child_pos; } heap->items[pos] = item; utimeq_heap_siftdown(heap, start_pos, pos); } STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_t heap_in = args[0]; mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); if (heap->len == heap->alloc) { mp_raise_msg(&mp_type_IndexError, "queue overflow"); } mp_uint_t l = heap->len; heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]); heap->items[l].id = utimeq_id++; heap->items[l].callback = args[2]; heap->items[l].args = args[3]; utimeq_heap_siftdown(heap, 0, heap->len); heap->len++; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush); STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); if (heap->len == 0) { mp_raise_msg(&mp_type_IndexError, "empty heap"); } mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref); if (!mp_obj_is_type(list_ref, &mp_type_list) || ret->len < 3) { mp_raise_TypeError(NULL); } struct qentry *item = &heap->items[0]; ret->items[0] = MP_OBJ_NEW_SMALL_INT(item->time); ret->items[1] = item->callback; ret->items[2] = item->args; heap->len -= 1; heap->items[0] = heap->items[heap->len]; heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer heap->items[heap->len].args = MP_OBJ_NULL; if (heap->len) { utimeq_heap_siftup(heap, 0); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop); STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) { mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); if (heap->len == 0) { mp_raise_msg(&mp_type_IndexError, "empty heap"); } struct qentry *item = &heap->items[0]; return MP_OBJ_NEW_SMALL_INT(item->time); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime); #if DEBUG STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) { mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); for (int i = 0; i < heap->len; i++) { printf(UINT_FMT "\t%p\t%p\n", heap->items[i].time, MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args)); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_dump_obj, mod_utimeq_dump); #endif STATIC mp_obj_t utimeq_unary_op(mp_unary_op_t op, mp_obj_t self_in) { mp_obj_utimeq_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); default: return MP_OBJ_NULL; // op not supported } } STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) }, { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) }, { MP_ROM_QSTR(MP_QSTR_peektime), MP_ROM_PTR(&mod_utimeq_peektime_obj) }, #if DEBUG { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(utimeq_locals_dict, utimeq_locals_dict_table); STATIC const mp_obj_type_t utimeq_type = { { &mp_type_type }, .name = MP_QSTR_utimeq, .make_new = utimeq_make_new, .unary_op = utimeq_unary_op, .locals_dict = (void*)&utimeq_locals_dict, }; STATIC const mp_rom_map_elem_t mp_module_utimeq_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utimeq) }, { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&utimeq_type) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_utimeq_globals, mp_module_utimeq_globals_table); const mp_obj_module_t mp_module_utimeq = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_utimeq_globals, }; #endif //MICROPY_PY_UTIMEQ ================================================ FILE: extmod/moduwebsocket.c ================================================ /* * 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 #include "py/runtime.h" #include "py/stream.h" #include "extmod/moduwebsocket.h" #if MICROPY_PY_UWEBSOCKET enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL }; enum { BLOCKING_WRITE = 0x80 }; typedef struct _mp_obj_websocket_t { mp_obj_base_t base; mp_obj_t sock; uint32_t msg_sz; byte mask[4]; byte state; byte to_recv; byte mask_pos; byte buf_pos; byte buf[6]; byte opts; // Copy of last data frame flags byte ws_flags; // Copy of current frame flags byte last_flags; } mp_obj_websocket_t; STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode); STATIC mp_obj_t websocket_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, 2, false); mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t); o->base.type = type; o->sock = args[0]; o->state = FRAME_HEADER; o->to_recv = 2; o->mask_pos = 0; o->buf_pos = 0; o->opts = FRAME_TXT; if (n_args > 1 && args[1] == mp_const_true) { o->opts |= BLOCKING_WRITE; } return MP_OBJ_FROM_PTR(o); } STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); const mp_stream_p_t *stream_p = mp_get_stream(self->sock); while (1) { if (self->to_recv != 0) { mp_uint_t out_sz = stream_p->read(self->sock, self->buf + self->buf_pos, self->to_recv, errcode); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } self->buf_pos += out_sz; self->to_recv -= out_sz; if (self->to_recv != 0) { *errcode = MP_EAGAIN; return MP_STREAM_ERROR; } } switch (self->state) { case FRAME_HEADER: { // TODO: Split frame handling below is untested so far, so conservatively disable it assert(self->buf[0] & 0x80); // "Control frames MAY be injected in the middle of a fragmented message." // So, they must be processed before data frames (and not alter // self->ws_flags) byte frame_type = self->buf[0]; self->last_flags = frame_type; frame_type &= FRAME_OPCODE_MASK; if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) { // Preserve previous frame type self->ws_flags = (self->ws_flags & FRAME_OPCODE_MASK) | (self->buf[0] & ~FRAME_OPCODE_MASK); } else { self->ws_flags = self->buf[0]; } // Reset mask in case someone will use "simplified" protocol // without masks. memset(self->mask, 0, sizeof(self->mask)); int to_recv = 0; size_t sz = self->buf[1] & 0x7f; if (sz == 126) { // Msg size is next 2 bytes to_recv += 2; } else if (sz == 127) { // Msg size is next 8 bytes assert(0); } if (self->buf[1] & 0x80) { // Next 4 bytes is mask to_recv += 4; } self->buf_pos = 0; self->to_recv = to_recv; self->msg_sz = sz; // May be overridden by FRAME_OPT if (to_recv != 0) { self->state = FRAME_OPT; } else { if (frame_type >= FRAME_CLOSE) { self->state = CONTROL; } else { self->state = PAYLOAD; } } continue; } case FRAME_OPT: { if ((self->buf_pos & 3) == 2) { // First two bytes are message length self->msg_sz = (self->buf[0] << 8) | self->buf[1]; } if (self->buf_pos >= 4) { // Last 4 bytes is mask memcpy(self->mask, self->buf + self->buf_pos - 4, 4); } self->buf_pos = 0; if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) { self->state = CONTROL; } else { self->state = PAYLOAD; } continue; } case PAYLOAD: case CONTROL: { mp_uint_t out_sz = 0; if (self->msg_sz == 0) { // In case message had zero payload goto no_payload; } size_t sz = MIN(size, self->msg_sz); out_sz = stream_p->read(self->sock, buf, sz, errcode); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } sz = out_sz; for (byte *p = buf; sz--; p++) { *p ^= self->mask[self->mask_pos++ & 3]; } self->msg_sz -= out_sz; if (self->msg_sz == 0) { byte last_state; no_payload: last_state = self->state; self->state = FRAME_HEADER; self->to_recv = 2; self->mask_pos = 0; self->buf_pos = 0; // Handle control frame if (last_state == CONTROL) { byte frame_type = self->last_flags & FRAME_OPCODE_MASK; if (frame_type == FRAME_CLOSE) { static const char close_resp[2] = {0x88, 0}; int err; websocket_write(self_in, close_resp, sizeof(close_resp), &err); return 0; } //DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags); continue; } } if (out_sz != 0) { return out_sz; } // Empty (data) frame received is not EOF continue; } } } } STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); assert(size < 0x10000); byte header[4] = {0x80 | (self->opts & FRAME_OPCODE_MASK)}; int hdr_sz; if (size < 126) { header[1] = size; hdr_sz = 2; } else { header[1] = 126; header[2] = size >> 8; header[3] = size & 0xff; hdr_sz = 4; } mp_obj_t dest[3]; if (self->opts & BLOCKING_WRITE) { mp_load_method(self->sock, MP_QSTR_setblocking, dest); dest[2] = mp_const_true; mp_call_method_n_kw(1, 0, dest); } mp_uint_t out_sz = mp_stream_write_exactly(self->sock, header, hdr_sz, errcode); if (*errcode == 0) { out_sz = mp_stream_write_exactly(self->sock, buf, size, errcode); } if (self->opts & BLOCKING_WRITE) { dest[2] = mp_const_false; mp_call_method_n_kw(1, 0, dest); } if (*errcode != 0) { return MP_STREAM_ERROR; } return out_sz; } STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); switch (request) { case MP_STREAM_CLOSE: // TODO: Send close signaling to the other side, otherwise it's // abrupt close (connection abort). mp_stream_close(self->sock); return 0; case MP_STREAM_GET_DATA_OPTS: return self->ws_flags & FRAME_OPCODE_MASK; case MP_STREAM_SET_DATA_OPTS: { int cur = self->opts & FRAME_OPCODE_MASK; self->opts = (self->opts & ~FRAME_OPCODE_MASK) | (arg & FRAME_OPCODE_MASK); return cur; } default: *errcode = MP_EINVAL; return MP_STREAM_ERROR; } } STATIC const mp_rom_map_elem_t websocket_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_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, }; STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table); STATIC const mp_stream_p_t websocket_stream_p = { .read = websocket_read, .write = websocket_write, .ioctl = websocket_ioctl, }; STATIC const mp_obj_type_t websocket_type = { { &mp_type_type }, .name = MP_QSTR_websocket, .make_new = websocket_make_new, .protocol = &websocket_stream_p, .locals_dict = (void*)&websocket_locals_dict, }; STATIC const mp_rom_map_elem_t uwebsocket_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uwebsocket) }, { MP_ROM_QSTR(MP_QSTR_websocket), MP_ROM_PTR(&websocket_type) }, }; STATIC MP_DEFINE_CONST_DICT(uwebsocket_module_globals, uwebsocket_module_globals_table); const mp_obj_module_t mp_module_uwebsocket = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&uwebsocket_module_globals, }; #endif // MICROPY_PY_UWEBSOCKET ================================================ FILE: extmod/moduwebsocket.h ================================================ #ifndef MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H #define MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H #define FRAME_OPCODE_MASK 0x0f enum { FRAME_CONT, FRAME_TXT, FRAME_BIN, FRAME_CLOSE = 0x8, FRAME_PING, FRAME_PONG }; #endif // MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H ================================================ FILE: extmod/moduzlib.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * 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/runtime.h" #include "py/stream.h" #include "py/mperrno.h" #if MICROPY_PY_UZLIB #include "uzlib/tinf.h" #if 0 // print debugging info #define DEBUG_printf DEBUG_printf #else // don't print debugging info #define DEBUG_printf(...) (void)0 #endif typedef struct _mp_obj_decompio_t { mp_obj_base_t base; mp_obj_t src_stream; TINF_DATA decomp; bool eof; } mp_obj_decompio_t; STATIC int read_src_stream(TINF_DATA *data) { byte *p = (void*)data; p -= offsetof(mp_obj_decompio_t, decomp); mp_obj_decompio_t *self = (mp_obj_decompio_t*)p; const mp_stream_p_t *stream = mp_get_stream(self->src_stream); int err; byte c; mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err); if (out_sz == MP_STREAM_ERROR) { mp_raise_OSError(err); } if (out_sz == 0) { nlr_raise(mp_obj_new_exception(&mp_type_EOFError)); } return c; } STATIC mp_obj_t decompio_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, 2, false); mp_get_stream_raise(args[0], MP_STREAM_OP_READ); mp_obj_decompio_t *o = m_new_obj(mp_obj_decompio_t); o->base.type = type; memset(&o->decomp, 0, sizeof(o->decomp)); o->decomp.readSource = read_src_stream; o->src_stream = args[0]; o->eof = false; mp_int_t dict_opt = 0; int dict_sz; if (n_args > 1) { dict_opt = mp_obj_get_int(args[1]); } if (dict_opt >= 16) { int st = uzlib_gzip_parse_header(&o->decomp); if (st != TINF_OK) { goto header_error; } dict_sz = 1 << (dict_opt - 16); } else if (dict_opt >= 0) { dict_opt = uzlib_zlib_parse_header(&o->decomp); if (dict_opt < 0) { header_error: mp_raise_ValueError("compression header"); } dict_sz = 1 << dict_opt; } else { dict_sz = 1 << -dict_opt; } uzlib_uncompress_init(&o->decomp, m_new(byte, dict_sz), dict_sz); return MP_OBJ_FROM_PTR(o); } STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_decompio_t *o = MP_OBJ_TO_PTR(o_in); if (o->eof) { return 0; } o->decomp.dest = buf; o->decomp.dest_limit = (byte*)buf + size; int st = uzlib_uncompress_chksum(&o->decomp); if (st == TINF_DONE) { o->eof = true; } if (st < 0) { *errcode = MP_EINVAL; return MP_STREAM_ERROR; } return o->decomp.dest - (byte*)buf; } #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t decompio_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) }, }; STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table); #endif STATIC const mp_stream_p_t decompio_stream_p = { .read = decompio_read, }; #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_obj_type_t decompio_type = { { &mp_type_type }, .name = MP_QSTR_DecompIO, .make_new = decompio_make_new, .protocol = &decompio_stream_p, .locals_dict = (void*)&decompio_locals_dict, }; #endif STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) { mp_obj_t data = args[0]; mp_buffer_info_t bufinfo; mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); TINF_DATA *decomp = m_new_obj(TINF_DATA); memset(decomp, 0, sizeof(*decomp)); DEBUG_printf("sizeof(TINF_DATA)=" UINT_FMT "\n", sizeof(*decomp)); uzlib_uncompress_init(decomp, NULL, 0); mp_uint_t dest_buf_size = (bufinfo.len + 15) & ~15; byte *dest_buf = m_new(byte, dest_buf_size); decomp->dest = dest_buf; decomp->dest_limit = dest_buf + dest_buf_size; DEBUG_printf("uzlib: Initial out buffer: " UINT_FMT " bytes\n", dest_buf_size); decomp->source = bufinfo.buf; decomp->source_limit = (byte*)bufinfo.buf + bufinfo.len; int st; bool is_zlib = true; if (n_args > 1 && MP_OBJ_SMALL_INT_VALUE(args[1]) < 0) { is_zlib = false; } if (is_zlib) { st = uzlib_zlib_parse_header(decomp); if (st < 0) { goto error; } } while (1) { st = uzlib_uncompress_chksum(decomp); if (st < 0) { goto error; } if (st == TINF_DONE) { break; } size_t offset = decomp->dest - dest_buf; dest_buf = m_renew(byte, dest_buf, dest_buf_size, dest_buf_size + 256); dest_buf_size += 256; decomp->dest = dest_buf + offset; decomp->dest_limit = decomp->dest + 256; } mp_uint_t final_sz = decomp->dest - dest_buf; DEBUG_printf("uzlib: Resizing from " UINT_FMT " to final size: " UINT_FMT " bytes\n", dest_buf_size, final_sz); dest_buf = (byte*)m_renew(byte, dest_buf, dest_buf_size, final_sz); mp_obj_t res = mp_obj_new_bytearray_by_ref(final_sz, dest_buf); m_del_obj(TINF_DATA, decomp); return res; error: nlr_raise(mp_obj_new_exception_arg1(&mp_type_ValueError, MP_OBJ_NEW_SMALL_INT(st))); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uzlib_decompress_obj, 1, 3, mod_uzlib_decompress); #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_uzlib_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uzlib) }, { MP_ROM_QSTR(MP_QSTR_decompress), MP_ROM_PTR(&mod_uzlib_decompress_obj) }, { MP_ROM_QSTR(MP_QSTR_DecompIO), MP_ROM_PTR(&decompio_type) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_uzlib_globals, mp_module_uzlib_globals_table); const mp_obj_module_t mp_module_uzlib = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_uzlib_globals, }; #endif // Source files #include'd here to make sure they're compiled in // only if module is enabled by config setting. #include "uzlib/tinflate.c" #include "uzlib/tinfzlib.c" #include "uzlib/tinfgzip.c" #include "uzlib/adler32.c" #include "uzlib/crc32.c" #endif // MICROPY_PY_UZLIB ================================================ FILE: extmod/modwebrepl.c ================================================ /* * 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 #include "py/runtime.h" #include "py/stream.h" #include "py/builtin.h" #ifdef MICROPY_PY_WEBREPL_DELAY #include "py/mphal.h" #endif #include "extmod/moduwebsocket.h" #if MICROPY_PY_WEBREPL #if 0 // print debugging info #define DEBUG_printf DEBUG_printf #else // don't print debugging info #define DEBUG_printf(...) (void)0 #endif struct webrepl_file { char sig[2]; char type; char flags; uint64_t offset; uint32_t size; uint16_t fname_len; char fname[64]; } __attribute__((packed)); enum { PUT_FILE = 1, GET_FILE, GET_VER }; enum { STATE_PASSWD, STATE_NORMAL }; typedef struct _mp_obj_webrepl_t { mp_obj_base_t base; mp_obj_t sock; byte state; byte hdr_to_recv; uint32_t data_to_recv; struct webrepl_file hdr; mp_obj_t cur_file; } mp_obj_webrepl_t; STATIC const char passwd_prompt[] = "Password: "; STATIC const char connected_prompt[] = "\r\nWebREPL connected\r\n>>> "; STATIC const char denied_prompt[] = "\r\nAccess denied\r\n"; STATIC char webrepl_passwd[10]; STATIC void write_webrepl(mp_obj_t websock, const void *buf, size_t len) { const mp_stream_p_t *sock_stream = mp_get_stream(websock); int err; int old_opts = sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, FRAME_BIN, &err); sock_stream->write(websock, buf, len, &err); sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, old_opts, &err); } #define SSTR(s) s, sizeof(s) - 1 STATIC void write_webrepl_str(mp_obj_t websock, const char *str, int sz) { int err; const mp_stream_p_t *sock_stream = mp_get_stream(websock); sock_stream->write(websock, str, sz, &err); } STATIC void write_webrepl_resp(mp_obj_t websock, uint16_t code) { char buf[4] = {'W', 'B', code & 0xff, code >> 8}; write_webrepl(websock, buf, sizeof(buf)); } STATIC mp_obj_t webrepl_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, 2, false); mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); DEBUG_printf("sizeof(struct webrepl_file) = %lu\n", sizeof(struct webrepl_file)); mp_obj_webrepl_t *o = m_new_obj(mp_obj_webrepl_t); o->base.type = type; o->sock = args[0]; o->hdr_to_recv = sizeof(struct webrepl_file); o->data_to_recv = 0; o->state = STATE_PASSWD; write_webrepl_str(args[0], SSTR(passwd_prompt)); return o; } STATIC void check_file_op_finished(mp_obj_webrepl_t *self) { if (self->data_to_recv == 0) { mp_stream_close(self->cur_file); self->hdr_to_recv = sizeof(struct webrepl_file); DEBUG_printf("webrepl: Finished file operation %d\n", self->hdr.type); write_webrepl_resp(self->sock, 0); } } STATIC int write_file_chunk(mp_obj_webrepl_t *self) { const mp_stream_p_t *file_stream = mp_get_stream(self->cur_file); byte readbuf[2 + 256]; int err; mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err); if (out_sz == MP_STREAM_ERROR) { return out_sz; } readbuf[0] = out_sz; readbuf[1] = out_sz >> 8; DEBUG_printf("webrepl: Sending %d bytes of file\n", out_sz); write_webrepl(self->sock, readbuf, 2 + out_sz); return out_sz; } STATIC void handle_op(mp_obj_webrepl_t *self) { // Handle operations not requiring opened file switch (self->hdr.type) { case GET_VER: { static const char ver[] = {MICROPY_VERSION_MAJOR, MICROPY_VERSION_MINOR, MICROPY_VERSION_MICRO}; write_webrepl(self->sock, ver, sizeof(ver)); self->hdr_to_recv = sizeof(struct webrepl_file); return; } } // Handle operations requiring opened file mp_obj_t open_args[2] = { mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname)), MP_OBJ_NEW_QSTR(MP_QSTR_rb) }; if (self->hdr.type == PUT_FILE) { open_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_wb); } self->cur_file = mp_builtin_open(2, open_args, (mp_map_t*)&mp_const_empty_map); #if 0 struct mp_stream_seek_t seek = { .offset = self->hdr.offset, .whence = 0 }; int err; mp_uint_t res = file_stream->ioctl(self->cur_file, MP_STREAM_SEEK, (uintptr_t)&seek, &err); assert(res != MP_STREAM_ERROR); #endif write_webrepl_resp(self->sock, 0); if (self->hdr.type == PUT_FILE) { self->data_to_recv = self->hdr.size; check_file_op_finished(self); } else if (self->hdr.type == GET_FILE) { self->data_to_recv = 1; } } STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode); STATIC mp_uint_t webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { mp_uint_t out_sz; do { out_sz = _webrepl_read(self_in, buf, size, errcode); } while (out_sz == -2); return out_sz; } STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { // We know that os.dupterm always calls with size = 1 assert(size == 1); mp_obj_webrepl_t *self = self_in; const mp_stream_p_t *sock_stream = mp_get_stream(self->sock); mp_uint_t out_sz = sock_stream->read(self->sock, buf, size, errcode); //DEBUG_printf("webrepl: Read %d initial bytes from websocket\n", out_sz); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } if (self->state == STATE_PASSWD) { char c = *(char*)buf; if (c == '\r' || c == '\n') { self->hdr.fname[self->data_to_recv] = 0; DEBUG_printf("webrepl: entered password: %s\n", self->hdr.fname); if (strcmp(self->hdr.fname, webrepl_passwd) != 0) { write_webrepl_str(self->sock, SSTR(denied_prompt)); return 0; } self->state = STATE_NORMAL; self->data_to_recv = 0; write_webrepl_str(self->sock, SSTR(connected_prompt)); } else if (self->data_to_recv < 10) { self->hdr.fname[self->data_to_recv++] = c; } return -2; } // If last read data belonged to text record (== REPL) int err; if (sock_stream->ioctl(self->sock, MP_STREAM_GET_DATA_OPTS, 0, &err) == 1) { return out_sz; } DEBUG_printf("webrepl: received bin data, hdr_to_recv: %d, data_to_recv=%d\n", self->hdr_to_recv, self->data_to_recv); if (self->hdr_to_recv != 0) { char *p = (char*)&self->hdr + sizeof(self->hdr) - self->hdr_to_recv; *p++ = *(char*)buf; if (--self->hdr_to_recv != 0) { mp_uint_t hdr_sz = sock_stream->read(self->sock, p, self->hdr_to_recv, errcode); if (hdr_sz == MP_STREAM_ERROR) { return hdr_sz; } self->hdr_to_recv -= hdr_sz; if (self->hdr_to_recv != 0) { return -2; } } DEBUG_printf("webrepl: op: %d, file: %s, chunk @%x, sz=%d\n", self->hdr.type, self->hdr.fname, (uint32_t)self->hdr.offset, self->hdr.size); handle_op(self); return -2; } if (self->data_to_recv != 0) { // Ports that don't have much available stack can make this filebuf static #if MICROPY_PY_WEBREPL_STATIC_FILEBUF static #endif byte filebuf[512]; filebuf[0] = *(byte*)buf; mp_uint_t buf_sz = 1; if (--self->data_to_recv != 0) { size_t to_read = MIN(sizeof(filebuf) - 1, self->data_to_recv); mp_uint_t sz = sock_stream->read(self->sock, filebuf + 1, to_read, errcode); if (sz == MP_STREAM_ERROR) { return sz; } self->data_to_recv -= sz; buf_sz += sz; } if (self->hdr.type == PUT_FILE) { DEBUG_printf("webrepl: Writing %lu bytes to file\n", buf_sz); int err; mp_uint_t res = mp_stream_write_exactly(self->cur_file, filebuf, buf_sz, &err); if (err != 0 || res != buf_sz) { assert(0); } } else if (self->hdr.type == GET_FILE) { assert(buf_sz == 1); assert(self->data_to_recv == 0); assert(filebuf[0] == 0); mp_uint_t out_sz = write_file_chunk(self); if (out_sz != 0) { self->data_to_recv = 1; } } check_file_op_finished(self); #ifdef MICROPY_PY_WEBREPL_DELAY // Some platforms may have broken drivers and easily gets // overloaded with modest traffic WebREPL file transfers // generate. The basic workaround is a crude rate control // done in such way. mp_hal_delay_ms(MICROPY_PY_WEBREPL_DELAY); #endif } return -2; } STATIC mp_uint_t webrepl_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_webrepl_t *self = self_in; if (self->state == STATE_PASSWD) { // Don't forward output until passwd is entered return size; } const mp_stream_p_t *stream_p = mp_get_stream(self->sock); return stream_p->write(self->sock, buf, size, errcode); } STATIC mp_uint_t webrepl_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_webrepl_t *self = MP_OBJ_TO_PTR(o_in); (void)arg; switch (request) { case MP_STREAM_CLOSE: // TODO: This is a place to do cleanup mp_stream_close(self->sock); return 0; default: *errcode = MP_EINVAL; return MP_STREAM_ERROR; } } STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) { size_t len; const char *passwd = mp_obj_str_get_data(passwd_in, &len); if (len > sizeof(webrepl_passwd) - 1) { mp_raise_ValueError(NULL); } strcpy(webrepl_passwd, passwd); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(webrepl_set_password_obj, webrepl_set_password); STATIC const mp_rom_map_elem_t webrepl_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_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, }; STATIC MP_DEFINE_CONST_DICT(webrepl_locals_dict, webrepl_locals_dict_table); STATIC const mp_stream_p_t webrepl_stream_p = { .read = webrepl_read, .write = webrepl_write, .ioctl = webrepl_ioctl, }; STATIC const mp_obj_type_t webrepl_type = { { &mp_type_type }, .name = MP_QSTR__webrepl, .make_new = webrepl_make_new, .protocol = &webrepl_stream_p, .locals_dict = (mp_obj_dict_t*)&webrepl_locals_dict, }; STATIC const mp_rom_map_elem_t webrepl_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__webrepl) }, { MP_ROM_QSTR(MP_QSTR__webrepl), MP_ROM_PTR(&webrepl_type) }, { MP_ROM_QSTR(MP_QSTR_password), MP_ROM_PTR(&webrepl_set_password_obj) }, }; STATIC MP_DEFINE_CONST_DICT(webrepl_module_globals, webrepl_module_globals_table); const mp_obj_module_t mp_module_webrepl = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&webrepl_module_globals, }; #endif // MICROPY_PY_WEBREPL ================================================ FILE: extmod/modwebsocket.c ================================================ /* * 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 #include "py/runtime.h" #include "py/stream.h" #include "extmod/modwebsocket.h" #if MICROPY_PY_WEBSOCKET enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL }; enum { BLOCKING_WRITE = 0x80 }; typedef struct _mp_obj_websocket_t { mp_obj_base_t base; mp_obj_t sock; uint32_t msg_sz; byte mask[4]; byte state; byte to_recv; byte mask_pos; byte buf_pos; byte buf[6]; byte opts; // Copy of last data frame flags byte ws_flags; // Copy of current frame flags byte last_flags; } mp_obj_websocket_t; STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode); STATIC mp_obj_t websocket_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, 2, false); mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t); o->base.type = type; o->sock = args[0]; o->state = FRAME_HEADER; o->to_recv = 2; o->mask_pos = 0; o->buf_pos = 0; o->opts = FRAME_TXT; if (n_args > 1 && args[1] == mp_const_true) { o->opts |= BLOCKING_WRITE; } return MP_OBJ_FROM_PTR(o); } STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); const mp_stream_p_t *stream_p = mp_get_stream(self->sock); while (1) { if (self->to_recv != 0) { mp_uint_t out_sz = stream_p->read(self->sock, self->buf + self->buf_pos, self->to_recv, errcode); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } self->buf_pos += out_sz; self->to_recv -= out_sz; if (self->to_recv != 0) { *errcode = MP_EAGAIN; return MP_STREAM_ERROR; } } switch (self->state) { case FRAME_HEADER: { // TODO: Split frame handling below is untested so far, so conservatively disable it assert(self->buf[0] & 0x80); // "Control frames MAY be injected in the middle of a fragmented message." // So, they must be processed before data frames (and not alter // self->ws_flags) byte frame_type = self->buf[0]; self->last_flags = frame_type; frame_type &= FRAME_OPCODE_MASK; if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) { // Preserve previous frame type self->ws_flags = (self->ws_flags & FRAME_OPCODE_MASK) | (self->buf[0] & ~FRAME_OPCODE_MASK); } else { self->ws_flags = self->buf[0]; } // Reset mask in case someone will use "simplified" protocol // without masks. memset(self->mask, 0, sizeof(self->mask)); int to_recv = 0; size_t sz = self->buf[1] & 0x7f; if (sz == 126) { // Msg size is next 2 bytes to_recv += 2; } else if (sz == 127) { // Msg size is next 8 bytes assert(0); } if (self->buf[1] & 0x80) { // Next 4 bytes is mask to_recv += 4; } self->buf_pos = 0; self->to_recv = to_recv; self->msg_sz = sz; // May be overridden by FRAME_OPT if (to_recv != 0) { self->state = FRAME_OPT; } else { if (frame_type >= FRAME_CLOSE) { self->state = CONTROL; } else { self->state = PAYLOAD; } } continue; } case FRAME_OPT: { if ((self->buf_pos & 3) == 2) { // First two bytes are message length self->msg_sz = (self->buf[0] << 8) | self->buf[1]; } if (self->buf_pos >= 4) { // Last 4 bytes is mask memcpy(self->mask, self->buf + self->buf_pos - 4, 4); } self->buf_pos = 0; if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) { self->state = CONTROL; } else { self->state = PAYLOAD; } continue; } case PAYLOAD: case CONTROL: { mp_uint_t out_sz = 0; if (self->msg_sz == 0) { // In case message had zero payload goto no_payload; } size_t sz = MIN(size, self->msg_sz); out_sz = stream_p->read(self->sock, buf, sz, errcode); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } sz = out_sz; for (byte *p = buf; sz--; p++) { *p ^= self->mask[self->mask_pos++ & 3]; } self->msg_sz -= out_sz; if (self->msg_sz == 0) { byte last_state; no_payload: last_state = self->state; self->state = FRAME_HEADER; self->to_recv = 2; self->mask_pos = 0; self->buf_pos = 0; // Handle control frame if (last_state == CONTROL) { byte frame_type = self->last_flags & FRAME_OPCODE_MASK; if (frame_type == FRAME_CLOSE) { static char close_resp[2] = {0x88, 0}; int err; websocket_write(self_in, close_resp, sizeof(close_resp), &err); return 0; } //DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags); continue; } } if (out_sz != 0) { return out_sz; } // Empty (data) frame received is not EOF continue; } } } } STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); assert(size < 0x10000); byte header[4] = {0x80 | (self->opts & FRAME_OPCODE_MASK)}; int hdr_sz; if (size < 126) { header[1] = size; hdr_sz = 2; } else { header[1] = 126; header[2] = size >> 8; header[3] = size & 0xff; hdr_sz = 4; } mp_obj_t dest[3]; if (self->opts & BLOCKING_WRITE) { mp_load_method(self->sock, MP_QSTR_setblocking, dest); dest[2] = mp_const_true; mp_call_method_n_kw(1, 0, dest); } mp_uint_t out_sz = mp_stream_write_exactly(self->sock, header, hdr_sz, errcode); if (*errcode == 0) { out_sz = mp_stream_write_exactly(self->sock, buf, size, errcode); } if (self->opts & BLOCKING_WRITE) { dest[2] = mp_const_false; mp_call_method_n_kw(1, 0, dest); } if (*errcode != 0) { return MP_STREAM_ERROR; } return out_sz; } STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); switch (request) { case MP_STREAM_CLOSE: // TODO: Send close signaling to the other side, otherwise it's // abrupt close (connection abort). mp_stream_close(self->sock); return 0; case MP_STREAM_GET_DATA_OPTS: return self->ws_flags & FRAME_OPCODE_MASK; case MP_STREAM_SET_DATA_OPTS: { int cur = self->opts & FRAME_OPCODE_MASK; self->opts = (self->opts & ~FRAME_OPCODE_MASK) | (arg & FRAME_OPCODE_MASK); return cur; } default: *errcode = MP_EINVAL; return MP_STREAM_ERROR; } } STATIC const mp_rom_map_elem_t websocket_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_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, }; STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table); STATIC const mp_stream_p_t websocket_stream_p = { .read = websocket_read, .write = websocket_write, .ioctl = websocket_ioctl, }; STATIC const mp_obj_type_t websocket_type = { { &mp_type_type }, .name = MP_QSTR_websocket, .make_new = websocket_make_new, .protocol = &websocket_stream_p, .locals_dict = (void*)&websocket_locals_dict, }; STATIC const mp_rom_map_elem_t websocket_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_websocket) }, { MP_ROM_QSTR(MP_QSTR_websocket), MP_ROM_PTR(&websocket_type) }, }; STATIC MP_DEFINE_CONST_DICT(websocket_module_globals, websocket_module_globals_table); const mp_obj_module_t mp_module_websocket = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&websocket_module_globals, }; #endif // MICROPY_PY_WEBSOCKET ================================================ FILE: extmod/modwebsocket.h ================================================ #ifndef MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H #define MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H #define FRAME_OPCODE_MASK 0x0f enum { FRAME_CONT, FRAME_TXT, FRAME_BIN, FRAME_CLOSE = 0x8, FRAME_PING, FRAME_PONG }; #endif // MICROPY_INCLUDED_EXTMOD_MODWEBSOCKET_H ================================================ FILE: extmod/re1.5/charclass.c ================================================ #include "re1.5.h" int _re1_5_classmatch(const char *pc, const char *sp) { // pc points to "cnt" byte after opcode int is_positive = (pc[-1] == Class); int cnt = *pc++; while (cnt--) { if (*sp >= *pc && *sp <= pc[1]) return is_positive; pc += 2; } return !is_positive; } int _re1_5_namedclassmatch(const char *pc, const char *sp) { // pc points to name of class int off = (*pc >> 5) & 1; if ((*pc | 0x20) == 'd') { if (!(*sp >= '0' && *sp <= '9')) { off ^= 1; } } else if ((*pc | 0x20) == 's') { if (!(*sp == ' ' || (*sp >= '\t' && *sp <= '\r'))) { off ^= 1; } } else { // w if (!((*sp >= 'A' && *sp <= 'Z') || (*sp >= 'a' && *sp <= 'z') || (*sp >= '0' && *sp <= '9') || *sp == '_')) { off ^= 1; } } return off; } ================================================ FILE: extmod/re1.5/compilecode.c ================================================ // Copyright 2014 Paul Sokolovsky. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "re1.5.h" #define INSERT_CODE(at, num, pc) \ ((code ? memmove(code + at + num, code + at, pc - at) : 0), pc += num) #define REL(at, to) (to - at - 2) #define EMIT(at, byte) (code ? (code[at] = byte) : (at)) #define PC (prog->bytelen) static const char *_compilecode(const char *re, ByteProg *prog, int sizecode) { char *code = sizecode ? NULL : prog->insts; int start = PC; int term = PC; int alt_label = 0; for (; *re && *re != ')'; re++) { switch (*re) { case '\\': re++; if (!*re) return NULL; // Trailing backslash if ((*re | 0x20) == 'd' || (*re | 0x20) == 's' || (*re | 0x20) == 'w') { term = PC; EMIT(PC++, NamedClass); EMIT(PC++, *re); prog->len++; break; } default: term = PC; EMIT(PC++, Char); EMIT(PC++, *re); prog->len++; break; case '.': term = PC; EMIT(PC++, Any); prog->len++; break; case '[': { int cnt; term = PC; re++; if (*re == '^') { EMIT(PC++, ClassNot); re++; } else { EMIT(PC++, Class); } PC++; // Skip # of pair byte prog->len++; for (cnt = 0; *re != ']'; re++, cnt++) { if (*re == '\\') { ++re; } if (!*re) return NULL; EMIT(PC++, *re); if (re[1] == '-' && re[2] != ']') { re += 2; } EMIT(PC++, *re); } EMIT(term + 1, cnt); break; } case '(': { term = PC; int sub = 0; int capture = re[1] != '?' || re[2] != ':'; if (capture) { sub = ++prog->sub; EMIT(PC++, Save); EMIT(PC++, 2 * sub); prog->len++; } else { re += 2; } re = _compilecode(re + 1, prog, sizecode); if (re == NULL || *re != ')') return NULL; // error, or no matching paren if (capture) { EMIT(PC++, Save); EMIT(PC++, 2 * sub + 1); prog->len++; } break; } case '?': if (PC == term) return NULL; // nothing to repeat INSERT_CODE(term, 2, PC); if (re[1] == '?') { EMIT(term, RSplit); re++; } else { EMIT(term, Split); } EMIT(term + 1, REL(term, PC)); prog->len++; term = PC; break; case '*': if (PC == term) return NULL; // nothing to repeat INSERT_CODE(term, 2, PC); EMIT(PC, Jmp); EMIT(PC + 1, REL(PC, term)); PC += 2; if (re[1] == '?') { EMIT(term, RSplit); re++; } else { EMIT(term, Split); } EMIT(term + 1, REL(term, PC)); prog->len += 2; term = PC; break; case '+': if (PC == term) return NULL; // nothing to repeat if (re[1] == '?') { EMIT(PC, Split); re++; } else { EMIT(PC, RSplit); } EMIT(PC + 1, REL(PC, term)); PC += 2; prog->len++; term = PC; break; case '|': if (alt_label) { EMIT(alt_label, REL(alt_label, PC) + 1); } INSERT_CODE(start, 2, PC); EMIT(PC++, Jmp); alt_label = PC++; EMIT(start, Split); EMIT(start + 1, REL(start, PC)); prog->len += 2; term = PC; break; case '^': EMIT(PC++, Bol); prog->len++; term = PC; break; case '$': EMIT(PC++, Eol); prog->len++; term = PC; break; } } if (alt_label) { EMIT(alt_label, REL(alt_label, PC) + 1); } return re; } int re1_5_sizecode(const char *re) { ByteProg dummyprog = { // Save 0, Save 1, Match; more bytes for "search" (vs "match") prefix code .bytelen = 5 + NON_ANCHORED_PREFIX }; if (_compilecode(re, &dummyprog, /*sizecode*/1) == NULL) return -1; return dummyprog.bytelen; } int re1_5_compilecode(ByteProg *prog, const char *re) { prog->len = 0; prog->bytelen = 0; prog->sub = 0; // Add code to implement non-anchored operation ("search"), // for anchored operation ("match"), this code will be just skipped. // TODO: Implement search in much more efficient manner prog->insts[prog->bytelen++] = RSplit; prog->insts[prog->bytelen++] = 3; prog->insts[prog->bytelen++] = Any; prog->insts[prog->bytelen++] = Jmp; prog->insts[prog->bytelen++] = -5; prog->len += 3; prog->insts[prog->bytelen++] = Save; prog->insts[prog->bytelen++] = 0; prog->len++; re = _compilecode(re, prog, /*sizecode*/0); if (re == NULL || *re) return 1; prog->insts[prog->bytelen++] = Save; prog->insts[prog->bytelen++] = 1; prog->len++; prog->insts[prog->bytelen++] = Match; prog->len++; return 0; } #if 0 int main(int argc, char *argv[]) { int pc = 0; ByteProg *code = re1_5_compilecode(argv[1]); re1_5_dumpcode(code); } #endif ================================================ FILE: extmod/re1.5/dumpcode.c ================================================ // Copyright 2014 Paul Sokolovsky. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "re1.5.h" void re1_5_dumpcode(ByteProg *prog) { int pc = 0; char *code = prog->insts; while (pc < prog->bytelen) { printf("%2d: ", pc); switch(code[pc++]) { default: assert(0); // re1_5_fatal("printprog"); case Split: printf("split %d (%d)\n", pc + (signed char)code[pc] + 1, (signed char)code[pc]); pc++; break; case RSplit: printf("rsplit %d (%d)\n", pc + (signed char)code[pc] + 1, (signed char)code[pc]); pc++; break; case Jmp: printf("jmp %d (%d)\n", pc + (signed char)code[pc] + 1, (signed char)code[pc]); pc++; break; case Char: printf("char %c\n", code[pc++]); break; case Any: printf("any\n"); break; case Class: case ClassNot: { int num = code[pc]; printf("class%s %d", (code[pc - 1] == ClassNot ? "not" : ""), num); pc++; while (num--) { printf(" 0x%02x-0x%02x", code[pc], code[pc + 1]); pc += 2; } printf("\n"); break; } case NamedClass: printf("namedclass %c\n", code[pc++]); break; case Match: printf("match\n"); break; case Save: printf("save %d\n", (unsigned char)code[pc++]); break; case Bol: printf("assert bol\n"); break; case Eol: printf("assert eol\n"); break; } } printf("Bytes: %d, insts: %d\n", prog->bytelen, prog->len); } ================================================ FILE: extmod/re1.5/re1.5.h ================================================ // Copyright 2007-2009 Russ Cox. All Rights Reserved. // Copyright 2014 Paul Sokolovsky. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef _RE1_5_REGEXP__H #define _RE1_5_REGEXP__H #include #include #include #include #include #define nil ((void*)0) #define nelem(x) (sizeof(x)/sizeof((x)[0])) typedef struct Regexp Regexp; typedef struct Prog Prog; typedef struct ByteProg ByteProg; typedef struct Inst Inst; typedef struct Subject Subject; struct Regexp { int type; int n; int ch; Regexp *left; Regexp *right; }; enum /* Regexp.type */ { Alt = 1, Cat, Lit, Dot, Paren, Quest, Star, Plus, }; Regexp *parse(char*); Regexp *reg(int type, Regexp *left, Regexp *right); void printre(Regexp*); #ifndef re1_5_fatal void re1_5_fatal(char*); #endif #ifndef re1_5_stack_chk #define re1_5_stack_chk() #endif void *mal(int); struct Prog { Inst *start; int len; }; struct ByteProg { int bytelen; int len; int sub; char insts[0]; }; struct Inst { int opcode; int c; int n; Inst *x; Inst *y; int gen; // global state, oooh! }; enum /* Inst.opcode */ { // Instructions which consume input bytes (and thus fail if none left) CONSUMERS = 1, Char = CONSUMERS, Any, Class, ClassNot, NamedClass, ASSERTS = 0x50, Bol = ASSERTS, Eol, // Instructions which take relative offset as arg JUMPS = 0x60, Jmp = JUMPS, Split, RSplit, // Other (special) instructions Save = 0x7e, Match = 0x7f, }; #define inst_is_consumer(inst) ((inst) < ASSERTS) #define inst_is_jump(inst) ((inst) & 0x70 == JUMPS) Prog *compile(Regexp*); void printprog(Prog*); extern int gen; enum { MAXSUB = 20 }; typedef struct Sub Sub; struct Sub { int ref; int nsub; const char *sub[MAXSUB]; }; Sub *newsub(int n); Sub *incref(Sub*); Sub *copy(Sub*); Sub *update(Sub*, int, const char*); void decref(Sub*); struct Subject { const char *begin; const char *end; }; #define NON_ANCHORED_PREFIX 5 #define HANDLE_ANCHORED(bytecode, is_anchored) ((is_anchored) ? (bytecode) + NON_ANCHORED_PREFIX : (bytecode)) int re1_5_backtrack(ByteProg*, Subject*, const char**, int, int); int re1_5_pikevm(ByteProg*, Subject*, const char**, int, int); int re1_5_recursiveloopprog(ByteProg*, Subject*, const char**, int, int); int re1_5_recursiveprog(ByteProg*, Subject*, const char**, int, int); int re1_5_thompsonvm(ByteProg*, Subject*, const char**, int, int); int re1_5_sizecode(const char *re); int re1_5_compilecode(ByteProg *prog, const char *re); void re1_5_dumpcode(ByteProg *prog); void cleanmarks(ByteProg *prog); int _re1_5_classmatch(const char *pc, const char *sp); int _re1_5_namedclassmatch(const char *pc, const char *sp); #endif /*_RE1_5_REGEXP__H*/ ================================================ FILE: extmod/re1.5/recursiveloop.c ================================================ // Copyright 2007-2009 Russ Cox. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "re1.5.h" static int recursiveloop(char *pc, const char *sp, Subject *input, const char **subp, int nsubp) { const char *old; int off; re1_5_stack_chk(); for(;;) { if(inst_is_consumer(*pc)) { // If we need to match a character, but there's none left, it's fail if(sp >= input->end) return 0; } switch(*pc++) { case Char: if(*sp != *pc++) return 0; case Any: sp++; continue; case Class: case ClassNot: if (!_re1_5_classmatch(pc, sp)) return 0; pc += *(unsigned char*)pc * 2 + 1; sp++; continue; case NamedClass: if (!_re1_5_namedclassmatch(pc, sp)) return 0; pc++; sp++; continue; case Match: return 1; case Jmp: off = (signed char)*pc++; pc = pc + off; continue; case Split: off = (signed char)*pc++; if(recursiveloop(pc, sp, input, subp, nsubp)) return 1; pc = pc + off; continue; case RSplit: off = (signed char)*pc++; if(recursiveloop(pc + off, sp, input, subp, nsubp)) return 1; continue; case Save: off = (unsigned char)*pc++; if(off >= nsubp) { continue; } old = subp[off]; subp[off] = sp; if(recursiveloop(pc, sp, input, subp, nsubp)) return 1; subp[off] = old; return 0; case Bol: if(sp != input->begin) return 0; continue; case Eol: if(sp != input->end) return 0; continue; } re1_5_fatal("recursiveloop"); } } int re1_5_recursiveloopprog(ByteProg *prog, Subject *input, const char **subp, int nsubp, int is_anchored) { return recursiveloop(HANDLE_ANCHORED(prog->insts, is_anchored), input->begin, input, subp, nsubp); } ================================================ FILE: extmod/uos_dupterm.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2016 Paul Sokolovsky * Copyright (c) 2017-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/mpconfig.h" #include "py/runtime.h" #include "py/objtuple.h" #include "py/objarray.h" #include "py/stream.h" #include "extmod/misc.h" #include "lib/utils/interrupt_char.h" #if MICROPY_PY_OS_DUPTERM void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) { mp_obj_t term = MP_STATE_VM(dupterm_objs[dupterm_idx]); MP_STATE_VM(dupterm_objs[dupterm_idx]) = MP_OBJ_NULL; mp_printf(&mp_plat_print, msg); if (exc != MP_OBJ_NULL) { mp_obj_print_exception(&mp_plat_print, exc); } nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_stream_close(term); nlr_pop(); } else { // Ignore any errors during stream closing } } uintptr_t mp_uos_dupterm_poll(uintptr_t poll_flags) { uintptr_t poll_flags_out = 0; for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { mp_obj_t s = MP_STATE_VM(dupterm_objs[idx]); if (s == MP_OBJ_NULL) { continue; } int errcode = 0; mp_uint_t ret = 0; const mp_stream_p_t *stream_p = mp_get_stream(s); #if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM if (mp_uos_dupterm_is_builtin_stream(s)) { ret = stream_p->ioctl(s, MP_STREAM_POLL, poll_flags, &errcode); } else #endif { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { ret = stream_p->ioctl(s, MP_STREAM_POLL, poll_flags, &errcode); nlr_pop(); } else { // Ignore error with ioctl } } if (ret != MP_STREAM_ERROR) { poll_flags_out |= ret; if (poll_flags_out == poll_flags) { // Finish early if all requested flags are set break; } } } return poll_flags_out; } int mp_uos_dupterm_rx_chr(void) { for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { continue; } #if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM if (mp_uos_dupterm_is_builtin_stream(MP_STATE_VM(dupterm_objs[idx]))) { byte buf[1]; int errcode = 0; const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx])); mp_uint_t out_sz = stream_p->read(MP_STATE_VM(dupterm_objs[idx]), buf, 1, &errcode); if (errcode == 0 && out_sz != 0) { return buf[0]; } else { continue; } } #endif nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { byte buf[1]; int errcode; const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx])); mp_uint_t out_sz = stream_p->read(MP_STATE_VM(dupterm_objs[idx]), buf, 1, &errcode); if (out_sz == 0) { nlr_pop(); mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL); } else if (out_sz == MP_STREAM_ERROR) { // errcode is valid if (mp_is_nonblocking_error(errcode)) { nlr_pop(); } else { mp_raise_OSError(errcode); } } else { // read 1 byte nlr_pop(); if (buf[0] == mp_interrupt_char) { // Signal keyboard interrupt to be raised as soon as the VM resumes mp_keyboard_interrupt(); return -2; } return buf[0]; } } else { mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val)); } } // No chars available return -1; } void mp_uos_dupterm_tx_strn(const char *str, size_t len) { for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { continue; } #if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM if (mp_uos_dupterm_is_builtin_stream(MP_STATE_VM(dupterm_objs[idx]))) { int errcode = 0; const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx])); stream_p->write(MP_STATE_VM(dupterm_objs[idx]), str, len, &errcode); continue; } #endif nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_stream_write(MP_STATE_VM(dupterm_objs[idx]), str, len, MP_STREAM_RW_WRITE); nlr_pop(); } else { mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val)); } } } STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) { mp_int_t idx = 0; if (n_args == 2) { idx = mp_obj_get_int(args[1]); } if (idx < 0 || idx >= MICROPY_PY_OS_DUPTERM) { mp_raise_ValueError("invalid dupterm index"); } mp_obj_t previous_obj = MP_STATE_VM(dupterm_objs[idx]); if (previous_obj == MP_OBJ_NULL) { previous_obj = mp_const_none; } if (args[0] == mp_const_none) { MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL; } else { mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); MP_STATE_VM(dupterm_objs[idx]) = args[0]; } return previous_obj; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm); #endif ================================================ FILE: extmod/utime_mphal.c ================================================ /* * 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. */ #include "py/mpconfig.h" #if MICROPY_PY_UTIME_MP_HAL #include #include "py/obj.h" #include "py/mphal.h" #include "py/smallint.h" #include "py/runtime.h" #include "extmod/utime_mphal.h" STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) { #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 return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_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_utime_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_utime_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_UTIME_TICKS_PERIOD - 1)); } MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_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_UTIME_TICKS_PERIOD - 1)); } MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_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_UTIME_TICKS_PERIOD - 1)); } MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_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_UTIME_TICKS_PERIOD / 2) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1)) - MICROPY_PY_UTIME_TICKS_PERIOD / 2; return MP_OBJ_NEW_SMALL_INT(diff); } MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_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); return MP_OBJ_NEW_SMALL_INT((ticks + delta) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1)); } MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj, time_ticks_add); #endif // MICROPY_PY_UTIME_MP_HAL ================================================ FILE: extmod/utime_mphal.h ================================================ /* * 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_UTIME_MPHAL_H #define MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H #include "py/obj.h" MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj); #endif // MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H ================================================ FILE: extmod/uzlib/adler32.c ================================================ /* * Adler-32 checksum * * Copyright (c) 2003 by Joergen Ibsen / Jibz * All Rights Reserved * * http://www.ibsensoftware.com/ * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be * held liable for any damages arising from the use of * this software. * * Permission is granted to anyone to use this software * for any purpose, including commercial applications, * and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be * misrepresented; you must not claim that you * wrote the original software. If you use this * software in a product, an acknowledgment in * the product documentation would be appreciated * but is not required. * * 2. Altered source versions must be plainly marked * as such, and must not be misrepresented as * being the original software. * * 3. This notice may not be removed or altered from * any source distribution. */ /* * Adler-32 algorithm taken from the zlib source, which is * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler */ #include "tinf.h" #define A32_BASE 65521 #define A32_NMAX 5552 uint32_t uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum /* 1 */) { const unsigned char *buf = (const unsigned char *)data; unsigned int s1 = prev_sum & 0xffff; unsigned int s2 = prev_sum >> 16; while (length > 0) { int k = length < A32_NMAX ? length : A32_NMAX; int i; for (i = k / 16; i; --i, buf += 16) { s1 += buf[0]; s2 += s1; s1 += buf[1]; s2 += s1; s1 += buf[2]; s2 += s1; s1 += buf[3]; s2 += s1; s1 += buf[4]; s2 += s1; s1 += buf[5]; s2 += s1; s1 += buf[6]; s2 += s1; s1 += buf[7]; s2 += s1; s1 += buf[8]; s2 += s1; s1 += buf[9]; s2 += s1; s1 += buf[10]; s2 += s1; s1 += buf[11]; s2 += s1; s1 += buf[12]; s2 += s1; s1 += buf[13]; s2 += s1; s1 += buf[14]; s2 += s1; s1 += buf[15]; s2 += s1; } for (i = k % 16; i; --i) { s1 += *buf++; s2 += s1; } s1 %= A32_BASE; s2 %= A32_BASE; length -= k; } return (s2 << 16) | s1; } ================================================ FILE: extmod/uzlib/crc32.c ================================================ /* * CRC32 checksum * * Copyright (c) 1998-2003 by Joergen Ibsen / Jibz * All Rights Reserved * * http://www.ibsensoftware.com/ * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be * held liable for any damages arising from the use of * this software. * * Permission is granted to anyone to use this software * for any purpose, including commercial applications, * and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be * misrepresented; you must not claim that you * wrote the original software. If you use this * software in a product, an acknowledgment in * the product documentation would be appreciated * but is not required. * * 2. Altered source versions must be plainly marked * as such, and must not be misrepresented as * being the original software. * * 3. This notice may not be removed or altered from * any source distribution. */ /* * CRC32 algorithm taken from the zlib source, which is * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler */ #include "tinf.h" static const unsigned int tinf_crc32tab[16] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; /* crc is previous value for incremental computation, 0xffffffff initially */ uint32_t uzlib_crc32(const void *data, unsigned int length, uint32_t crc) { const unsigned char *buf = (const unsigned char *)data; unsigned int i; for (i = 0; i < length; ++i) { crc ^= buf[i]; crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); } // return value suitable for passing in next time, for final value invert it return crc/* ^ 0xffffffff*/; } ================================================ FILE: extmod/uzlib/defl_static.h ================================================ /* * Copyright (c) uzlib authors * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be * held liable for any damages arising from the use of * this software. * * Permission is granted to anyone to use this software * for any purpose, including commercial applications, * and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be * misrepresented; you must not claim that you * wrote the original software. If you use this * software in a product, an acknowledgment in * the product documentation would be appreciated * but is not required. * * 2. Altered source versions must be plainly marked * as such, and must not be misrepresented as * being the original software. * * 3. This notice may not be removed or altered from * any source distribution. */ /* This files contains type declaration and prototypes for defl_static.c. They may be altered/distinct from the originals used in PuTTY source code. */ struct Outbuf { unsigned char *outbuf; int outlen, outsize; unsigned long outbits; int noutbits; int comp_disabled; }; void outbits(struct Outbuf *out, unsigned long bits, int nbits); void zlib_start_block(struct Outbuf *ctx); void zlib_finish_block(struct Outbuf *ctx); void zlib_literal(struct Outbuf *ectx, unsigned char c); void zlib_match(struct Outbuf *ectx, int distance, int len); ================================================ FILE: extmod/uzlib/tinf.h ================================================ /* Compatibility header for the original tinf lib/older versions of uzlib. Note: may be removed in the future, please migrate to uzlib.h. */ #include "uzlib.h" ================================================ FILE: extmod/uzlib/tinf_compat.h ================================================ /* This header contains compatibility defines for the original tinf API and uzlib 2.x and below API. These defines are deprecated and going to be removed in the future, so applications should migrate to new uzlib API. */ #define TINF_DATA struct uzlib_uncomp #define destSize dest_size #define destStart dest_start #define readSource source_read_cb ================================================ FILE: extmod/uzlib/tinfgzip.c ================================================ /* * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) * * Copyright (c) 2003 by Joergen Ibsen / Jibz * All Rights Reserved * * http://www.ibsensoftware.com/ * * Copyright (c) 2014-2018 by Paul Sokolovsky * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be * held liable for any damages arising from the use of * this software. * * Permission is granted to anyone to use this software * for any purpose, including commercial applications, * and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be * misrepresented; you must not claim that you * wrote the original software. If you use this * software in a product, an acknowledgment in * the product documentation would be appreciated * but is not required. * * 2. Altered source versions must be plainly marked * as such, and must not be misrepresented as * being the original software. * * 3. This notice may not be removed or altered from * any source distribution. */ #include "tinf.h" #define FTEXT 1 #define FHCRC 2 #define FEXTRA 4 #define FNAME 8 #define FCOMMENT 16 void tinf_skip_bytes(TINF_DATA *d, int num); uint16_t tinf_get_uint16(TINF_DATA *d); void tinf_skip_bytes(TINF_DATA *d, int num) { while (num--) uzlib_get_byte(d); } uint16_t tinf_get_uint16(TINF_DATA *d) { unsigned int v = uzlib_get_byte(d); v = (uzlib_get_byte(d) << 8) | v; return v; } int uzlib_gzip_parse_header(TINF_DATA *d) { unsigned char flg; /* -- check format -- */ /* check id bytes */ if (uzlib_get_byte(d) != 0x1f || uzlib_get_byte(d) != 0x8b) return TINF_DATA_ERROR; /* check method is deflate */ if (uzlib_get_byte(d) != 8) return TINF_DATA_ERROR; /* get flag byte */ flg = uzlib_get_byte(d); /* check that reserved bits are zero */ if (flg & 0xe0) return TINF_DATA_ERROR; /* -- find start of compressed data -- */ /* skip rest of base header of 10 bytes */ tinf_skip_bytes(d, 6); /* skip extra data if present */ if (flg & FEXTRA) { unsigned int xlen = tinf_get_uint16(d); tinf_skip_bytes(d, xlen); } /* skip file name if present */ if (flg & FNAME) { while (uzlib_get_byte(d)); } /* skip file comment if present */ if (flg & FCOMMENT) { while (uzlib_get_byte(d)); } /* check header crc if present */ if (flg & FHCRC) { /*unsigned int hcrc =*/ tinf_get_uint16(d); // TODO: Check! // if (hcrc != (tinf_crc32(src, start - src) & 0x0000ffff)) // return TINF_DATA_ERROR; } /* initialize for crc32 checksum */ d->checksum_type = TINF_CHKSUM_CRC; d->checksum = ~0; return TINF_OK; } ================================================ FILE: extmod/uzlib/tinflate.c ================================================ /* * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) * * Copyright (c) 2003 by Joergen Ibsen / Jibz * All Rights Reserved * http://www.ibsensoftware.com/ * * Copyright (c) 2014-2018 by Paul Sokolovsky * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be * held liable for any damages arising from the use of * this software. * * Permission is granted to anyone to use this software * for any purpose, including commercial applications, * and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be * misrepresented; you must not claim that you * wrote the original software. If you use this * software in a product, an acknowledgment in * the product documentation would be appreciated * but is not required. * * 2. Altered source versions must be plainly marked * as such, and must not be misrepresented as * being the original software. * * 3. This notice may not be removed or altered from * any source distribution. */ #include #include "tinf.h" #define UZLIB_DUMP_ARRAY(heading, arr, size) \ { \ printf("%s", heading); \ for (int i = 0; i < size; ++i) { \ printf(" %d", (arr)[i]); \ } \ printf("\n"); \ } uint32_t tinf_get_le_uint32(TINF_DATA *d); uint32_t tinf_get_be_uint32(TINF_DATA *d); /* --------------------------------------------------- * * -- uninitialized global data (static structures) -- * * --------------------------------------------------- */ #ifdef RUNTIME_BITS_TABLES /* extra bits and base tables for length codes */ unsigned char length_bits[30]; unsigned short length_base[30]; /* extra bits and base tables for distance codes */ unsigned char dist_bits[30]; unsigned short dist_base[30]; #else const unsigned char length_bits[30] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; const unsigned short length_base[30] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 }; const unsigned char dist_bits[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; const unsigned short dist_base[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; #endif /* special ordering of code length codes */ const unsigned char clcidx[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; /* ----------------------- * * -- utility functions -- * * ----------------------- */ #ifdef RUNTIME_BITS_TABLES /* build extra bits and base tables */ static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first) { int i, sum; /* build bits table */ for (i = 0; i < delta; ++i) bits[i] = 0; for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta; /* build base table */ for (sum = first, i = 0; i < 30; ++i) { base[i] = sum; sum += 1 << bits[i]; } } #endif /* build the fixed huffman trees */ static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt) { int i; /* build fixed length tree */ for (i = 0; i < 7; ++i) lt->table[i] = 0; lt->table[7] = 24; lt->table[8] = 152; lt->table[9] = 112; for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i; for (i = 0; i < 144; ++i) lt->trans[24 + i] = i; for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i; for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i; /* build fixed distance tree */ for (i = 0; i < 5; ++i) dt->table[i] = 0; dt->table[5] = 32; for (i = 0; i < 32; ++i) dt->trans[i] = i; } /* given an array of code lengths, build a tree */ static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num) { unsigned short offs[16]; unsigned int i, sum; /* clear code length count table */ for (i = 0; i < 16; ++i) t->table[i] = 0; /* scan symbol lengths, and sum code length counts */ for (i = 0; i < num; ++i) t->table[lengths[i]]++; #if UZLIB_CONF_DEBUG_LOG >= 2 UZLIB_DUMP_ARRAY("codelen counts:", t->table, TINF_ARRAY_SIZE(t->table)); #endif /* In the lengths array, 0 means unused code. So, t->table[0] now contains number of unused codes. But table's purpose is to contain # of codes of particular length, and there're 0 codes of length 0. */ t->table[0] = 0; /* compute offset table for distribution sort */ for (sum = 0, i = 0; i < 16; ++i) { offs[i] = sum; sum += t->table[i]; } #if UZLIB_CONF_DEBUG_LOG >= 2 UZLIB_DUMP_ARRAY("codelen offsets:", offs, TINF_ARRAY_SIZE(offs)); #endif /* create code->symbol translation table (symbols sorted by code) */ for (i = 0; i < num; ++i) { if (lengths[i]) t->trans[offs[lengths[i]]++] = i; } } /* ---------------------- * * -- decode functions -- * * ---------------------- */ unsigned char uzlib_get_byte(TINF_DATA *d) { /* If end of source buffer is not reached, return next byte from source buffer. */ if (d->source < d->source_limit) { return *d->source++; } /* Otherwise if there's callback and we haven't seen EOF yet, try to read next byte using it. (Note: the callback can also update ->source and ->source_limit). */ if (d->readSource && !d->eof) { int val = d->readSource(d); if (val >= 0) { return (unsigned char)val; } } /* Otherwise, we hit EOF (either from ->readSource() or from exhaustion of the buffer), and it will be "sticky", i.e. further calls to this function will end up here too. */ d->eof = true; return 0; } uint32_t tinf_get_le_uint32(TINF_DATA *d) { uint32_t val = 0; int i; for (i = 4; i--;) { val = val >> 8 | ((uint32_t)uzlib_get_byte(d)) << 24; } return val; } uint32_t tinf_get_be_uint32(TINF_DATA *d) { uint32_t val = 0; int i; for (i = 4; i--;) { val = val << 8 | uzlib_get_byte(d); } return val; } /* get one bit from source stream */ static int tinf_getbit(TINF_DATA *d) { unsigned int bit; /* check if tag is empty */ if (!d->bitcount--) { /* load next tag */ d->tag = uzlib_get_byte(d); d->bitcount = 7; } /* shift bit out of tag */ bit = d->tag & 0x01; d->tag >>= 1; return bit; } /* read a num bit value from a stream and add base */ static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base) { unsigned int val = 0; /* read num bits */ if (num) { unsigned int limit = 1 << (num); unsigned int mask; for (mask = 1; mask < limit; mask *= 2) if (tinf_getbit(d)) val += mask; } return val + base; } /* given a data stream and a tree, decode a symbol */ static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t) { int sum = 0, cur = 0, len = 0; /* get more bits while code value is above sum */ do { cur = 2*cur + tinf_getbit(d); if (++len == TINF_ARRAY_SIZE(t->table)) { return TINF_DATA_ERROR; } sum += t->table[len]; cur -= t->table[len]; } while (cur >= 0); sum += cur; #if UZLIB_CONF_PARANOID_CHECKS if (sum < 0 || sum >= TINF_ARRAY_SIZE(t->trans)) { return TINF_DATA_ERROR; } #endif return t->trans[sum]; } /* given a data stream, decode dynamic trees from it */ static int tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) { /* code lengths for 288 literal/len symbols and 32 dist symbols */ unsigned char lengths[288+32]; unsigned int hlit, hdist, hclen, hlimit; unsigned int i, num, length; /* get 5 bits HLIT (257-286) */ hlit = tinf_read_bits(d, 5, 257); /* get 5 bits HDIST (1-32) */ hdist = tinf_read_bits(d, 5, 1); /* get 4 bits HCLEN (4-19) */ hclen = tinf_read_bits(d, 4, 4); for (i = 0; i < 19; ++i) lengths[i] = 0; /* read code lengths for code length alphabet */ for (i = 0; i < hclen; ++i) { /* get 3 bits code length (0-7) */ unsigned int clen = tinf_read_bits(d, 3, 0); lengths[clcidx[i]] = clen; } /* build code length tree, temporarily use length tree */ tinf_build_tree(lt, lengths, 19); /* decode code lengths for the dynamic trees */ hlimit = hlit + hdist; for (num = 0; num < hlimit; ) { int sym = tinf_decode_symbol(d, lt); unsigned char fill_value = 0; int lbits, lbase = 3; /* error decoding */ if (sym < 0) return sym; switch (sym) { case 16: /* copy previous code length 3-6 times (read 2 bits) */ if (num == 0) return TINF_DATA_ERROR; fill_value = lengths[num - 1]; lbits = 2; break; case 17: /* repeat code length 0 for 3-10 times (read 3 bits) */ lbits = 3; break; case 18: /* repeat code length 0 for 11-138 times (read 7 bits) */ lbits = 7; lbase = 11; break; default: /* values 0-15 represent the actual code lengths */ lengths[num++] = sym; /* continue the for loop */ continue; } /* special code length 16-18 are handled here */ length = tinf_read_bits(d, lbits, lbase); if (num + length > hlimit) return TINF_DATA_ERROR; for (; length; --length) { lengths[num++] = fill_value; } } #if UZLIB_CONF_DEBUG_LOG >= 2 printf("lit code lengths (%d):", hlit); UZLIB_DUMP_ARRAY("", lengths, hlit); printf("dist code lengths (%d):", hdist); UZLIB_DUMP_ARRAY("", lengths + hlit, hdist); #endif #if UZLIB_CONF_PARANOID_CHECKS /* Check that there's "end of block" symbol */ if (lengths[256] == 0) { return TINF_DATA_ERROR; } #endif /* build dynamic trees */ tinf_build_tree(lt, lengths, hlit); tinf_build_tree(dt, lengths + hlit, hdist); return TINF_OK; } /* ----------------------------- * * -- block inflate functions -- * * ----------------------------- */ /* given a stream and two trees, inflate next byte of output */ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) { if (d->curlen == 0) { unsigned int offs; int dist; int sym = tinf_decode_symbol(d, lt); //printf("huff sym: %02x\n", sym); if (d->eof) { return TINF_DATA_ERROR; } /* literal byte */ if (sym < 256) { TINF_PUT(d, sym); return TINF_OK; } /* end of block */ if (sym == 256) { return TINF_DONE; } /* substring from sliding dictionary */ sym -= 257; if (sym >= 29) { return TINF_DATA_ERROR; } /* possibly get more bits from length code */ d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]); dist = tinf_decode_symbol(d, dt); if (dist >= 30) { return TINF_DATA_ERROR; } /* possibly get more bits from distance code */ offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); /* calculate and validate actual LZ offset to use */ if (d->dict_ring) { if (offs > d->dict_size) { return TINF_DICT_ERROR; } /* Note: unlike full-dest-in-memory case below, we don't try to catch offset which points to not yet filled part of the dictionary here. Doing so would require keeping another variable to track "filled in" size of the dictionary. Appearance of such an offset cannot lead to accessing memory outside of the dictionary buffer, and clients which don't want to leak unrelated information, should explicitly initialize dictionary buffer passed to uzlib. */ d->lzOff = d->dict_idx - offs; if (d->lzOff < 0) { d->lzOff += d->dict_size; } } else { /* catch trying to point before the start of dest buffer */ if (offs > d->dest - d->destStart) { return TINF_DATA_ERROR; } d->lzOff = -offs; } } /* copy next byte from dict substring */ if (d->dict_ring) { TINF_PUT(d, d->dict_ring[d->lzOff]); if ((unsigned)++d->lzOff == d->dict_size) { d->lzOff = 0; } } else { d->dest[0] = d->dest[d->lzOff]; d->dest++; } d->curlen--; return TINF_OK; } /* inflate next byte from uncompressed block of data */ static int tinf_inflate_uncompressed_block(TINF_DATA *d) { if (d->curlen == 0) { unsigned int length, invlength; /* get length */ length = uzlib_get_byte(d); length += 256 * uzlib_get_byte(d); /* get one's complement of length */ invlength = uzlib_get_byte(d); invlength += 256 * uzlib_get_byte(d); /* check length */ if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; /* increment length to properly return TINF_DONE below, without producing data at the same time */ d->curlen = length + 1; /* make sure we start next block on a byte boundary */ d->bitcount = 0; } if (--d->curlen == 0) { return TINF_DONE; } unsigned char c = uzlib_get_byte(d); TINF_PUT(d, c); return TINF_OK; } /* ---------------------- * * -- public functions -- * * ---------------------- */ /* initialize global (static) data */ void uzlib_init(void) { #ifdef RUNTIME_BITS_TABLES /* build extra bits and base tables */ tinf_build_bits_base(length_bits, length_base, 4, 3); tinf_build_bits_base(dist_bits, dist_base, 2, 1); /* fix a special case */ length_bits[28] = 0; length_base[28] = 258; #endif } /* initialize decompression structure */ void uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen) { d->eof = 0; d->bitcount = 0; d->bfinal = 0; d->btype = -1; d->dict_size = dictLen; d->dict_ring = dict; d->dict_idx = 0; d->curlen = 0; } /* inflate next output bytes from compressed stream */ int uzlib_uncompress(TINF_DATA *d) { do { int res; /* start a new block */ if (d->btype == -1) { next_blk: /* read final block flag */ d->bfinal = tinf_getbit(d); /* read block type (2 bits) */ d->btype = tinf_read_bits(d, 2, 0); #if UZLIB_CONF_DEBUG_LOG >= 1 printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal); #endif if (d->btype == 1) { /* build fixed huffman trees */ tinf_build_fixed_trees(&d->ltree, &d->dtree); } else if (d->btype == 2) { /* decode trees from stream */ res = tinf_decode_trees(d, &d->ltree, &d->dtree); if (res != TINF_OK) { return res; } } } /* process current block */ switch (d->btype) { case 0: /* decompress uncompressed block */ res = tinf_inflate_uncompressed_block(d); break; case 1: case 2: /* decompress block with fixed/dynamic huffman trees */ /* trees were decoded previously, so it's the same routine for both */ res = tinf_inflate_block_data(d, &d->ltree, &d->dtree); break; default: return TINF_DATA_ERROR; } if (res == TINF_DONE && !d->bfinal) { /* the block has ended (without producing more data), but we can't return without data, so start procesing next block */ goto next_blk; } if (res != TINF_OK) { return res; } } while (d->dest < d->dest_limit); return TINF_OK; } /* inflate next output bytes from compressed stream, updating checksum, and at the end of stream, verify it */ int uzlib_uncompress_chksum(TINF_DATA *d) { int res; unsigned char *data = d->dest; res = uzlib_uncompress(d); if (res < 0) return res; switch (d->checksum_type) { case TINF_CHKSUM_ADLER: d->checksum = uzlib_adler32(data, d->dest - data, d->checksum); break; case TINF_CHKSUM_CRC: d->checksum = uzlib_crc32(data, d->dest - data, d->checksum); break; } if (res == TINF_DONE) { unsigned int val; switch (d->checksum_type) { case TINF_CHKSUM_ADLER: val = tinf_get_be_uint32(d); if (d->checksum != val) { return TINF_CHKSUM_ERROR; } break; case TINF_CHKSUM_CRC: val = tinf_get_le_uint32(d); if (~d->checksum != val) { return TINF_CHKSUM_ERROR; } // Uncompressed size. TODO: Check val = tinf_get_le_uint32(d); break; } } return res; } ================================================ FILE: extmod/uzlib/tinfzlib.c ================================================ /* * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) * * Copyright (c) 2003 by Joergen Ibsen / Jibz * All Rights Reserved * * http://www.ibsensoftware.com/ * * Copyright (c) 2014-2018 by Paul Sokolovsky * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be * held liable for any damages arising from the use of * this software. * * Permission is granted to anyone to use this software * for any purpose, including commercial applications, * and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be * misrepresented; you must not claim that you * wrote the original software. If you use this * software in a product, an acknowledgment in * the product documentation would be appreciated * but is not required. * * 2. Altered source versions must be plainly marked * as such, and must not be misrepresented as * being the original software. * * 3. This notice may not be removed or altered from * any source distribution. */ #include "tinf.h" int uzlib_zlib_parse_header(TINF_DATA *d) { unsigned char cmf, flg; /* -- get header bytes -- */ cmf = uzlib_get_byte(d); flg = uzlib_get_byte(d); /* -- check format -- */ /* check checksum */ if ((256*cmf + flg) % 31) return TINF_DATA_ERROR; /* check method is deflate */ if ((cmf & 0x0f) != 8) return TINF_DATA_ERROR; /* check window size is valid */ if ((cmf >> 4) > 7) return TINF_DATA_ERROR; /* check there is no preset dictionary */ if (flg & 0x20) return TINF_DATA_ERROR; /* initialize for adler32 checksum */ d->checksum_type = TINF_CHKSUM_ADLER; d->checksum = 1; return cmf >> 4; } ================================================ FILE: extmod/uzlib/uzlib.h ================================================ /* * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) * * Copyright (c) 2003 by Joergen Ibsen / Jibz * All Rights Reserved * http://www.ibsensoftware.com/ * * Copyright (c) 2014-2018 by Paul Sokolovsky * * This software is provided 'as-is', without any express * or implied warranty. In no event will the authors be * held liable for any damages arising from the use of * this software. * * Permission is granted to anyone to use this software * for any purpose, including commercial applications, * and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be * misrepresented; you must not claim that you * wrote the original software. If you use this * software in a product, an acknowledgment in * the product documentation would be appreciated * but is not required. * * 2. Altered source versions must be plainly marked * as such, and must not be misrepresented as * being the original software. * * 3. This notice may not be removed or altered from * any source distribution. */ #ifndef UZLIB_H_INCLUDED #define UZLIB_H_INCLUDED #include #include #include #include "defl_static.h" #include "uzlib_conf.h" #if UZLIB_CONF_DEBUG_LOG #include #endif /* calling convention */ #ifndef TINFCC #ifdef __WATCOMC__ #define TINFCC __cdecl #else #define TINFCC #endif #endif #ifdef __cplusplus extern "C" { #endif /* ok status, more data produced */ #define TINF_OK 0 /* end of compressed stream reached */ #define TINF_DONE 1 #define TINF_DATA_ERROR (-3) #define TINF_CHKSUM_ERROR (-4) #define TINF_DICT_ERROR (-5) /* checksum types */ #define TINF_CHKSUM_NONE 0 #define TINF_CHKSUM_ADLER 1 #define TINF_CHKSUM_CRC 2 /* helper macros */ #define TINF_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) /* data structures */ typedef struct { unsigned short table[16]; /* table of code length counts */ unsigned short trans[288]; /* code -> symbol translation table */ } TINF_TREE; struct uzlib_uncomp { /* Pointer to the next byte in the input buffer */ const unsigned char *source; /* Pointer to the next byte past the input buffer (source_limit = source + len) */ const unsigned char *source_limit; /* If source_limit == NULL, or source >= source_limit, this function will be used to read next byte from source stream. The function may also return -1 in case of EOF (or irrecoverable error). Note that besides returning the next byte, it may also update source and source_limit fields, thus allowing for buffered operation. */ int (*source_read_cb)(struct uzlib_uncomp *uncomp); unsigned int tag; unsigned int bitcount; /* Destination (output) buffer start */ unsigned char *dest_start; /* Current pointer in dest buffer */ unsigned char *dest; /* Pointer past the end of the dest buffer, similar to source_limit */ unsigned char *dest_limit; /* Accumulating checksum */ unsigned int checksum; char checksum_type; bool eof; int btype; int bfinal; unsigned int curlen; int lzOff; unsigned char *dict_ring; unsigned int dict_size; unsigned int dict_idx; TINF_TREE ltree; /* dynamic length/symbol tree */ TINF_TREE dtree; /* dynamic distance tree */ }; #include "tinf_compat.h" #define TINF_PUT(d, c) \ { \ *d->dest++ = c; \ if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \ } unsigned char TINFCC uzlib_get_byte(TINF_DATA *d); /* Decompression API */ void TINFCC uzlib_init(void); void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen); int TINFCC uzlib_uncompress(TINF_DATA *d); int TINFCC uzlib_uncompress_chksum(TINF_DATA *d); int TINFCC uzlib_zlib_parse_header(TINF_DATA *d); int TINFCC uzlib_gzip_parse_header(TINF_DATA *d); /* Compression API */ typedef const uint8_t *uzlib_hash_entry_t; struct uzlib_comp { struct Outbuf out; uzlib_hash_entry_t *hash_table; unsigned int hash_bits; unsigned int dict_size; }; void TINFCC uzlib_compress(struct uzlib_comp *c, const uint8_t *src, unsigned slen); /* Checksum API */ /* prev_sum is previous value for incremental computation, 1 initially */ uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); /* crc is previous value for incremental computation, 0xffffffff initially */ uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* UZLIB_H_INCLUDED */ ================================================ FILE: extmod/uzlib/uzlib_conf.h ================================================ /* * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) * * Copyright (c) 2014-2018 by Paul Sokolovsky */ #ifndef UZLIB_CONF_H_INCLUDED #define UZLIB_CONF_H_INCLUDED #ifndef UZLIB_CONF_DEBUG_LOG /* Debug logging level 0, 1, 2, etc. */ #define UZLIB_CONF_DEBUG_LOG 0 #endif #ifndef UZLIB_CONF_PARANOID_CHECKS /* Perform extra checks on the input stream, even if they aren't proven to be strictly required (== lack of them wasn't proven to lead to crashes). */ #define UZLIB_CONF_PARANOID_CHECKS 0 #endif #endif /* UZLIB_CONF_H_INCLUDED */ ================================================ FILE: extmod/vfs.c ================================================ /* * 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 #include "py/runtime.h" #include "py/objstr.h" #include "py/mperrno.h" #include "extmod/vfs.h" #if MICROPY_VFS #if MICROPY_VFS_FAT #include "extmod/vfs_fat.h" #endif #if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 #include "extmod/vfs_lfs.h" #endif #if MICROPY_VFS_POSIX #include "extmod/vfs_posix.h" #endif // For mp_vfs_proxy_call, the maximum number of additional args that can be passed. // A fixed maximum size is used to avoid the need for a costly variable array. #define PROXY_MAX_ARGS (2) // path is the path to lookup and *path_out holds the path within the VFS // object (starts with / if an absolute path). // Returns MP_VFS_ROOT for root dir (and then path_out is undefined) and // MP_VFS_NONE for path not found. mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) { if (*path == '/' || MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) { // an absolute path, or the current volume is root, so search root dir bool is_abs = 0; if (*path == '/') { ++path; is_abs = 1; } if (*path == '\0') { // path is "" or "/" so return virtual root return MP_VFS_ROOT; } for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { size_t len = vfs->len - 1; if (len == 0) { *path_out = path - is_abs; return vfs; } if (strncmp(path, vfs->str + 1, len) == 0) { if (path[len] == '/') { *path_out = path + len; return vfs; } else if (path[len] == '\0') { *path_out = "/"; return vfs; } } } // if we get here then there's nothing mounted on / if (is_abs) { // path began with / and was not found return MP_VFS_NONE; } } // a relative path within a mounted device *path_out = path; return MP_STATE_VM(vfs_cur); } // Version of mp_vfs_lookup_path that takes and returns uPy string objects. STATIC mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) { const char *path = mp_obj_str_get_str(path_in); const char *p_out; mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out); if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) { *path_out = mp_obj_new_str_of_type(mp_obj_get_type(path_in), (const byte*)p_out, strlen(p_out)); } return vfs; } STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) { assert(n_args <= PROXY_MAX_ARGS); if (vfs == MP_VFS_NONE) { // mount point not found mp_raise_OSError(MP_ENODEV); } if (vfs == MP_VFS_ROOT) { // can't do operation on root dir mp_raise_OSError(MP_EPERM); } mp_obj_t meth[2 + PROXY_MAX_ARGS]; mp_load_method(vfs->obj, meth_name, meth); if (args != NULL) { memcpy(meth + 2, args, n_args * sizeof(*args)); } return mp_call_method_n_kw(n_args, 0, meth); } mp_import_stat_t mp_vfs_import_stat(const char *path) { const char *path_out; mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &path_out); if (vfs == MP_VFS_NONE || vfs == MP_VFS_ROOT) { return MP_IMPORT_STAT_NO_EXIST; } // If the mounted object has the VFS protocol, call its import_stat helper const mp_vfs_proto_t *proto = mp_obj_get_type(vfs->obj)->protocol; if (proto != NULL) { return proto->import_stat(MP_OBJ_TO_PTR(vfs->obj), path_out); } // delegate to vfs.stat() method mp_obj_t path_o = mp_obj_new_str(path_out, strlen(path_out)); mp_obj_t stat; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { stat = mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_o); nlr_pop(); } else { // assume an exception means that the path is not found return MP_IMPORT_STAT_NO_EXIST; } mp_obj_t *items; mp_obj_get_array_fixed_n(stat, 10, &items); mp_int_t st_mode = mp_obj_get_int(items[0]); if (st_mode & MP_S_IFDIR) { return MP_IMPORT_STAT_DIR; } else { return MP_IMPORT_STAT_FILE; } } STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { #if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t vfs = MP_OBJ_NULL; mp_vfs_blockdev_t blockdev; mp_vfs_blockdev_init(&blockdev, bdev_obj); uint8_t buf[44]; mp_vfs_blockdev_read_ext(&blockdev, 0, 8, sizeof(buf), buf); #if MICROPY_VFS_LFS1 if (memcmp(&buf[32], "littlefs", 8) == 0) { // LFS1 vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, 0, &bdev_obj); nlr_pop(); return vfs; } #endif #if MICROPY_VFS_LFS2 if (memcmp(&buf[0], "littlefs", 8) == 0) { // LFS2 vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, 0, &bdev_obj); nlr_pop(); return vfs; } #endif nlr_pop(); } else { // Ignore exception (eg block device doesn't support extended readblocks) } #endif #if MICROPY_VFS_FAT return mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, 0, &bdev_obj); #endif return bdev_obj; } mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_readonly, ARG_mkfs }; static const mp_arg_t allowed_args[] = { { MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} }, { MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} }, }; // parse args 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); // get the mount point size_t mnt_len; const char *mnt_str = mp_obj_str_get_data(pos_args[1], &mnt_len); // see if we need to auto-detect and create the filesystem mp_obj_t vfs_obj = pos_args[0]; mp_obj_t dest[2]; mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest); if (dest[0] == MP_OBJ_NULL) { // Input object has no mount method, assume it's a block device and try to // auto-detect the filesystem and create the corresponding VFS entity. vfs_obj = mp_vfs_autodetect(vfs_obj); } // create new object mp_vfs_mount_t *vfs = m_new_obj(mp_vfs_mount_t); vfs->str = mnt_str; vfs->len = mnt_len; vfs->obj = vfs_obj; vfs->next = NULL; // call the underlying object to do any mounting operation mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t*)&args); // check that the destination mount point is unused const char *path_out; mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(mp_obj_str_get_str(pos_args[1]), &path_out); if (existing_mount != MP_VFS_NONE && existing_mount != MP_VFS_ROOT) { if (vfs->len != 1 && existing_mount->len == 1) { // if root dir is mounted, still allow to mount something within a subdir of root } else { // mount point in use mp_raise_OSError(MP_EPERM); } } // insert the vfs into the mount table mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); while (*vfsp != NULL) { if ((*vfsp)->len == 1) { // make sure anything mounted at the root stays at the end of the list vfs->next = *vfsp; break; } vfsp = &(*vfsp)->next; } *vfsp = vfs; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj, 2, mp_vfs_mount); mp_obj_t mp_vfs_umount(mp_obj_t mnt_in) { // remove vfs from the mount table mp_vfs_mount_t *vfs = NULL; size_t mnt_len; const char *mnt_str = NULL; if (mp_obj_is_str(mnt_in)) { mnt_str = mp_obj_str_get_data(mnt_in, &mnt_len); } for (mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); *vfsp != NULL; vfsp = &(*vfsp)->next) { if ((mnt_str != NULL && !memcmp(mnt_str, (*vfsp)->str, mnt_len + 1)) || (*vfsp)->obj == mnt_in) { vfs = *vfsp; *vfsp = (*vfsp)->next; break; } } if (vfs == NULL) { mp_raise_OSError(MP_EINVAL); } // if we unmounted the current device then set current to root if (MP_STATE_VM(vfs_cur) == vfs) { MP_STATE_VM(vfs_cur) = MP_VFS_ROOT; } // call the underlying object to do any unmounting operation mp_vfs_proxy_call(vfs, MP_QSTR_umount, 0, NULL); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_umount_obj, mp_vfs_umount); // Note: buffering and encoding args are currently ignored mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_file, ARG_mode, ARG_encoding }; static const mp_arg_t allowed_args[] = { { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} }, { MP_QSTR_buffering, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); #if MICROPY_VFS_POSIX // If the file is an integer then delegate straight to the POSIX handler if (mp_obj_is_small_int(args[ARG_file].u_obj)) { return mp_vfs_posix_file_open(&mp_type_textio, args[ARG_file].u_obj, args[ARG_mode].u_obj); } #endif mp_vfs_mount_t *vfs = lookup_path(args[ARG_file].u_obj, &args[ARG_file].u_obj); return mp_vfs_proxy_call(vfs, MP_QSTR_open, 2, (mp_obj_t*)&args); } MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_open_obj, 0, mp_vfs_open); mp_obj_t mp_vfs_chdir(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); MP_STATE_VM(vfs_cur) = vfs; if (vfs == MP_VFS_ROOT) { // If we change to the root dir and a VFS is mounted at the root then // we must change that VFS's current dir to the root dir so that any // subsequent relative paths begin at the root of that VFS. for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { if (vfs->len == 1) { mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_); mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &root); break; } } } else { mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &path_out); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj, mp_vfs_chdir); mp_obj_t mp_vfs_getcwd(void) { if (MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) { return MP_OBJ_NEW_QSTR(MP_QSTR__slash_); } mp_obj_t cwd_o = mp_vfs_proxy_call(MP_STATE_VM(vfs_cur), MP_QSTR_getcwd, 0, NULL); if (MP_STATE_VM(vfs_cur)->len == 1) { // don't prepend "/" for vfs mounted at root return cwd_o; } const char *cwd = mp_obj_str_get_str(cwd_o); vstr_t vstr; vstr_init(&vstr, MP_STATE_VM(vfs_cur)->len + strlen(cwd) + 1); vstr_add_strn(&vstr, MP_STATE_VM(vfs_cur)->str, MP_STATE_VM(vfs_cur)->len); if (!(cwd[0] == '/' && cwd[1] == 0)) { vstr_add_str(&vstr, cwd); } return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); } MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd); typedef struct _mp_vfs_ilistdir_it_t { mp_obj_base_t base; mp_fun_1_t iternext; union { mp_vfs_mount_t *vfs; mp_obj_t iter; } cur; bool is_str; bool is_iter; } mp_vfs_ilistdir_it_t; STATIC mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) { mp_vfs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); if (self->is_iter) { // continue delegating to root dir return mp_iternext(self->cur.iter); } else if (self->cur.vfs == NULL) { // finished iterating mount points and no root dir is mounted return MP_OBJ_STOP_ITERATION; } else { // continue iterating mount points mp_vfs_mount_t *vfs = self->cur.vfs; self->cur.vfs = vfs->next; if (vfs->len == 1) { // vfs is mounted at root dir, delegate to it mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_); self->is_iter = true; self->cur.iter = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &root); return mp_iternext(self->cur.iter); } else { // a mounted directory mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); t->items[0] = mp_obj_new_str_of_type( self->is_str ? &mp_type_str : &mp_type_bytes, (const byte*)vfs->str + 1, vfs->len - 1); t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number return MP_OBJ_FROM_PTR(t); } } } mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args) { mp_obj_t path_in; if (n_args == 1) { path_in = args[0]; } else { path_in = MP_OBJ_NEW_QSTR(MP_QSTR_); } mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); if (vfs == MP_VFS_ROOT) { // list the root directory mp_vfs_ilistdir_it_t *iter = m_new_obj(mp_vfs_ilistdir_it_t); iter->base.type = &mp_type_polymorph_iter; iter->iternext = mp_vfs_ilistdir_it_iternext; iter->cur.vfs = MP_STATE_VM(vfs_mount_table); iter->is_str = mp_obj_get_type(path_in) == &mp_type_str; iter->is_iter = false; return MP_OBJ_FROM_PTR(iter); } return mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj, 0, 1, mp_vfs_ilistdir); mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) { mp_obj_t iter = mp_vfs_ilistdir(n_args, args); mp_obj_t dir_list = mp_obj_new_list(0, NULL); mp_obj_t next; while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { mp_obj_list_append(dir_list, mp_obj_subscr(next, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL)); } return dir_list; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir); mp_obj_t mp_vfs_mkdir(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); if (vfs == MP_VFS_ROOT || (vfs != MP_VFS_NONE && !strcmp(mp_obj_str_get_str(path_out), "/"))) { mp_raise_OSError(MP_EEXIST); } return mp_vfs_proxy_call(vfs, MP_QSTR_mkdir, 1, &path_out); } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj, mp_vfs_mkdir); mp_obj_t mp_vfs_remove(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); return mp_vfs_proxy_call(vfs, MP_QSTR_remove, 1, &path_out); } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_remove_obj, mp_vfs_remove); mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) { mp_obj_t args[2]; mp_vfs_mount_t *old_vfs = lookup_path(old_path_in, &args[0]); mp_vfs_mount_t *new_vfs = lookup_path(new_path_in, &args[1]); if (old_vfs != new_vfs) { // can't rename across filesystems mp_raise_OSError(MP_EPERM); } return mp_vfs_proxy_call(old_vfs, MP_QSTR_rename, 2, args); } MP_DEFINE_CONST_FUN_OBJ_2(mp_vfs_rename_obj, mp_vfs_rename); mp_obj_t mp_vfs_rmdir(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); return mp_vfs_proxy_call(vfs, MP_QSTR_rmdir, 1, &path_out); } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj, mp_vfs_rmdir); mp_obj_t mp_vfs_stat(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); if (vfs == MP_VFS_ROOT) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); // st_mode for (int i = 1; i <= 9; ++i) { t->items[i] = MP_OBJ_NEW_SMALL_INT(0); // dev, nlink, uid, gid, size, atime, mtime, ctime } return MP_OBJ_FROM_PTR(t); } return mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_out); } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_stat_obj, mp_vfs_stat); mp_obj_t mp_vfs_statvfs(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); if (vfs == MP_VFS_ROOT) { // statvfs called on the root directory, see if there's anything mounted there for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { if (vfs->len == 1) { break; } } // If there's nothing mounted at root then return a mostly-empty tuple if (vfs == NULL) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); // fill in: bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flags for (int i = 0; i <= 8; ++i) { t->items[i] = MP_OBJ_NEW_SMALL_INT(0); } // Put something sensible in f_namemax t->items[9] = MP_OBJ_NEW_SMALL_INT(MICROPY_ALLOC_PATH_MAX); return MP_OBJ_FROM_PTR(t); } // VFS mounted at root so delegate the call to it path_out = MP_OBJ_NEW_QSTR(MP_QSTR__slash_); } return mp_vfs_proxy_call(vfs, MP_QSTR_statvfs, 1, &path_out); } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj, mp_vfs_statvfs); #endif // MICROPY_VFS ================================================ FILE: extmod/vfs.h ================================================ /* * 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_EXTMOD_VFS_H #define MICROPY_INCLUDED_EXTMOD_VFS_H #include "py/lexer.h" #include "py/obj.h" // return values of mp_vfs_lookup_path // ROOT is 0 so that the default current directory is the root directory #define MP_VFS_NONE ((mp_vfs_mount_t*)1) #define MP_VFS_ROOT ((mp_vfs_mount_t*)0) // MicroPython's port-standardized versions of stat constants #define MP_S_IFDIR (0x4000) #define MP_S_IFREG (0x8000) // these are the values for mp_vfs_blockdev_t.flags #define MP_BLOCKDEV_FLAG_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func #define MP_BLOCKDEV_FLAG_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount #define MP_BLOCKDEV_FLAG_HAVE_IOCTL (0x0004) // new protocol with ioctl #define MP_BLOCKDEV_FLAG_NO_FILESYSTEM (0x0008) // the block device has no filesystem on it // constants for block protocol ioctl #define MP_BLOCKDEV_IOCTL_INIT (1) #define MP_BLOCKDEV_IOCTL_DEINIT (2) #define MP_BLOCKDEV_IOCTL_SYNC (3) #define MP_BLOCKDEV_IOCTL_BLOCK_COUNT (4) #define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5) #define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6) // At the moment the VFS protocol just has import_stat, but could be extended to other methods typedef struct _mp_vfs_proto_t { mp_import_stat_t (*import_stat)(void *self, const char *path); } mp_vfs_proto_t; typedef struct _mp_vfs_blockdev_t { uint16_t flags; size_t block_size; mp_obj_t readblocks[5]; mp_obj_t writeblocks[5]; // new protocol uses just ioctl, old uses sync (optional) and count union { mp_obj_t ioctl[4]; struct { mp_obj_t sync[2]; mp_obj_t count[2]; } old; } u; } mp_vfs_blockdev_t; typedef struct _mp_vfs_mount_t { const char *str; // mount point with leading / size_t len; mp_obj_t obj; struct _mp_vfs_mount_t *next; } mp_vfs_mount_t; void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev); int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, uint8_t *buf); int mp_vfs_blockdev_read_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, uint8_t *buf); int mp_vfs_blockdev_write(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, const uint8_t *buf); int mp_vfs_blockdev_write_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, const uint8_t *buf); mp_obj_t mp_vfs_blockdev_ioctl(mp_vfs_blockdev_t *self, uintptr_t cmd, uintptr_t arg); mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out); mp_import_stat_t mp_vfs_import_stat(const char *path); mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); mp_obj_t mp_vfs_umount(mp_obj_t mnt_in); mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); mp_obj_t mp_vfs_chdir(mp_obj_t path_in); mp_obj_t mp_vfs_getcwd(void); mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args); mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args); mp_obj_t mp_vfs_mkdir(mp_obj_t path_in); mp_obj_t mp_vfs_remove(mp_obj_t path_in); mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in); mp_obj_t mp_vfs_rmdir(mp_obj_t path_in); mp_obj_t mp_vfs_stat(mp_obj_t path_in); mp_obj_t mp_vfs_statvfs(mp_obj_t path_in); MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj); MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_remove_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_vfs_rename_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj); #endif // MICROPY_INCLUDED_EXTMOD_VFS_H ================================================ FILE: extmod/vfs_fat.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2014 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/mpconfig.h" #if MICROPY_VFS_FAT #if !MICROPY_VFS #error "with MICROPY_VFS_FAT enabled, must also enable MICROPY_VFS" #endif #include #include "py/runtime.h" #include "py/mperrno.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs_fat.h" #include "lib/timeutils/timeutils.h" #if FF_MAX_SS == FF_MIN_SS #define SECSIZE(fs) (FF_MIN_SS) #else #define SECSIZE(fs) ((fs)->ssize) #endif #define mp_obj_fat_vfs_t fs_user_mount_t STATIC mp_import_stat_t fat_vfs_import_stat(void *vfs_in, const char *path) { fs_user_mount_t *vfs = vfs_in; FILINFO fno; assert(vfs != NULL); FRESULT res = f_stat(&vfs->fatfs, path, &fno); if (res == FR_OK) { if ((fno.fattrib & AM_DIR) != 0) { return MP_IMPORT_STAT_DIR; } else { return MP_IMPORT_STAT_FILE; } } return MP_IMPORT_STAT_NO_EXIST; } STATIC mp_obj_t fat_vfs_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); // create new object fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t); vfs->base.type = type; vfs->fatfs.drv = vfs; // Initialise underlying block device vfs->blockdev.flags = MP_BLOCKDEV_FLAG_FREE_OBJ; vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE mp_vfs_blockdev_init(&vfs->blockdev, args[0]); // mount the block device so the VFS methods can be used FRESULT res = f_mount(&vfs->fatfs); if (res == FR_NO_FILESYSTEM) { // don't error out if no filesystem, to let mkfs()/mount() create one if wanted vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NO_FILESYSTEM; } else if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } return MP_OBJ_FROM_PTR(vfs); } #if _FS_REENTRANT STATIC mp_obj_t fat_vfs_del(mp_obj_t self_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(self_in); // f_umount only needs to be called to release the sync object f_umount(&self->fatfs); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_del_obj, fat_vfs_del); #endif STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) { // create new object fs_user_mount_t *vfs = MP_OBJ_TO_PTR(fat_vfs_make_new(&mp_fat_vfs_type, 1, 0, &bdev_in)); // make the filesystem uint8_t working_buf[FF_MAX_SS]; FRESULT res = f_mkfs(&vfs->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf)); if (res == FR_MKFS_ABORTED) { // Probably doesn't support FAT16 res = f_mkfs(&vfs->fatfs, FM_FAT32, 0, working_buf, sizeof(working_buf)); } if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_mkfs_fun_obj, fat_vfs_mkfs); STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(fat_vfs_mkfs_obj, MP_ROM_PTR(&fat_vfs_mkfs_fun_obj)); typedef struct _mp_vfs_fat_ilistdir_it_t { mp_obj_base_t base; mp_fun_1_t iternext; bool is_str; FF_DIR dir; } mp_vfs_fat_ilistdir_it_t; STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) { mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); for (;;) { FILINFO fno; FRESULT res = f_readdir(&self->dir, &fno); char *fn = fno.fname; if (res != FR_OK || fn[0] == 0) { // stop on error or end of dir break; } // Note that FatFS already filters . and .., so we don't need to // make 4-tuple with info about this entry mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL)); if (self->is_str) { t->items[0] = mp_obj_new_str(fn, strlen(fn)); } else { t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn)); } if (fno.fattrib & AM_DIR) { // dir t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); } else { // file t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG); } t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number t->items[3] = mp_obj_new_int_from_uint(fno.fsize); return MP_OBJ_FROM_PTR(t); } // ignore error because we may be closing a second time f_closedir(&self->dir); return MP_OBJ_STOP_ITERATION; } STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]); bool is_str_type = true; const char *path; if (n_args == 2) { if (mp_obj_get_type(args[1]) == &mp_type_bytes) { is_str_type = false; } path = mp_obj_str_get_str(args[1]); } else { path = ""; } // Create a new iterator object to list the dir mp_vfs_fat_ilistdir_it_t *iter = m_new_obj(mp_vfs_fat_ilistdir_it_t); iter->base.type = &mp_type_polymorph_iter; iter->iternext = mp_vfs_fat_ilistdir_it_iternext; iter->is_str = is_str_type; FRESULT res = f_opendir(&self->fatfs, &iter->dir, path); if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } return MP_OBJ_FROM_PTR(iter); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_ilistdir_func); STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *path = mp_obj_str_get_str(path_in); FILINFO fno; FRESULT res = f_stat(&self->fatfs, path, &fno); if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } // check if path is a file or directory if ((fno.fattrib & AM_DIR) == attr) { res = f_unlink(&self->fatfs, path); if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } return mp_const_none; } else { mp_raise_OSError(attr ? MP_ENOTDIR : MP_EISDIR); } } STATIC mp_obj_t fat_vfs_remove(mp_obj_t vfs_in, mp_obj_t path_in) { return fat_vfs_remove_internal(vfs_in, path_in, 0); // 0 == file attribute } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_remove_obj, fat_vfs_remove); STATIC mp_obj_t fat_vfs_rmdir(mp_obj_t vfs_in, mp_obj_t path_in) { return fat_vfs_remove_internal(vfs_in, path_in, AM_DIR); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_rmdir_obj, fat_vfs_rmdir); STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_out) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *old_path = mp_obj_str_get_str(path_in); const char *new_path = mp_obj_str_get_str(path_out); FRESULT res = f_rename(&self->fatfs, old_path, new_path); if (res == FR_EXIST) { // if new_path exists then try removing it (but only if it's a file) fat_vfs_remove_internal(vfs_in, path_out, 0); // 0 == file attribute // try to rename again res = f_rename(&self->fatfs, old_path, new_path); } if (res == FR_OK) { return mp_const_none; } else { mp_raise_OSError(fresult_to_errno_table[res]); } } STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_rename_obj, fat_vfs_rename); STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *path = mp_obj_str_get_str(path_o); FRESULT res = f_mkdir(&self->fatfs, path); if (res == FR_OK) { return mp_const_none; } else { mp_raise_OSError(fresult_to_errno_table[res]); } } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_mkdir_obj, fat_vfs_mkdir); /// Change current directory. STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *path; path = mp_obj_str_get_str(path_in); FRESULT res = f_chdir(&self->fatfs, path); if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_chdir_obj, fat_vfs_chdir); /// Get the current directory. STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); char buf[MICROPY_ALLOC_PATH_MAX + 1]; FRESULT res = f_getcwd(&self->fatfs, buf, sizeof(buf)); if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } return mp_obj_new_str(buf, strlen(buf)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd); /// \function stat(path) /// Get the status of a file or directory. STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *path = mp_obj_str_get_str(path_in); FILINFO fno; if (path[0] == 0 || (path[0] == '/' && path[1] == 0)) { // stat root directory fno.fsize = 0; fno.fdate = 0x2821; // Jan 1, 2000 fno.ftime = 0; fno.fattrib = AM_DIR; } else { FRESULT res = f_stat(&self->fatfs, path, &fno); if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); mp_int_t mode = 0; if (fno.fattrib & AM_DIR) { mode |= MP_S_IFDIR; } else { mode |= MP_S_IFREG; } mp_int_t seconds = timeutils_seconds_since_2000( 1980 + ((fno.fdate >> 9) & 0x7f), (fno.fdate >> 5) & 0x0f, fno.fdate & 0x1f, (fno.ftime >> 11) & 0x1f, (fno.ftime >> 5) & 0x3f, 2 * (fno.ftime & 0x1f) ); t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(fno.fsize); // st_size t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // st_ctime return MP_OBJ_FROM_PTR(t); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_stat_obj, fat_vfs_stat); // Get the status of a VFS. STATIC mp_obj_t fat_vfs_statvfs(mp_obj_t vfs_in, mp_obj_t path_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); (void)path_in; DWORD nclst; FATFS *fatfs = &self->fatfs; FRESULT res = f_getfree(fatfs, &nclst); if (FR_OK != res) { mp_raise_OSError(fresult_to_errno_table[res]); } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(fatfs->csize * SECSIZE(fatfs)); // f_bsize t->items[1] = t->items[0]; // f_frsize t->items[2] = MP_OBJ_NEW_SMALL_INT((fatfs->n_fatent - 2)); // f_blocks t->items[3] = MP_OBJ_NEW_SMALL_INT(nclst); // f_bfree t->items[4] = t->items[3]; // f_bavail t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags t->items[9] = MP_OBJ_NEW_SMALL_INT(FF_MAX_LFN); // f_namemax return MP_OBJ_FROM_PTR(t); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_statvfs_obj, fat_vfs_statvfs); STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); // Read-only device indicated by writeblocks[0] == MP_OBJ_NULL. // User can specify read-only device by: // 1. readonly=True keyword argument // 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already) if (mp_obj_is_true(readonly)) { self->blockdev.writeblocks[0] = MP_OBJ_NULL; } // check if we need to make the filesystem FRESULT res = (self->blockdev.flags & MP_BLOCKDEV_FLAG_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK; if (res == FR_NO_FILESYSTEM && mp_obj_is_true(mkfs)) { uint8_t working_buf[FF_MAX_SS]; res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf)); } if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } self->blockdev.flags &= ~MP_BLOCKDEV_FLAG_NO_FILESYSTEM; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_fat_mount_obj, vfs_fat_mount); STATIC mp_obj_t vfs_fat_umount(mp_obj_t self_in) { (void)self_in; // keep the FAT filesystem mounted internally so the VFS methods can still be used return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount); STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { #if _FS_REENTRANT { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&fat_vfs_del_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) }, { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) }, { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&fat_vfs_ilistdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&fat_vfs_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&fat_vfs_rmdir_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&fat_vfs_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&fat_vfs_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&fat_vfs_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&fat_vfs_rename_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&fat_vfs_stat_obj) }, { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&fat_vfs_statvfs_obj) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_fat_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&fat_vfs_umount_obj) }, }; STATIC MP_DEFINE_CONST_DICT(fat_vfs_locals_dict, fat_vfs_locals_dict_table); STATIC const mp_vfs_proto_t fat_vfs_proto = { .import_stat = fat_vfs_import_stat, }; const mp_obj_type_t mp_fat_vfs_type = { { &mp_type_type }, .name = MP_QSTR_VfsFat, .make_new = fat_vfs_make_new, .protocol = &fat_vfs_proto, .locals_dict = (mp_obj_dict_t*)&fat_vfs_locals_dict, }; #endif // MICROPY_VFS_FAT ================================================ FILE: extmod/vfs_fat.h ================================================ /* * 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_EXTMOD_VFS_FAT_H #define MICROPY_INCLUDED_EXTMOD_VFS_FAT_H #include "py/obj.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs.h" typedef struct _fs_user_mount_t { mp_obj_base_t base; mp_vfs_blockdev_t blockdev; FATFS fatfs; } fs_user_mount_t; extern const byte fresult_to_errno_table[20]; extern const mp_obj_type_t mp_fat_vfs_type; extern const mp_obj_type_t mp_type_vfs_fat_fileio; extern const mp_obj_type_t mp_type_vfs_fat_textio; MP_DECLARE_CONST_FUN_OBJ_3(fat_vfs_open_obj); #endif // MICROPY_INCLUDED_EXTMOD_VFS_FAT_H ================================================ FILE: extmod/vfs_fat_diskio.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * Original template for this file comes from: * Low level disk I/O module skeleton for FatFs, (C)ChaN, 2013 * * 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" #if MICROPY_VFS && MICROPY_VFS_FAT #include #include #include "py/mphal.h" #include "py/runtime.h" #include "py/binary.h" #include "py/objarray.h" #include "py/mperrno.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "extmod/vfs_fat.h" typedef void *bdev_t; STATIC fs_user_mount_t *disk_get_device(void *bdev) { return (fs_user_mount_t*)bdev; } /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT disk_read ( bdev_t pdrv, /* Physical drive nmuber (0..) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address (LBA) */ UINT count /* Number of sectors to read (1..128) */ ) { fs_user_mount_t *vfs = disk_get_device(pdrv); if (vfs == NULL) { return RES_PARERR; } int ret = mp_vfs_blockdev_read(&vfs->blockdev, sector, count, buff); return ret == 0 ? RES_OK : RES_ERROR; } /*-----------------------------------------------------------------------*/ /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT disk_write ( bdev_t pdrv, /* Physical drive nmuber (0..) */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address (LBA) */ UINT count /* Number of sectors to write (1..128) */ ) { fs_user_mount_t *vfs = disk_get_device(pdrv); if (vfs == NULL) { return RES_PARERR; } int ret = mp_vfs_blockdev_write(&vfs->blockdev, sector, count, buff); if (ret == -MP_EROFS) { // read-only block device return RES_WRPRT; } return ret == 0 ? RES_OK : RES_ERROR; } /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ DRESULT disk_ioctl ( bdev_t pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { fs_user_mount_t *vfs = disk_get_device(pdrv); if (vfs == NULL) { return RES_PARERR; } // First part: call the relevant method of the underlying block device static const uint8_t op_map[8] = { [CTRL_SYNC] = MP_BLOCKDEV_IOCTL_SYNC, [GET_SECTOR_COUNT] = MP_BLOCKDEV_IOCTL_BLOCK_COUNT, [GET_SECTOR_SIZE] = MP_BLOCKDEV_IOCTL_BLOCK_SIZE, [IOCTL_INIT] = MP_BLOCKDEV_IOCTL_INIT, }; uint8_t bp_op = op_map[cmd & 7]; mp_obj_t ret = mp_const_none; if (bp_op != 0) { ret = mp_vfs_blockdev_ioctl(&vfs->blockdev, bp_op, 0); } // Second part: convert the result for return switch (cmd) { case CTRL_SYNC: return RES_OK; case GET_SECTOR_COUNT: { *((DWORD*)buff) = mp_obj_get_int(ret); return RES_OK; } case GET_SECTOR_SIZE: { if (ret == mp_const_none) { // Default sector size *((WORD*)buff) = 512; } else { *((WORD*)buff) = mp_obj_get_int(ret); } // need to store ssize because we use it in disk_read/disk_write vfs->blockdev.block_size = *((WORD*)buff); return RES_OK; } case GET_BLOCK_SIZE: *((DWORD*)buff) = 1; // erase block size in units of sector size return RES_OK; case IOCTL_INIT: case IOCTL_STATUS: { DSTATUS stat; if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) { // error initialising stat = STA_NOINIT; } else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) { stat = STA_PROTECT; } else { stat = 0; } *((DSTATUS*)buff) = stat; return RES_OK; } default: return RES_PARERR; } } #endif // MICROPY_VFS && MICROPY_VFS_FAT ================================================ FILE: extmod/vfs_fat_file.c ================================================ /* * 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" #if MICROPY_VFS && MICROPY_VFS_FAT #include #include "py/runtime.h" #include "py/stream.h" #include "py/mperrno.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs_fat.h" // this table converts from FRESULT to POSIX errno const byte fresult_to_errno_table[20] = { [FR_OK] = 0, [FR_DISK_ERR] = MP_EIO, [FR_INT_ERR] = MP_EIO, [FR_NOT_READY] = MP_EBUSY, [FR_NO_FILE] = MP_ENOENT, [FR_NO_PATH] = MP_ENOENT, [FR_INVALID_NAME] = MP_EINVAL, [FR_DENIED] = MP_EACCES, [FR_EXIST] = MP_EEXIST, [FR_INVALID_OBJECT] = MP_EINVAL, [FR_WRITE_PROTECTED] = MP_EROFS, [FR_INVALID_DRIVE] = MP_ENODEV, [FR_NOT_ENABLED] = MP_ENODEV, [FR_NO_FILESYSTEM] = MP_ENODEV, [FR_MKFS_ABORTED] = MP_EIO, [FR_TIMEOUT] = MP_EIO, [FR_LOCKED] = MP_EIO, [FR_NOT_ENOUGH_CORE] = MP_ENOMEM, [FR_TOO_MANY_OPEN_FILES] = MP_EMFILE, [FR_INVALID_PARAMETER] = MP_EINVAL, }; typedef struct _pyb_file_obj_t { mp_obj_base_t base; FIL fp; } pyb_file_obj_t; STATIC void file_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_printf(print, "", mp_obj_get_type_str(self_in), MP_OBJ_TO_PTR(self_in)); } STATIC mp_uint_t file_obj_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in); UINT sz_out; FRESULT res = f_read(&self->fp, buf, size, &sz_out); if (res != FR_OK) { *errcode = fresult_to_errno_table[res]; return MP_STREAM_ERROR; } return sz_out; } STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in); UINT sz_out; FRESULT res = f_write(&self->fp, buf, size, &sz_out); if (res != FR_OK) { *errcode = fresult_to_errno_table[res]; return MP_STREAM_ERROR; } if (sz_out != size) { // The FatFS documentation says that this means disk full. *errcode = MP_ENOSPC; return MP_STREAM_ERROR; } return sz_out; } STATIC mp_obj_t file_obj___exit__(size_t n_args, const mp_obj_t *args) { (void)n_args; return mp_stream_close(args[0]); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__); STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { pyb_file_obj_t *self = MP_OBJ_TO_PTR(o_in); if (request == MP_STREAM_SEEK) { struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)(uintptr_t)arg; switch (s->whence) { case 0: // SEEK_SET f_lseek(&self->fp, s->offset); break; case 1: // SEEK_CUR f_lseek(&self->fp, f_tell(&self->fp) + s->offset); break; case 2: // SEEK_END f_lseek(&self->fp, f_size(&self->fp) + s->offset); break; } s->offset = f_tell(&self->fp); return 0; } else if (request == MP_STREAM_FLUSH) { FRESULT res = f_sync(&self->fp); if (res != FR_OK) { *errcode = fresult_to_errno_table[res]; return MP_STREAM_ERROR; } return 0; } else if (request == MP_STREAM_CLOSE) { // if fs==NULL then the file is closed and in that case this method is a no-op if (self->fp.obj.fs != NULL) { FRESULT res = f_close(&self->fp); if (res != FR_OK) { *errcode = fresult_to_errno_table[res]; return MP_STREAM_ERROR; } } return 0; } else { *errcode = MP_EINVAL; return MP_STREAM_ERROR; } } // Note: encoding is ignored for now; it's also not a valid kwarg for CPython's FileIO, // but by adding it here we can use one single mp_arg_t array for open() and FileIO's constructor STATIC const mp_arg_t file_open_args[] = { { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} }, { MP_QSTR_encoding, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} }, }; #define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args) STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_arg_val_t *args) { int mode = 0; const char *mode_s = mp_obj_str_get_str(args[1].u_obj); // TODO make sure only one of r, w, x, a, and b, t are specified while (*mode_s) { switch (*mode_s++) { case 'r': mode |= FA_READ; break; case 'w': mode |= FA_WRITE | FA_CREATE_ALWAYS; break; case 'x': mode |= FA_WRITE | FA_CREATE_NEW; break; case 'a': mode |= FA_WRITE | FA_OPEN_ALWAYS; break; case '+': mode |= FA_READ | FA_WRITE; break; #if MICROPY_PY_IO_FILEIO case 'b': type = &mp_type_vfs_fat_fileio; break; #endif case 't': type = &mp_type_vfs_fat_textio; break; } } pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t); o->base.type = type; const char *fname = mp_obj_str_get_str(args[0].u_obj); assert(vfs != NULL); FRESULT res = f_open(&vfs->fatfs, &o->fp, fname, mode); if (res != FR_OK) { m_del_obj(pyb_file_obj_t, o); mp_raise_OSError(fresult_to_errno_table[res]); } // for 'a' mode, we must begin at the end of the file if ((mode & FA_OPEN_ALWAYS) != 0) { f_lseek(&o->fp, f_size(&o->fp)); } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS]; mp_arg_parse_all_kw_array(n_args, n_kw, args, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals); return file_open(NULL, type, arg_vals); } // TODO gc hook to close the file if not already closed STATIC const mp_rom_map_elem_t vfs_fat_rawfile_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_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_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_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___del__), MP_ROM_PTR(&mp_stream_close_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&file_obj___exit___obj) }, }; STATIC MP_DEFINE_CONST_DICT(vfs_fat_rawfile_locals_dict, vfs_fat_rawfile_locals_dict_table); #if MICROPY_PY_IO_FILEIO STATIC const mp_stream_p_t vfs_fat_fileio_stream_p = { .read = file_obj_read, .write = file_obj_write, .ioctl = file_obj_ioctl, }; const mp_obj_type_t mp_type_vfs_fat_fileio = { { &mp_type_type }, .name = MP_QSTR_FileIO, .print = file_obj_print, .make_new = file_obj_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &vfs_fat_fileio_stream_p, .locals_dict = (mp_obj_dict_t*)&vfs_fat_rawfile_locals_dict, }; #endif STATIC const mp_stream_p_t vfs_fat_textio_stream_p = { .read = file_obj_read, .write = file_obj_write, .ioctl = file_obj_ioctl, .is_text = true, }; const mp_obj_type_t mp_type_vfs_fat_textio = { { &mp_type_type }, .name = MP_QSTR_TextIOWrapper, .print = file_obj_print, .make_new = file_obj_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &vfs_fat_textio_stream_p, .locals_dict = (mp_obj_dict_t*)&vfs_fat_rawfile_locals_dict, }; // Factory function for I/O stream classes STATIC mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) { // TODO: analyze buffering args and instantiate appropriate type fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS]; arg_vals[0].u_obj = path; arg_vals[1].u_obj = mode; arg_vals[2].u_obj = mp_const_none; return file_open(self, &mp_type_vfs_fat_textio, arg_vals); } MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self); #endif // MICROPY_VFS && MICROPY_VFS_FAT ================================================ FILE: extmod/vfs_posix.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017-2018 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" #include "py/mperrno.h" #include "extmod/vfs.h" #include "extmod/vfs_posix.h" #if MICROPY_VFS_POSIX #include #include #include #include typedef struct _mp_obj_vfs_posix_t { mp_obj_base_t base; vstr_t root; size_t root_len; bool readonly; } mp_obj_vfs_posix_t; STATIC const char *vfs_posix_get_path_str(mp_obj_vfs_posix_t *self, mp_obj_t path) { if (self->root_len == 0) { return mp_obj_str_get_str(path); } else { self->root.len = self->root_len; vstr_add_str(&self->root, mp_obj_str_get_str(path)); return vstr_null_terminated_str(&self->root); } } STATIC mp_obj_t vfs_posix_get_path_obj(mp_obj_vfs_posix_t *self, mp_obj_t path) { if (self->root_len == 0) { return path; } else { self->root.len = self->root_len; vstr_add_str(&self->root, mp_obj_str_get_str(path)); return mp_obj_new_str(self->root.buf, self->root.len); } } STATIC mp_obj_t vfs_posix_fun1_helper(mp_obj_t self_in, mp_obj_t path_in, int (*f)(const char*)) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); int ret = f(vfs_posix_get_path_str(self, path_in)); if (ret != 0) { mp_raise_OSError(errno); } return mp_const_none; } STATIC mp_import_stat_t mp_vfs_posix_import_stat(void *self_in, const char *path) { mp_obj_vfs_posix_t *self = self_in; if (self->root_len != 0) { self->root.len = self->root_len; vstr_add_str(&self->root, path); path = vstr_null_terminated_str(&self->root); } struct stat st; if (stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) { return MP_IMPORT_STAT_DIR; } else if (S_ISREG(st.st_mode)) { return MP_IMPORT_STAT_FILE; } } return MP_IMPORT_STAT_NO_EXIST; } STATIC mp_obj_t vfs_posix_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); mp_obj_vfs_posix_t *vfs = m_new_obj(mp_obj_vfs_posix_t); vfs->base.type = type; vstr_init(&vfs->root, 0); if (n_args == 1) { vstr_add_str(&vfs->root, mp_obj_str_get_str(args[0])); vstr_add_char(&vfs->root, '/'); } vfs->root_len = vfs->root.len; vfs->readonly = false; return MP_OBJ_FROM_PTR(vfs); } STATIC mp_obj_t vfs_posix_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); if (mp_obj_is_true(readonly)) { self->readonly = true; } if (mp_obj_is_true(mkfs)) { mp_raise_OSError(MP_EPERM); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_mount_obj, vfs_posix_mount); STATIC mp_obj_t vfs_posix_umount(mp_obj_t self_in) { (void)self_in; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_umount_obj, vfs_posix_umount); STATIC mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *mode = mp_obj_str_get_str(mode_in); if (self->readonly && (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) { mp_raise_OSError(MP_EROFS); } if (!mp_obj_is_small_int(path_in)) { path_in = vfs_posix_get_path_obj(self, path_in); } return mp_vfs_posix_file_open(&mp_type_textio, path_in, mode_in); } STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_open_obj, vfs_posix_open); STATIC mp_obj_t vfs_posix_chdir(mp_obj_t self_in, mp_obj_t path_in) { return vfs_posix_fun1_helper(self_in, path_in, chdir); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_chdir_obj, vfs_posix_chdir); STATIC mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); char buf[MICROPY_ALLOC_PATH_MAX + 1]; const char *ret = getcwd(buf, sizeof(buf)); if (ret == NULL) { mp_raise_OSError(errno); } ret += self->root_len; return mp_obj_new_str(ret, strlen(ret)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd); typedef struct _vfs_posix_ilistdir_it_t { mp_obj_base_t base; mp_fun_1_t iternext; bool is_str; DIR *dir; } vfs_posix_ilistdir_it_t; STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) { vfs_posix_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); if (self->dir == NULL) { return MP_OBJ_STOP_ITERATION; } for (;;) { struct dirent *dirent = readdir(self->dir); if (dirent == NULL) { closedir(self->dir); self->dir = NULL; return MP_OBJ_STOP_ITERATION; } const char *fn = dirent->d_name; if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) { // skip . and .. continue; } // make 3-tuple with info about this entry mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); if (self->is_str) { t->items[0] = mp_obj_new_str(fn, strlen(fn)); } else { t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn)); } #ifdef _DIRENT_HAVE_D_TYPE #ifdef DTTOIF t->items[1] = MP_OBJ_NEW_SMALL_INT(DTTOIF(dirent->d_type)); #else if (dirent->d_type == DT_DIR) { t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); } else if (dirent->d_type == DT_REG) { t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG); } else { t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type); } #endif #else // DT_UNKNOWN should have 0 value on any reasonable system t->items[1] = MP_OBJ_NEW_SMALL_INT(0); #endif #ifdef _DIRENT_HAVE_D_INO t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino); #else t->items[2] = MP_OBJ_NEW_SMALL_INT(0); #endif return MP_OBJ_FROM_PTR(t); } } STATIC mp_obj_t vfs_posix_ilistdir(mp_obj_t self_in, mp_obj_t path_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); vfs_posix_ilistdir_it_t *iter = m_new_obj(vfs_posix_ilistdir_it_t); iter->base.type = &mp_type_polymorph_iter; iter->iternext = vfs_posix_ilistdir_it_iternext; iter->is_str = mp_obj_get_type(path_in) == &mp_type_str; const char *path = vfs_posix_get_path_str(self, path_in); if (path[0] == '\0') { path = "."; } iter->dir = opendir(path); if (iter->dir == NULL) { mp_raise_OSError(errno); } return MP_OBJ_FROM_PTR(iter); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_ilistdir_obj, vfs_posix_ilistdir); typedef struct _mp_obj_listdir_t { mp_obj_base_t base; mp_fun_1_t iternext; DIR *dir; } mp_obj_listdir_t; STATIC mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); int ret = mkdir(vfs_posix_get_path_str(self, path_in), 0777); if (ret != 0) { mp_raise_OSError(errno); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_mkdir_obj, vfs_posix_mkdir); STATIC mp_obj_t vfs_posix_remove(mp_obj_t self_in, mp_obj_t path_in) { return vfs_posix_fun1_helper(self_in, path_in, unlink); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_remove_obj, vfs_posix_remove); STATIC mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *old_path = vfs_posix_get_path_str(self, old_path_in); const char *new_path = vfs_posix_get_path_str(self, new_path_in); int ret = rename(old_path, new_path); if (ret != 0) { mp_raise_OSError(errno); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_rename_obj, vfs_posix_rename); STATIC mp_obj_t vfs_posix_rmdir(mp_obj_t self_in, mp_obj_t path_in) { return vfs_posix_fun1_helper(self_in, path_in, rmdir); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir); STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); struct stat sb; int ret = stat(vfs_posix_get_path_str(self, path_in), &sb); if (ret != 0) { mp_raise_OSError(errno); } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode); t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino); t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.st_dev); t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.st_nlink); t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.st_uid); t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.st_gid); t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.st_size); t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.st_atime); t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.st_mtime); t->items[9] = MP_OBJ_NEW_SMALL_INT(sb.st_ctime); return MP_OBJ_FROM_PTR(t); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat); #ifdef __ANDROID__ #define USE_STATFS 1 #endif #if USE_STATFS #include #define STRUCT_STATVFS struct statfs #define STATVFS statfs #define F_FAVAIL sb.f_ffree #define F_NAMEMAX sb.f_namelen #define F_FLAG sb.f_flags #else #include #define STRUCT_STATVFS struct statvfs #define STATVFS statvfs #define F_FAVAIL sb.f_favail #define F_NAMEMAX sb.f_namemax #define F_FLAG sb.f_flag #endif STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); STRUCT_STATVFS sb; const char *path = vfs_posix_get_path_str(self, path_in); int ret = STATVFS(path, &sb); if (ret != 0) { mp_raise_OSError(errno); } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize); t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize); t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks); t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree); t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail); t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files); t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree); t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL); t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG); t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX); return MP_OBJ_FROM_PTR(t); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_statvfs_obj, vfs_posix_statvfs); STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_posix_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_posix_umount_obj) }, { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_posix_open_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_posix_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_posix_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_posix_ilistdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&vfs_posix_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&vfs_posix_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_posix_rename_obj) }, { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_posix_rmdir_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_posix_stat_obj) }, { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_posix_statvfs_obj) }, }; STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table); STATIC const mp_vfs_proto_t vfs_posix_proto = { .import_stat = mp_vfs_posix_import_stat, }; const mp_obj_type_t mp_type_vfs_posix = { { &mp_type_type }, .name = MP_QSTR_VfsPosix, .make_new = vfs_posix_make_new, .protocol = &vfs_posix_proto, .locals_dict = (mp_obj_dict_t*)&vfs_posix_locals_dict, }; #endif // MICROPY_VFS_POSIX ================================================ FILE: extmod/vfs_posix.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 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_EXTMOD_VFS_POSIX_H #define MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H #include "py/lexer.h" #include "py/obj.h" extern const mp_obj_type_t mp_type_vfs_posix; extern const mp_obj_type_t mp_type_vfs_posix_fileio; extern const mp_obj_type_t mp_type_vfs_posix_textio; mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in); #endif // MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H ================================================ FILE: extmod/vfs_posix_file.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2013-2018 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" #include "py/stream.h" #include "extmod/vfs_posix.h" #if MICROPY_VFS_POSIX #include #ifdef _WIN32 #define fsync _commit #endif typedef struct _mp_obj_vfs_posix_file_t { mp_obj_base_t base; int fd; } mp_obj_vfs_posix_file_t; #ifdef MICROPY_CPYTHON_COMPAT STATIC void check_fd_is_open(const mp_obj_vfs_posix_file_t *o) { if (o->fd < 0) { mp_raise_msg(&mp_type_ValueError, "I/O operation on closed file"); } } #else #define check_fd_is_open(o) #endif STATIC void vfs_posix_file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", mp_obj_get_type_str(self_in), self->fd); } mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in) { mp_obj_vfs_posix_file_t *o = m_new_obj(mp_obj_vfs_posix_file_t); const char *mode_s = mp_obj_str_get_str(mode_in); int mode_rw = 0, mode_x = 0; while (*mode_s) { switch (*mode_s++) { case 'r': mode_rw = O_RDONLY; break; case 'w': mode_rw = O_WRONLY; mode_x = O_CREAT | O_TRUNC; break; case 'a': mode_rw = O_WRONLY; mode_x = O_CREAT | O_APPEND; break; case '+': mode_rw = O_RDWR; break; #if MICROPY_PY_IO_FILEIO // If we don't have io.FileIO, then files are in text mode implicitly case 'b': type = &mp_type_vfs_posix_fileio; break; case 't': type = &mp_type_vfs_posix_textio; break; #endif } } o->base.type = type; mp_obj_t fid = file_in; if (mp_obj_is_small_int(fid)) { o->fd = MP_OBJ_SMALL_INT_VALUE(fid); return MP_OBJ_FROM_PTR(o); } const char *fname = mp_obj_str_get_str(fid); int fd = open(fname, mode_x | mode_rw, 0644); if (fd == -1) { mp_raise_OSError(errno); } o->fd = fd; return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t vfs_posix_file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} }, }; mp_arg_val_t arg_vals[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, arg_vals); return mp_vfs_posix_file_open(type, arg_vals[0].u_obj, arg_vals[1].u_obj); } STATIC mp_obj_t vfs_posix_file_fileno(mp_obj_t self_in) { mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in); check_fd_is_open(self); return MP_OBJ_NEW_SMALL_INT(self->fd); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_file_fileno_obj, vfs_posix_file_fileno); STATIC mp_obj_t vfs_posix_file___exit__(size_t n_args, const mp_obj_t *args) { (void)n_args; return mp_stream_close(args[0]); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(vfs_posix_file___exit___obj, 4, 4, vfs_posix_file___exit__); STATIC mp_uint_t vfs_posix_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); mp_int_t r = read(o->fd, buf, size); if (r == -1) { *errcode = errno; return MP_STREAM_ERROR; } return r; } STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); #if MICROPY_PY_OS_DUPTERM if (o->fd <= STDERR_FILENO) { mp_hal_stdout_tx_strn(buf, size); return size; } #endif mp_int_t r = write(o->fd, buf, size); while (r == -1 && errno == EINTR) { if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { mp_obj_t obj = MP_STATE_VM(mp_pending_exception); MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; nlr_raise(obj); } r = write(o->fd, buf, size); } if (r == -1) { *errcode = errno; return MP_STREAM_ERROR; } return r; } STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); switch (request) { case MP_STREAM_FLUSH: if (fsync(o->fd) < 0) { *errcode = errno; return MP_STREAM_ERROR; } return 0; case MP_STREAM_SEEK: { struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)arg; off_t off = lseek(o->fd, s->offset, s->whence); if (off == (off_t)-1) { *errcode = errno; return MP_STREAM_ERROR; } s->offset = off; return 0; } case MP_STREAM_CLOSE: close(o->fd); #ifdef MICROPY_CPYTHON_COMPAT o->fd = -1; #endif return 0; default: *errcode = EINVAL; return MP_STREAM_ERROR; } } STATIC const mp_rom_map_elem_t vfs_posix_rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&vfs_posix_file_fileno_obj) }, { 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_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_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___enter__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&vfs_posix_file___exit___obj) }, }; STATIC MP_DEFINE_CONST_DICT(vfs_posix_rawfile_locals_dict, vfs_posix_rawfile_locals_dict_table); #if MICROPY_PY_IO_FILEIO STATIC const mp_stream_p_t vfs_posix_fileio_stream_p = { .read = vfs_posix_file_read, .write = vfs_posix_file_write, .ioctl = vfs_posix_file_ioctl, }; const mp_obj_type_t mp_type_vfs_posix_fileio = { { &mp_type_type }, .name = MP_QSTR_FileIO, .print = vfs_posix_file_print, .make_new = vfs_posix_file_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &vfs_posix_fileio_stream_p, .locals_dict = (mp_obj_dict_t*)&vfs_posix_rawfile_locals_dict, }; #endif STATIC const mp_stream_p_t vfs_posix_textio_stream_p = { .read = vfs_posix_file_read, .write = vfs_posix_file_write, .ioctl = vfs_posix_file_ioctl, .is_text = true, }; const mp_obj_type_t mp_type_vfs_posix_textio = { { &mp_type_type }, .name = MP_QSTR_TextIOWrapper, .print = vfs_posix_file_print, .make_new = vfs_posix_file_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &vfs_posix_textio_stream_p, .locals_dict = (mp_obj_dict_t*)&vfs_posix_rawfile_locals_dict, }; const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_textio}, STDIN_FILENO}; const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_textio}, STDOUT_FILENO}; const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_textio}, STDERR_FILENO}; #endif // MICROPY_VFS_POSIX ================================================ FILE: extmod/vfs_reader.c ================================================ /* * 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 "py/runtime.h" #include "py/stream.h" #include "py/reader.h" #include "extmod/vfs.h" #if MICROPY_READER_VFS typedef struct _mp_reader_vfs_t { mp_obj_t file; uint16_t len; uint16_t pos; byte buf[24]; } mp_reader_vfs_t; STATIC mp_uint_t mp_reader_vfs_readbyte(void *data) { mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data; if (reader->pos >= reader->len) { if (reader->len < sizeof(reader->buf)) { return MP_READER_EOF; } else { int errcode; reader->len = mp_stream_rw(reader->file, reader->buf, sizeof(reader->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); if (errcode != 0) { // TODO handle errors properly return MP_READER_EOF; } if (reader->len == 0) { return MP_READER_EOF; } reader->pos = 0; } } return reader->buf[reader->pos++]; } STATIC void mp_reader_vfs_close(void *data) { mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data; mp_stream_close(reader->file); m_del_obj(mp_reader_vfs_t, reader); } void mp_reader_new_file(mp_reader_t *reader, const char *filename) { mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t); mp_obj_t arg = mp_obj_new_str(filename, strlen(filename)); rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map); int errcode; rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); if (errcode != 0) { mp_raise_OSError(errcode); } rf->pos = 0; reader->data = rf; reader->readbyte = mp_reader_vfs_readbyte; reader->close = mp_reader_vfs_close; } #endif // MICROPY_READER_VFS ================================================ FILE: extmod/virtpin.c ================================================ /* * 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 "extmod/virtpin.h" int mp_virtual_pin_read(mp_obj_t pin) { mp_obj_base_t* s = (mp_obj_base_t*)MP_OBJ_TO_PTR(pin); mp_pin_p_t *pin_p = (mp_pin_p_t*)s->type->protocol; return pin_p->ioctl(pin, MP_PIN_READ, 0, NULL); } void mp_virtual_pin_write(mp_obj_t pin, int value) { mp_obj_base_t* s = (mp_obj_base_t*)MP_OBJ_TO_PTR(pin); mp_pin_p_t *pin_p = (mp_pin_p_t*)s->type->protocol; pin_p->ioctl(pin, MP_PIN_WRITE, value, NULL); } ================================================ FILE: extmod/virtpin.h ================================================ /* * 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_EXTMOD_VIRTPIN_H #define MICROPY_INCLUDED_EXTMOD_VIRTPIN_H #include "py/obj.h" #define MP_PIN_READ (1) #define MP_PIN_WRITE (2) #define MP_PIN_INPUT (3) #define MP_PIN_OUTPUT (4) // Pin protocol typedef struct _mp_pin_p_t { mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); } mp_pin_p_t; int mp_virtual_pin_read(mp_obj_t pin); void mp_virtual_pin_write(mp_obj_t pin, int value); // If a port exposes a Pin object, it's constructor should be like this mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); #endif // MICROPY_INCLUDED_EXTMOD_VIRTPIN_H ================================================ FILE: extmod/webrepl/manifest.py ================================================ freeze('.', ('webrepl.py', 'webrepl_setup.py', 'websocket_helper.py',)) ================================================ FILE: extmod/webrepl/webrepl.py ================================================ # This module should be imported from REPL, not run from command line. import socket import uos import network import uwebsocket import websocket_helper import _webrepl listen_s = None client_s = None def setup_conn(port, accept_handler): global listen_s listen_s = socket.socket() listen_s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ai = socket.getaddrinfo("0.0.0.0", port) addr = ai[0][4] listen_s.bind(addr) listen_s.listen(1) if accept_handler: listen_s.setsockopt(socket.SOL_SOCKET, 20, accept_handler) for i in (network.AP_IF, network.STA_IF): iface = network.WLAN(i) if iface.active(): print("WebREPL daemon started on ws://%s:%d" % (iface.ifconfig()[0], port)) return listen_s def accept_conn(listen_sock): global client_s cl, remote_addr = listen_sock.accept() prev = uos.dupterm(None) uos.dupterm(prev) if prev: print("\nConcurrent WebREPL connection from", remote_addr, "rejected") cl.close() return print("\nWebREPL connection from:", remote_addr) client_s = cl websocket_helper.server_handshake(cl) ws = uwebsocket.websocket(cl, True) ws = _webrepl._webrepl(ws) cl.setblocking(False) # notify REPL on socket incoming data (ESP32/ESP8266-only) if hasattr(uos, 'dupterm_notify'): cl.setsockopt(socket.SOL_SOCKET, 20, uos.dupterm_notify) uos.dupterm(ws) def stop(): global listen_s, client_s uos.dupterm(None) if client_s: client_s.close() if listen_s: listen_s.close() def start(port=8266, password=None): stop() if password is None: try: import webrepl_cfg _webrepl.password(webrepl_cfg.PASS) setup_conn(port, accept_conn) print("Started webrepl in normal mode") except: print("WebREPL is not configured, run 'import webrepl_setup'") else: _webrepl.password(password) setup_conn(port, accept_conn) print("Started webrepl in manual override mode") def start_foreground(port=8266): stop() s = setup_conn(port, None) accept_conn(s) ================================================ FILE: extmod/webrepl/webrepl_setup.py ================================================ import sys #import uos as os import os import machine RC = "./boot.py" CONFIG = "./webrepl_cfg.py" def input_choice(prompt, choices): while 1: resp = input(prompt) if resp in choices: return resp def getpass(prompt): return input(prompt) def input_pass(): while 1: passwd1 = getpass("New password (4-9 chars): ") if len(passwd1) < 4 or len(passwd1) > 9: print("Invalid password length") continue passwd2 = getpass("Confirm password: ") if passwd1 == passwd2: return passwd1 print("Passwords do not match") def exists(fname): try: with open(fname): pass return True except OSError: return False def get_daemon_status(): with open(RC) as f: for l in f: if "webrepl" in l: if l.startswith("#"): return False return True return None def change_daemon(action): LINES = ("import webrepl", "webrepl.start()") with open(RC) as old_f, open(RC + ".tmp", "w") as new_f: found = False for l in old_f: for patt in LINES: if patt in l: found = True if action and l.startswith("#"): l = l[1:] elif not action and not l.startswith("#"): l = "#" + l new_f.write(l) if not found: new_f.write("import webrepl\nwebrepl.start()\n") # FatFs rename() is not POSIX compliant, will raise OSError if # dest file exists. os.remove(RC) os.rename(RC + ".tmp", RC) def main(): status = get_daemon_status() print("WebREPL daemon auto-start status:", "enabled" if status else "disabled") print("\nWould you like to (E)nable or (D)isable it running on boot?") print("(Empty line to quit)") resp = input("> ").upper() if resp == "E": if exists(CONFIG): resp2 = input_choice("Would you like to change WebREPL password? (y/n) ", ("y", "n", "")) else: print("To enable WebREPL, you must set password for it") resp2 = "y" if resp2 == "y": passwd = input_pass() with open(CONFIG, "w") as f: f.write("PASS = %r\n" % passwd) if resp not in ("D", "E") or (resp == "D" and not status) or (resp == "E" and status): print("No further action required") sys.exit() change_daemon(resp == "E") print("Changes will be activated after reboot") resp = input_choice("Would you like to reboot now? (y/n) ", ("y", "n", "")) if resp == "y": machine.reset() main() ================================================ FILE: extmod/webrepl/websocket_helper.py ================================================ import sys try: import ubinascii as binascii except: import binascii try: import uhashlib as hashlib except: import hashlib DEBUG = 0 def server_handshake(sock): clr = sock.makefile("rwb", 0) l = clr.readline() #sys.stdout.write(repr(l)) webkey = None while 1: l = clr.readline() if not l: raise OSError("EOF in headers") if l == b"\r\n": break # sys.stdout.write(l) h, v = [x.strip() for x in l.split(b":", 1)] if DEBUG: print((h, v)) if h == b'Sec-WebSocket-Key': webkey = v if not webkey: raise OSError("Not a websocket request") if DEBUG: print("Sec-WebSocket-Key:", webkey, len(webkey)) d = hashlib.sha1(webkey) d.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11") respkey = d.digest() respkey = binascii.b2a_base64(respkey)[:-1] if DEBUG: print("respkey:", respkey) sock.send(b"""\ HTTP/1.1 101 Switching Protocols\r Upgrade: websocket\r Connection: Upgrade\r Sec-WebSocket-Accept: """) sock.send(respkey) sock.send("\r\n\r\n") # Very simplified client handshake, works for MicroPython's # websocket server implementation, but probably not for other # servers. def client_handshake(sock): cl = sock.makefile("rwb", 0) cl.write(b"""\ GET / HTTP/1.1\r Host: echo.websocket.org\r Connection: Upgrade\r Upgrade: websocket\r Sec-WebSocket-Key: foo\r \r """) l = cl.readline() # print(l) while 1: l = cl.readline() if l == b"\r\n": break # sys.stdout.write(l) ================================================ FILE: lib/mp-readline/readline.c ================================================ /* * 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/repl.h" #include "py/mphal.h" #include "lib/mp-readline/readline.h" #if 0 // print debugging info #define DEBUG_PRINT (1) #define DEBUG_printf printf #else // don't print debugging info #define DEBUG_printf(...) (void)0 #endif #define READLINE_HIST_SIZE (MP_ARRAY_SIZE(MP_STATE_PORT(readline_hist))) enum { ESEQ_NONE, ESEQ_ESC, ESEQ_ESC_BRACKET, ESEQ_ESC_BRACKET_DIGIT, ESEQ_ESC_O }; void readline_init0(void) { memset(MP_STATE_PORT(readline_hist), 0, READLINE_HIST_SIZE * sizeof(const char*)); } STATIC char *str_dup_maybe(const char *str) { uint32_t len = strlen(str); char *s2 = m_new_maybe(char, len + 1); if (s2 == NULL) { return NULL; } memcpy(s2, str, len + 1); return s2; } // By default assume terminal which implements VT100 commands... #ifndef MICROPY_HAL_HAS_VT100 #define MICROPY_HAL_HAS_VT100 (1) #endif // ...and provide the implementation using them #if MICROPY_HAL_HAS_VT100 STATIC void mp_hal_move_cursor_back(uint pos) { if (pos <= 4) { // fast path for most common case of 1 step back mp_hal_stdout_tx_strn("\b\b\b\b", pos); } else { char vt100_command[6]; // snprintf needs space for the terminating null character int n = snprintf(&vt100_command[0], sizeof(vt100_command), "\x1b[%u", pos); if (n > 0) { vt100_command[n] = 'D'; // replace null char mp_hal_stdout_tx_strn(vt100_command, n + 1); } } } STATIC void mp_hal_erase_line_from_cursor(uint n_chars_to_erase) { (void)n_chars_to_erase; mp_hal_stdout_tx_strn("\x1b[K", 3); } #endif typedef struct _readline_t { vstr_t *line; size_t orig_line_len; int escape_seq; int hist_cur; size_t cursor_pos; char escape_seq_buf[1]; const char *prompt; } readline_t; STATIC readline_t rl; int readline_process_char(int c) { size_t last_line_len = rl.line->len; int redraw_step_back = 0; bool redraw_from_cursor = false; int redraw_step_forward = 0; if (rl.escape_seq == ESEQ_NONE) { if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_E && vstr_len(rl.line) == rl.orig_line_len) { // control character with empty line return c; } else if (c == CHAR_CTRL_A) { // CTRL-A with non-empty line is go-to-start-of-line goto home_key; #if MICROPY_REPL_EMACS_KEYS } else if (c == CHAR_CTRL_B) { // CTRL-B with non-empty line is go-back-one-char goto left_arrow_key; #endif } else if (c == CHAR_CTRL_C) { // CTRL-C with non-empty line is cancel return c; #if MICROPY_REPL_EMACS_KEYS } else if (c == CHAR_CTRL_D) { // CTRL-D with non-empty line is delete-at-cursor goto delete_key; #endif } else if (c == CHAR_CTRL_E) { // CTRL-E is go-to-end-of-line goto end_key; #if MICROPY_REPL_EMACS_KEYS } else if (c == CHAR_CTRL_F) { // CTRL-F with non-empty line is go-forward-one-char goto right_arrow_key; } else if (c == CHAR_CTRL_K) { // CTRL-K is kill from cursor to end-of-line, inclusive vstr_cut_tail_bytes(rl.line, last_line_len - rl.cursor_pos); // set redraw parameters redraw_from_cursor = true; } else if (c == CHAR_CTRL_N) { // CTRL-N is go to next line in history goto down_arrow_key; } else if (c == CHAR_CTRL_P) { // CTRL-P is go to previous line in history goto up_arrow_key; } else if (c == CHAR_CTRL_U) { // CTRL-U is kill from beginning-of-line up to cursor vstr_cut_out_bytes(rl.line, rl.orig_line_len, rl.cursor_pos - rl.orig_line_len); // set redraw parameters redraw_step_back = rl.cursor_pos - rl.orig_line_len; redraw_from_cursor = true; #endif } else if (c == '\r') { // newline mp_hal_stdout_tx_str("\r\n"); readline_push_history(vstr_null_terminated_str(rl.line) + rl.orig_line_len); return 0; } else if (c == 27) { // escape sequence rl.escape_seq = ESEQ_ESC; } else if (c == 8 || c == 127) { // backspace/delete if (rl.cursor_pos > rl.orig_line_len) { // work out how many chars to backspace #if MICROPY_REPL_AUTO_INDENT int nspace = 0; for (size_t i = rl.orig_line_len; i < rl.cursor_pos; i++) { if (rl.line->buf[i] != ' ') { nspace = 0; break; } nspace += 1; } if (nspace < 4) { nspace = 1; } else { nspace = 4; } #else int nspace = 1; #endif // do the backspace vstr_cut_out_bytes(rl.line, rl.cursor_pos - nspace, nspace); // set redraw parameters redraw_step_back = nspace; redraw_from_cursor = true; } #if MICROPY_HELPER_REPL } else if (c == 9) { // tab magic const char *compl_str; size_t compl_len = mp_repl_autocomplete(rl.line->buf + rl.orig_line_len, rl.cursor_pos - rl.orig_line_len, &mp_plat_print, &compl_str); if (compl_len == 0) { // no match } else if (compl_len == (size_t)(-1)) { // many matches mp_hal_stdout_tx_str(rl.prompt); mp_hal_stdout_tx_strn(rl.line->buf + rl.orig_line_len, rl.cursor_pos - rl.orig_line_len); redraw_from_cursor = true; } else { // one match for (size_t i = 0; i < compl_len; ++i) { vstr_ins_byte(rl.line, rl.cursor_pos + i, *compl_str++); } // set redraw parameters redraw_from_cursor = true; redraw_step_forward = compl_len; } #endif } else if (32 <= c && c <= 126) { // printable character vstr_ins_char(rl.line, rl.cursor_pos, c); // set redraw parameters redraw_from_cursor = true; redraw_step_forward = 1; } } else if (rl.escape_seq == ESEQ_ESC) { switch (c) { case '[': rl.escape_seq = ESEQ_ESC_BRACKET; break; case 'O': rl.escape_seq = ESEQ_ESC_O; break; default: DEBUG_printf("(ESC %d)", c); rl.escape_seq = ESEQ_NONE; } } else if (rl.escape_seq == ESEQ_ESC_BRACKET) { if ('0' <= c && c <= '9') { rl.escape_seq = ESEQ_ESC_BRACKET_DIGIT; rl.escape_seq_buf[0] = c; } else { rl.escape_seq = ESEQ_NONE; if (c == 'A') { #if MICROPY_REPL_EMACS_KEYS up_arrow_key: #endif // up arrow if (rl.hist_cur + 1 < (int)READLINE_HIST_SIZE && MP_STATE_PORT(readline_hist)[rl.hist_cur + 1] != NULL) { // increase hist num rl.hist_cur += 1; // set line to history rl.line->len = rl.orig_line_len; vstr_add_str(rl.line, MP_STATE_PORT(readline_hist)[rl.hist_cur]); // set redraw parameters redraw_step_back = rl.cursor_pos - rl.orig_line_len; redraw_from_cursor = true; redraw_step_forward = rl.line->len - rl.orig_line_len; } } else if (c == 'B') { #if MICROPY_REPL_EMACS_KEYS down_arrow_key: #endif // down arrow if (rl.hist_cur >= 0) { // decrease hist num rl.hist_cur -= 1; // set line to history vstr_cut_tail_bytes(rl.line, rl.line->len - rl.orig_line_len); if (rl.hist_cur >= 0) { vstr_add_str(rl.line, MP_STATE_PORT(readline_hist)[rl.hist_cur]); } // set redraw parameters redraw_step_back = rl.cursor_pos - rl.orig_line_len; redraw_from_cursor = true; redraw_step_forward = rl.line->len - rl.orig_line_len; } } else if (c == 'C') { #if MICROPY_REPL_EMACS_KEYS right_arrow_key: #endif // right arrow if (rl.cursor_pos < rl.line->len) { redraw_step_forward = 1; } } else if (c == 'D') { #if MICROPY_REPL_EMACS_KEYS left_arrow_key: #endif // left arrow if (rl.cursor_pos > rl.orig_line_len) { redraw_step_back = 1; } } else if (c == 'H') { // home goto home_key; } else if (c == 'F') { // end goto end_key; } else { DEBUG_printf("(ESC [ %d)", c); } } } else if (rl.escape_seq == ESEQ_ESC_BRACKET_DIGIT) { if (c == '~') { if (rl.escape_seq_buf[0] == '1' || rl.escape_seq_buf[0] == '7') { home_key: redraw_step_back = rl.cursor_pos - rl.orig_line_len; } else if (rl.escape_seq_buf[0] == '4' || rl.escape_seq_buf[0] == '8') { end_key: redraw_step_forward = rl.line->len - rl.cursor_pos; } else if (rl.escape_seq_buf[0] == '3') { // delete #if MICROPY_REPL_EMACS_KEYS delete_key: #endif if (rl.cursor_pos < rl.line->len) { vstr_cut_out_bytes(rl.line, rl.cursor_pos, 1); redraw_from_cursor = true; } } else { DEBUG_printf("(ESC [ %c %d)", rl.escape_seq_buf[0], c); } } else { DEBUG_printf("(ESC [ %c %d)", rl.escape_seq_buf[0], c); } rl.escape_seq = ESEQ_NONE; } else if (rl.escape_seq == ESEQ_ESC_O) { switch (c) { case 'H': goto home_key; case 'F': goto end_key; default: DEBUG_printf("(ESC O %d)", c); rl.escape_seq = ESEQ_NONE; } } else { rl.escape_seq = ESEQ_NONE; } // redraw command prompt, efficiently if (redraw_step_back > 0) { mp_hal_move_cursor_back(redraw_step_back); rl.cursor_pos -= redraw_step_back; } if (redraw_from_cursor) { if (rl.line->len < last_line_len) { // erase old chars mp_hal_erase_line_from_cursor(last_line_len - rl.cursor_pos); } // draw new chars mp_hal_stdout_tx_strn(rl.line->buf + rl.cursor_pos, rl.line->len - rl.cursor_pos); // move cursor forward if needed (already moved forward by length of line, so move it back) mp_hal_move_cursor_back(rl.line->len - (rl.cursor_pos + redraw_step_forward)); rl.cursor_pos += redraw_step_forward; } else if (redraw_step_forward > 0) { // draw over old chars to move cursor forwards mp_hal_stdout_tx_strn(rl.line->buf + rl.cursor_pos, redraw_step_forward); rl.cursor_pos += redraw_step_forward; } return -1; } #if MICROPY_REPL_AUTO_INDENT STATIC void readline_auto_indent(void) { vstr_t *line = rl.line; if (line->len > 1 && line->buf[line->len - 1] == '\n') { int i; for (i = line->len - 1; i > 0; i--) { if (line->buf[i - 1] == '\n') { break; } } size_t j; for (j = i; j < line->len; j++) { if (line->buf[j] != ' ') { break; } } // i=start of line; j=first non-space if (i > 0 && j + 1 == line->len) { // previous line is not first line and is all spaces for (size_t k = i - 1; k > 0; --k) { if (line->buf[k - 1] == '\n') { // don't auto-indent if last 2 lines are all spaces return; } else if (line->buf[k - 1] != ' ') { // 2nd previous line is not all spaces break; } } } int n = (j - i) / 4; if (line->buf[line->len - 2] == ':') { n += 1; } while (n-- > 0) { vstr_add_strn(line, " ", 4); mp_hal_stdout_tx_strn(" ", 4); rl.cursor_pos += 4; } } } #endif void readline_note_newline(const char *prompt) { rl.orig_line_len = rl.line->len; rl.cursor_pos = rl.orig_line_len; rl.prompt = prompt; mp_hal_stdout_tx_str(prompt); #if MICROPY_REPL_AUTO_INDENT readline_auto_indent(); #endif } void readline_init(vstr_t *line, const char *prompt) { rl.line = line; rl.orig_line_len = line->len; rl.escape_seq = ESEQ_NONE; rl.escape_seq_buf[0] = 0; rl.hist_cur = -1; rl.cursor_pos = rl.orig_line_len; rl.prompt = prompt; mp_hal_stdout_tx_str(prompt); #if MICROPY_REPL_AUTO_INDENT readline_auto_indent(); #endif } int readline(vstr_t *line, const char *prompt) { readline_init(line, prompt); for (;;) { int c = mp_hal_stdin_rx_chr(); int r = readline_process_char(c); if (r >= 0) { return r; } } } void readline_push_history(const char *line) { if (line[0] != '\0' && (MP_STATE_PORT(readline_hist)[0] == NULL || strcmp(MP_STATE_PORT(readline_hist)[0], line) != 0)) { // a line which is not empty and different from the last one // so update the history char *most_recent_hist = str_dup_maybe(line); if (most_recent_hist != NULL) { for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { MP_STATE_PORT(readline_hist)[i] = MP_STATE_PORT(readline_hist)[i - 1]; } MP_STATE_PORT(readline_hist)[0] = most_recent_hist; } } } ================================================ FILE: lib/mp-readline/readline.h ================================================ /* * 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_LIB_MP_READLINE_READLINE_H #define MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H #define CHAR_CTRL_A (1) #define CHAR_CTRL_B (2) #define CHAR_CTRL_C (3) #define CHAR_CTRL_D (4) #define CHAR_CTRL_E (5) #define CHAR_CTRL_F (6) #define CHAR_CTRL_K (11) #define CHAR_CTRL_N (14) #define CHAR_CTRL_P (16) #define CHAR_CTRL_U (21) void readline_init0(void); int readline(vstr_t *line, const char *prompt); void readline_push_history(const char *line); void readline_init(vstr_t *line, const char *prompt); void readline_note_newline(const char *prompt); int readline_process_char(int c); #endif // MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H ================================================ FILE: lib/netutils/netutils.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2015 Daniel Campora * * 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 "lib/netutils/netutils.h" // Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian) { char ip_str[16]; mp_uint_t ip_len; if (endian == NETUTILS_LITTLE) { ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[3], ip[2], ip[1], ip[0]); } else { ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); } return mp_obj_new_str(ip_str, ip_len); } // Takes an array with a raw IP address, and a port, and returns a net-address // tuple such as ('192.168.0.1', 8080). mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian) { mp_obj_t tuple[2] = { tuple[0] = netutils_format_ipv4_addr(ip, endian), tuple[1] = mp_obj_new_int(port), }; return mp_obj_new_tuple(2, tuple); } void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { size_t addr_len; const char *addr_str = mp_obj_str_get_data(addr_in, &addr_len); if (addr_len == 0) { // special case of no address given memset(out_ip, 0, NETUTILS_IPV4ADDR_BUFSIZE); return; } const char *s = addr_str; const char *s_top = addr_str + addr_len; for (mp_uint_t i = 3 ; ; i--) { mp_uint_t val = 0; for (; s < s_top && *s != '.'; s++) { val = val * 10 + *s - '0'; } if (endian == NETUTILS_LITTLE) { out_ip[i] = val; } else { out_ip[NETUTILS_IPV4ADDR_BUFSIZE - 1 - i] = val; } if (i == 0 && s == s_top) { return; } else if (i > 0 && s < s_top && *s == '.') { s++; } else { mp_raise_ValueError("invalid arguments"); } } } // Takes an address of the form ('192.168.0.1', 8080), returns the port and // puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { mp_obj_t *addr_items; mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); netutils_parse_ipv4_addr(addr_items[0], out_ip, endian); return mp_obj_get_int(addr_items[1]); } ================================================ FILE: lib/netutils/netutils.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2015 Daniel Campora * * 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_NETUTILS_NETUTILS_H #define MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H #define NETUTILS_IPV4ADDR_BUFSIZE 4 #define NETUTILS_TRACE_IS_TX (0x0001) #define NETUTILS_TRACE_PAYLOAD (0x0002) #define NETUTILS_TRACE_NEWLINE (0x0004) typedef enum _netutils_endian_t { NETUTILS_LITTLE, NETUTILS_BIG, } netutils_endian_t; // Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian); // Takes an array with a raw IP address, and a port, and returns a net-address // tuple such as ('192.168.0.1', 8080). mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian); void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); // Takes an address of the form ('192.168.0.1', 8080), returns the port and // puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags); #endif // MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H ================================================ FILE: lib/oofatfs/diskio.h ================================================ /* This file is part of ooFatFs, a customised version of FatFs * See https://github.com/micropython/oofatfs for details */ /*-----------------------------------------------------------------------/ / Low level disk interface modlue include file (C)ChaN, 2014 / /-----------------------------------------------------------------------*/ #ifndef _DISKIO_DEFINED #define _DISKIO_DEFINED #ifdef __cplusplus extern "C" { #endif /* Status of Disk Functions */ typedef BYTE DSTATUS; /* Results of Disk Functions */ typedef enum { RES_OK = 0, /* 0: Successful */ RES_ERROR, /* 1: R/W Error */ RES_WRPRT, /* 2: Write Protected */ RES_NOTRDY, /* 3: Not Ready */ RES_PARERR /* 4: Invalid Parameter */ } DRESULT; /*---------------------------------------*/ /* Prototypes for disk control functions */ DRESULT disk_read (void *drv, BYTE* buff, DWORD sector, UINT count); DRESULT disk_write (void *drv, const BYTE* buff, DWORD sector, UINT count); DRESULT disk_ioctl (void *drv, BYTE cmd, void* buff); /* Disk Status Bits (DSTATUS) */ #define STA_NOINIT 0x01 /* Drive not initialized */ #define STA_NODISK 0x02 /* No medium in the drive */ #define STA_PROTECT 0x04 /* Write protected */ /* Command code for disk_ioctrl fucntion */ /* Generic command (Used by FatFs) */ #define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ #define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ #define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ #define IOCTL_INIT 5 #define IOCTL_STATUS 6 /* Generic command (Not used by FatFs) */ #define CTRL_POWER 5 /* Get/Set power status */ #define CTRL_LOCK 6 /* Lock/Unlock media removal */ #define CTRL_EJECT 7 /* Eject media */ #define CTRL_FORMAT 8 /* Create physical format on the media */ /* MMC/SDC specific ioctl command */ #define MMC_GET_TYPE 10 /* Get card type */ #define MMC_GET_CSD 11 /* Get CSD */ #define MMC_GET_CID 12 /* Get CID */ #define MMC_GET_OCR 13 /* Get OCR */ #define MMC_GET_SDSTAT 14 /* Get SD status */ #define ISDIO_READ 55 /* Read data form SD iSDIO register */ #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ /* ATA/CF specific ioctl command */ #define ATA_GET_REV 20 /* Get F/W revision */ #define ATA_GET_MODEL 21 /* Get model name */ #define ATA_GET_SN 22 /* Get serial number */ #ifdef __cplusplus } #endif #endif ================================================ FILE: lib/oofatfs/ff.c ================================================ /* This file is part of ooFatFs, a customised version of FatFs * See https://github.com/micropython/oofatfs for details */ /*----------------------------------------------------------------------------/ / FatFs - Generic FAT file system module R0.12b / /-----------------------------------------------------------------------------/ / / Copyright (C) 2016, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided / that the following condition is met: / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. / / This software is provided by the copyright holder and contributors "AS IS" / and any warranties related to this software are DISCLAIMED. / The copyright owner or contributors be NOT LIABLE for any damages caused / by use of this software. /----------------------------------------------------------------------------*/ #include #include "ff.h" /* Declarations of FatFs API */ #include "diskio.h" /* Declarations of device I/O functions */ // DIR has been renamed FF_DIR in the public API so it doesn't clash with POSIX #define DIR FF_DIR /*-------------------------------------------------------------------------- Module Private Definitions ---------------------------------------------------------------------------*/ #if _FATFS != 68020 /* Revision ID */ #error Wrong include file (ff.h). #endif #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } /* Reentrancy related */ #if _FS_REENTRANT #if _USE_LFN == 1 #error Static LFN work area cannot be used at thread-safe configuration #endif #define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } #else #define ENTER_FF(fs) #define LEAVE_FF(fs, res) return res #endif /* Definitions of sector size */ #if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) #error Wrong sector size configuration #endif #if _MAX_SS == _MIN_SS #define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ #else #define SS(fs) ((fs)->ssize) /* Variable sector size */ #endif /* Timestamp */ #if _FS_NORTC == 1 #if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 #error Invalid _FS_NORTC settings #endif #define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) #else #define GET_FATTIME() get_fattime() #endif /* File lock controls */ #if _FS_LOCK != 0 #if _FS_READONLY #error _FS_LOCK must be 0 at read-only configuration #endif typedef struct { FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ DWORD clu; /* Object ID 2, directory (0:root) */ DWORD ofs; /* Object ID 3, directory offset */ WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ } FILESEM; #endif /* DBCS code ranges and SBCS upper conversion tables */ #if _CODE_PAGE == 932 /* Japanese Shift-JIS */ #define _DF1S 0x81 /* DBC 1st byte range 1 start */ #define _DF1E 0x9F /* DBC 1st byte range 1 end */ #define _DF2S 0xE0 /* DBC 1st byte range 2 start */ #define _DF2E 0xFC /* DBC 1st byte range 2 end */ #define _DS1S 0x40 /* DBC 2nd byte range 1 start */ #define _DS1E 0x7E /* DBC 2nd byte range 1 end */ #define _DS2S 0x80 /* DBC 2nd byte range 2 start */ #define _DS2E 0xFC /* DBC 2nd byte range 2 end */ #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ #define _DF1S 0x81 #define _DF1E 0xFE #define _DS1S 0x40 #define _DS1E 0x7E #define _DS2S 0x80 #define _DS2E 0xFE #elif _CODE_PAGE == 949 /* Korean */ #define _DF1S 0x81 #define _DF1E 0xFE #define _DS1S 0x41 #define _DS1E 0x5A #define _DS2S 0x61 #define _DS2E 0x7A #define _DS3S 0x81 #define _DS3E 0xFE #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ #define _DF1S 0x81 #define _DF1E 0xFE #define _DS1S 0x40 #define _DS1E 0x7E #define _DS2S 0xA1 #define _DS2E 0xFE #elif _CODE_PAGE == 437 /* U.S. */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 720 /* Arabic */ #define _DF1S 0 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 737 /* Greek */ #define _DF1S 0 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 771 /* KBL */ #define _DF1S 0 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} #elif _CODE_PAGE == 775 /* Baltic */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \ 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 850 /* Latin 1 */ #define _DF1S 0 #define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 852 /* Latin 2 */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \ 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \ 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} #elif _CODE_PAGE == 855 /* Cyrillic */ #define _DF1S 0 #define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \ 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \ 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 857 /* Turkish */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \ 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 860 /* Portuguese */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 861 /* Icelandic */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 862 /* Hebrew */ #define _DF1S 0 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 863 /* Canadian-French */ #define _DF1S 0 #define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 864 /* Arabic */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 865 /* Nordic */ #define _DF1S 0 #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 866 /* Russian */ #define _DF1S 0 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} #elif _CODE_PAGE == 869 /* Greek 2 */ #define _DF1S 0 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \ 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ #if _USE_LFN != 0 #error Cannot enable LFN without valid code page. #endif #define _DF1S 0 #else #error Unknown code page #endif /* Character code support macros */ #define IsUpper(c) (((c)>='A')&&((c)<='Z')) #define IsLower(c) (((c)>='a')&&((c)<='z')) #define IsDigit(c) (((c)>='0')&&((c)<='9')) #if _DF1S != 0 /* Code page is DBCS */ #ifdef _DF2S /* Two 1st byte areas */ #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) #else /* One 1st byte area */ #define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) #endif #ifdef _DS3S /* Three 2nd byte areas */ #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) #else /* Two 2nd byte areas */ #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) #endif #else /* Code page is SBCS */ #define IsDBCS1(c) 0 #define IsDBCS2(c) 0 #endif /* _DF1S */ /* File attribute bits (internal use) */ #define AM_VOL 0x08 /* Volume label */ #define AM_LFN 0x0F /* LFN entry */ #define AM_MASK 0x3F /* Mask of defined bits */ /* File access control and file status flags (internal use) */ #define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ #define FA_MODIFIED 0x40 /* File has been modified */ #define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ /* Name status flags */ #define NSFLAG 11 /* Index of name status byte in fn[] */ #define NS_LOSS 0x01 /* Out of 8.3 format */ #define NS_LFN 0x02 /* Force to create LFN entry */ #define NS_LAST 0x04 /* Last segment */ #define NS_BODY 0x08 /* Lower case flag (body) */ #define NS_EXT 0x10 /* Lower case flag (ext) */ #define NS_DOT 0x20 /* Dot entry */ #define NS_NOLFN 0x40 /* Do not find LFN */ #define NS_NONAME 0x80 /* Not followed */ /* Limits and boundaries (differ from specs but correct for real DOS/Windows) */ #define MAX_FAT12 0xFF5 /* Maximum number of FAT12 clusters */ #define MAX_FAT16 0xFFF5 /* Maximum number of FAT16 clusters */ #define MAX_FAT32 0xFFFFFF5 /* Maximum number of FAT32 clusters */ #define MAX_EXFAT 0x7FFFFFFD /* Maximum number of exFAT clusters (limited by implementation) */ #define MAX_DIR 0x200000 /* Maximum size of FAT directory */ #define MAX_DIR_EX 0x10000000 /* Maximum size of exFAT directory */ /* FatFs refers the members in the FAT structures as byte array instead of / structure members because the structure is not binary compatible between / different platforms */ #define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ #define BS_OEMName 3 /* OEM name (8-byte) */ #define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ #define BPB_NumFATs 16 /* Number of FATs (BYTE) */ #define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */ #define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ #define BPB_Media 21 /* Media descriptor byte (BYTE) */ #define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ #define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */ #define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ #define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ #define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ #define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ #define BS_NTres 37 /* Error flag (BYTE) */ #define BS_BootSig 38 /* Extended boot signature (BYTE) */ #define BS_VolID 39 /* Volume serial number (DWORD) */ #define BS_VolLab 43 /* Volume label string (8-byte) */ #define BS_FilSysType 54 /* File system type string (8-byte) */ #define BS_BootCode 62 /* Boot code (448-byte) */ #define BS_55AA 510 /* Signature word (WORD) */ #define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ #define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ #define BPB_FSVer32 42 /* FAT32: File system version (WORD) */ #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ #define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ #define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ #define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ #define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ #define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ #define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ #define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ #define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */ #define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ #define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ #define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ #define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ #define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ #define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ #define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ #define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ #define BPB_RootClusEx 96 /* exFAT: Root directory cluster (DWORD) */ #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ #define BPB_FSVerEx 104 /* exFAT: File system version (WORD) */ #define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */ #define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */ #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in byte (BYTE) */ #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in sector (BYTE) */ #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ #define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ #define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ #define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ #define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ #define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ #define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ #define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ #define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ #define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ #define SZ_PTE 16 /* MBR: Size of a partition table entry */ #define PTE_Boot 0 /* MBR PTE: Boot indicator */ #define PTE_StHead 1 /* MBR PTE: Start head */ #define PTE_StSec 2 /* MBR PTE: Start sector */ #define PTE_StCyl 3 /* MBR PTE: Start cylinder */ #define PTE_System 4 /* MBR PTE: System ID */ #define PTE_EdHead 5 /* MBR PTE: End head */ #define PTE_EdSec 6 /* MBR PTE: End sector */ #define PTE_EdCyl 7 /* MBR PTE: End cylinder */ #define PTE_StLba 8 /* MBR PTE: Start in LBA */ #define PTE_SizLba 12 /* MBR PTE: Size in LBA */ #define DIR_Name 0 /* Short file name (11-byte) */ #define DIR_Attr 11 /* Attribute (BYTE) */ #define DIR_NTres 12 /* Lower case flag (BYTE) */ #define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ #define DIR_CrtTime 14 /* Created time (DWORD) */ #define DIR_LstAccDate 18 /* Last accessed date (WORD) */ #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ #define DIR_ModTime 22 /* Modified time (DWORD) */ #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ #define DIR_FileSize 28 /* File size (DWORD) */ #define LDIR_Ord 0 /* LFN entry order and LLE flag (BYTE) */ #define LDIR_Attr 11 /* LFN attribute (BYTE) */ #define LDIR_Type 12 /* LFN type (BYTE) */ #define LDIR_Chksum 13 /* Checksum of the SFN entry (BYTE) */ #define LDIR_FstClusLO 26 /* Must be zero (WORD) */ #define XDIR_Type 0 /* Type of exFAT directory entry (BYTE) */ #define XDIR_NumLabel 1 /* Number of volume label characters (BYTE) */ #define XDIR_Label 2 /* Volume label (11-WORD) */ #define XDIR_CaseSum 4 /* Sum of case conversion table (DWORD) */ #define XDIR_NumSec 1 /* Number of secondary entries (BYTE) */ #define XDIR_SetSum 2 /* Sum of the set of directory entries (WORD) */ #define XDIR_Attr 4 /* File attribute (WORD) */ #define XDIR_CrtTime 8 /* Created time (DWORD) */ #define XDIR_ModTime 12 /* Modified time (DWORD) */ #define XDIR_AccTime 16 /* Last accessed time (DWORD) */ #define XDIR_CrtTime10 20 /* Created time subsecond (BYTE) */ #define XDIR_ModTime10 21 /* Modified time subsecond (BYTE) */ #define XDIR_CrtTZ 22 /* Created timezone (BYTE) */ #define XDIR_ModTZ 23 /* Modified timezone (BYTE) */ #define XDIR_AccTZ 24 /* Last accessed timezone (BYTE) */ #define XDIR_GenFlags 33 /* Gneral secondary flags (WORD) */ #define XDIR_NumName 35 /* Number of file name characters (BYTE) */ #define XDIR_NameHash 36 /* Hash of file name (WORD) */ #define XDIR_ValidFileSize 40 /* Valid file size (QWORD) */ #define XDIR_FstClus 52 /* First cluster of the file data (DWORD) */ #define XDIR_FileSize 56 /* File/Directory size (QWORD) */ #define SZDIRE 32 /* Size of a directory entry */ #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ #define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ #define RDDEM 0x05 /* Replacement of the character collides with DDEM */ /*-------------------------------------------------------------------------- Module Private Work Area ---------------------------------------------------------------------------*/ /* Remark: Variables here without initial value shall be guaranteed zero/null / at start-up. If not, either the linker or start-up routine being used is / not compliance with C standard. */ #if _VOLUMES < 1 || _VOLUMES > 9 #error Wrong _VOLUMES setting #endif static WORD Fsid; /* File system mount ID */ #if _FS_LOCK != 0 static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ #endif #if _USE_LFN == 0 /* Non-LFN configuration */ #define DEF_NAMBUF #define INIT_NAMBUF(fs) #define FREE_NAMBUF() #else #if _MAX_LFN < 12 || _MAX_LFN > 255 #error Wrong _MAX_LFN setting #endif #if _USE_LFN == 1 /* LFN enabled with static working buffer */ #if _FS_EXFAT static BYTE DirBuf[SZDIRE*19]; /* Directory entry block scratchpad buffer (19 entries in size) */ #endif static WCHAR LfnBuf[_MAX_LFN+1]; /* LFN enabled with static working buffer */ #define DEF_NAMBUF #define INIT_NAMBUF(fs) #define FREE_NAMBUF() #elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ #if _FS_EXFAT #define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[SZDIRE*19]; #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } #define FREE_NAMBUF() #else #define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } #define FREE_NAMBUF() #endif #elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ #if _FS_EXFAT #define DEF_NAMBUF WCHAR *lfn; #define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2 + SZDIRE*19); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } #define FREE_NAMBUF() ff_memfree(lfn) #else #define DEF_NAMBUF WCHAR *lfn; #define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } #define FREE_NAMBUF() ff_memfree(lfn) #endif #else #error Wrong _USE_LFN setting #endif #endif #ifdef _EXCVT static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended characters */ #endif /*-------------------------------------------------------------------------- Module Private Functions ---------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /* Load/Store multi-byte word in the FAT structure */ /*-----------------------------------------------------------------------*/ static WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ { WORD rv; rv = ptr[1]; rv = rv << 8 | ptr[0]; return rv; } static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ { DWORD rv; rv = ptr[3]; rv = rv << 8 | ptr[2]; rv = rv << 8 | ptr[1]; rv = rv << 8 | ptr[0]; return rv; } #if _FS_EXFAT static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ { QWORD rv; rv = ptr[7]; rv = rv << 8 | ptr[6]; rv = rv << 8 | ptr[5]; rv = rv << 8 | ptr[4]; rv = rv << 8 | ptr[3]; rv = rv << 8 | ptr[2]; rv = rv << 8 | ptr[1]; rv = rv << 8 | ptr[0]; return rv; } #endif #if !_FS_READONLY static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ { *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; } static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ { *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; } #if _FS_EXFAT static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ { *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; } #endif #endif /* !_FS_READONLY */ /*-----------------------------------------------------------------------*/ /* String functions */ /*-----------------------------------------------------------------------*/ // These were originally provided by the FatFs library but we use externally // provided versions from C stdlib to (hopefully) reduce code size and use // more efficient versions. #define mem_cpy memcpy #define mem_set memset #define mem_cmp memcmp /* Check if chr is contained in the string */ static int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */ while (*str && *str != chr) str++; return *str; } #if _FS_REENTRANT /*-----------------------------------------------------------------------*/ /* Request/Release grant to access the volume */ /*-----------------------------------------------------------------------*/ static int lock_fs ( FATFS* fs /* File system object */ ) { return ff_req_grant(fs->sobj); } static void unlock_fs ( FATFS* fs, /* File system object */ FRESULT res /* Result code to be returned */ ) { if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { ff_rel_grant(fs->sobj); } } #endif #if _FS_LOCK != 0 /*-----------------------------------------------------------------------*/ /* File lock control functions */ /*-----------------------------------------------------------------------*/ static FRESULT chk_lock ( /* Check if the file can be accessed */ DIR* dp, /* Directory object pointing the file to be checked */ int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ ) { UINT i, be; /* Search file semaphore table */ for (i = be = 0; i < _FS_LOCK; i++) { if (Files[i].fs) { /* Existing entry */ if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */ Files[i].clu == dp->obj.sclust && Files[i].ofs == dp->dptr) break; } else { /* Blank entry */ be = 1; } } if (i == _FS_LOCK) { /* The object is not opened */ return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ } /* The object has been opened. Reject any open against writing file and all write mode open */ return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; } static int enq_lock (void) /* Check if an entry is available for a new object */ { UINT i; for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; return (i == _FS_LOCK) ? 0 : 1; } static UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ DIR* dp, /* Directory object pointing the file to register or increment */ int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ ) { UINT i; for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ if (Files[i].fs == dp->obj.fs && Files[i].clu == dp->obj.sclust && Files[i].ofs == dp->dptr) break; } if (i == _FS_LOCK) { /* Not opened. Register it as new. */ for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ Files[i].fs = dp->obj.fs; Files[i].clu = dp->obj.sclust; Files[i].ofs = dp->dptr; Files[i].ctr = 0; } if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ return i + 1; } static FRESULT dec_lock ( /* Decrement object open counter */ UINT i /* Semaphore index (1..) */ ) { WORD n; FRESULT res; if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ n = Files[i].ctr; if (n == 0x100) n = 0; /* If write mode open, delete the entry */ if (n > 0) n--; /* Decrement read mode open count */ Files[i].ctr = n; if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ res = FR_OK; } else { res = FR_INT_ERR; /* Invalid index nunber */ } return res; } static void clear_lock ( /* Clear lock entries of the volume */ FATFS *fs ) { UINT i; for (i = 0; i < _FS_LOCK; i++) { if (Files[i].fs == fs) Files[i].fs = 0; } } #endif /* _FS_LOCK != 0 */ /*-----------------------------------------------------------------------*/ /* Move/Flush disk access window in the file system object */ /*-----------------------------------------------------------------------*/ #if !_FS_READONLY static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERROR */ FATFS* fs /* File system object */ ) { DWORD wsect; UINT nf; FRESULT res = FR_OK; if (fs->wflag) { /* Write back the sector if it is dirty */ wsect = fs->winsect; /* Current sector number */ if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { res = FR_DISK_ERR; } else { fs->wflag = 0; if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ wsect += fs->fsize; disk_write(fs->drv, fs->win, wsect, 1); } } } } return res; } #endif static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */ FATFS* fs, /* File system object */ DWORD sector /* Sector number to make appearance in the fs->win[] */ ) { FRESULT res = FR_OK; if (sector != fs->winsect) { /* Window offset changed? */ #if !_FS_READONLY res = sync_window(fs); /* Write-back changes */ #endif if (res == FR_OK) { /* Fill sector window with new data */ if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ res = FR_DISK_ERR; } fs->winsect = sector; } } return res; } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Synchronize file system and strage device */ /*-----------------------------------------------------------------------*/ static FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ FATFS* fs /* File system object */ ) { FRESULT res; res = sync_window(fs); if (res == FR_OK) { /* Update FSInfo sector if needed */ if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* Create FSInfo structure */ mem_set(fs->win, 0, SS(fs)); st_word(fs->win + BS_55AA, 0xAA55); st_dword(fs->win + FSI_LeadSig, 0x41615252); st_dword(fs->win + FSI_StrucSig, 0x61417272); st_dword(fs->win + FSI_Free_Count, fs->free_clst); st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Write it into the FSInfo sector */ fs->winsect = fs->volbase + 1; disk_write(fs->drv, fs->win, fs->winsect, 1); fs->fsi_flag = 0; } /* Make sure that no pending write process in the physical drive */ if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; } return res; } #endif /*-----------------------------------------------------------------------*/ /* Get sector# from cluster# */ /*-----------------------------------------------------------------------*/ static DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ FATFS* fs, /* File system object */ DWORD clst /* Cluster# to be converted */ ) { clst -= 2; if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */ return clst * fs->csize + fs->database; } /*-----------------------------------------------------------------------*/ /* FAT access - Read value of a FAT entry */ /*-----------------------------------------------------------------------*/ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ _FDID* obj, /* Corresponding object */ DWORD clst /* Cluster number to get the value */ ) { UINT wc, bc; DWORD val; FATFS *fs = obj->fs; if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ val = 1; /* Internal error */ } else { val = 0xFFFFFFFF; /* Default value falls on disk error */ switch (fs->fs_type) { case FS_FAT12 : bc = (UINT)clst; bc += bc / 2; if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; wc = fs->win[bc++ % SS(fs)]; if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; wc |= fs->win[bc % SS(fs)] << 8; val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); break; case FS_FAT16 : if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; val = ld_word(fs->win + clst * 2 % SS(fs)); break; case FS_FAT32 : if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; break; #if _FS_EXFAT case FS_EXFAT : if (obj->objsize) { DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ if (obj->stat == 2) { /* Is there no valid chain on the FAT? */ if (cofs <= clen) { val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* Generate the value */ break; } } if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the contiguous part? */ val = clst + 1; /* Generate the value */ break; } if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; break; } } /* go next */ #endif default: val = 1; /* Internal error */ } } return val; } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* FAT access - Change value of a FAT entry */ /*-----------------------------------------------------------------------*/ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ FATFS* fs, /* Corresponding file system object */ DWORD clst, /* FAT index number (cluster number) to be changed */ DWORD val /* New value to be set to the entry */ ) { UINT bc; BYTE *p; FRESULT res = FR_INT_ERR; if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ switch (fs->fs_type) { case FS_FAT12 : /* Bitfield items */ bc = (UINT)clst; bc += bc / 2; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; p = fs->win + bc++ % SS(fs); *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; fs->wflag = 1; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; p = fs->win + bc % SS(fs); *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); fs->wflag = 1; break; case FS_FAT16 : /* WORD aligned items */ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); if (res != FR_OK) break; st_word(fs->win + clst * 2 % SS(fs), (WORD)val); fs->wflag = 1; break; case FS_FAT32 : /* DWORD aligned items */ #if _FS_EXFAT case FS_EXFAT : #endif res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); if (res != FR_OK) break; if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); } st_dword(fs->win + clst * 4 % SS(fs), val); fs->wflag = 1; break; } } return res; } #endif /* !_FS_READONLY */ #if _FS_EXFAT && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* exFAT: Accessing FAT and Allocation Bitmap */ /*-----------------------------------------------------------------------*/ /*---------------------------------------------*/ /* exFAT: Find a contiguous free cluster block */ /*---------------------------------------------*/ static DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Disk error */ FATFS* fs, /* File system object */ DWORD clst, /* Cluster number to scan from */ DWORD ncl /* Number of contiguous clusters to find (1..) */ ) { BYTE bm, bv; UINT i; DWORD val, scl, ctr; clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ if (clst >= fs->n_fatent - 2) clst = 0; scl = val = clst; ctr = 0; for (;;) { if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */ i = val / 8 % SS(fs); bm = 1 << (val % 8); do { do { bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ val = 0; bm = 0; i = 4096; } if (!bv) { /* Is it a free cluster? */ if (++ctr == ncl) return scl + 2; /* Check run length */ } else { scl = val; ctr = 0; /* Encountered a live cluster, restart to scan */ } if (val == clst) return 0; /* All cluster scanned? */ } while (bm); bm = 1; } while (++i < SS(fs)); } } /*------------------------------------*/ /* exFAT: Set/Clear a block of bitmap */ /*------------------------------------*/ static FRESULT change_bitmap ( FATFS* fs, /* File system object */ DWORD clst, /* Cluster number to change from */ DWORD ncl, /* Number of clusters to be changed */ int bv /* bit value to be set (0 or 1) */ ) { BYTE bm; UINT i; DWORD sect; clst -= 2; /* The first bit corresponds to cluster #2 */ sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */ i = clst / 8 % SS(fs); /* Byte offset in the sector */ bm = 1 << (clst % 8); /* Bit mask in the byte */ for (;;) { if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; do { do { if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ fs->win[i] ^= bm; /* Flip the bit */ fs->wflag = 1; if (--ncl == 0) return FR_OK; /* All bits processed? */ } while (bm <<= 1); /* Next bit */ bm = 1; } while (++i < SS(fs)); /* Next byte */ i = 0; } } /*---------------------------------------------*/ /* Complement contiguous part of the FAT chain */ /*---------------------------------------------*/ static FRESULT fill_fat_chain ( _FDID* obj /* Pointer to the corresponding object */ ) { FRESULT res; DWORD cl, n; if (obj->stat == 3) { /* Has the object been changed 'fragmented'? */ for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ res = put_fat(obj->fs, cl, cl + 1); if (res != FR_OK) return res; } obj->stat = 0; /* Change status 'FAT chain is valid' */ } return FR_OK; } #endif /* _FS_EXFAT && !_FS_READONLY */ #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* FAT handling - Remove a cluster chain */ /*-----------------------------------------------------------------------*/ static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ _FDID* obj, /* Corresponding object */ DWORD clst, /* Cluster to remove a chain from */ DWORD pclst /* Previous cluster of clst (0:an entire chain) */ ) { FRESULT res = FR_OK; DWORD nxt; FATFS *fs = obj->fs; #if _FS_EXFAT || _USE_TRIM DWORD scl = clst, ecl = clst; #endif #if _USE_TRIM DWORD rt[2]; #endif if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ /* Mark the previous cluster 'EOC' on the FAT if it exists */ if (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { res = put_fat(fs, pclst, 0xFFFFFFFF); if (res != FR_OK) return res; } /* Remove the chain */ do { nxt = get_fat(obj, clst); /* Get cluster status */ if (nxt == 0) break; /* Empty cluster? */ if (nxt == 1) return FR_INT_ERR; /* Internal error? */ if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ if (res != FR_OK) return res; } if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ fs->free_clst++; fs->fsi_flag |= 1; } #if _FS_EXFAT || _USE_TRIM if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ ecl = nxt; } else { /* End of contiguous cluster block */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ if (res != FR_OK) return res; } #endif #if _USE_TRIM rt[0] = clust2sect(fs, scl); /* Start sector */ rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Inform device the block can be erased */ #endif scl = ecl = nxt; } #endif clst = nxt; /* Next cluster */ } while (clst < fs->n_fatent); /* Repeat while not the last link */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { if (pclst == 0) { /* Does object have no chain? */ obj->stat = 0; /* Change the object status 'initial' */ } else { if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Did the chain got contiguous? */ obj->stat = 2; /* Change the object status 'contiguous' */ } } } #endif return FR_OK; } /*-----------------------------------------------------------------------*/ /* FAT handling - Stretch a chain or Create a new chain */ /*-----------------------------------------------------------------------*/ static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ _FDID* obj, /* Corresponding object */ DWORD clst /* Cluster# to stretch, 0:Create a new chain */ ) { DWORD cs, ncl, scl; FRESULT res; FATFS *fs = obj->fs; if (clst == 0) { /* Create a new chain */ scl = fs->last_clst; /* Get suggested cluster to start from */ if (scl == 0 || scl >= fs->n_fatent) scl = 1; } else { /* Stretch current chain */ cs = get_fat(obj, clst); /* Check the cluster status */ if (cs < 2) return 1; /* Invalid value */ if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ scl = clst; } #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ if (res == FR_INT_ERR) return 1; if (res == FR_DISK_ERR) return 0xFFFFFFFF; if (clst == 0) { /* Is it a new chain? */ obj->stat = 2; /* Set status 'contiguous chain' */ } else { /* This is a stretched chain */ if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ obj->stat = 3; /* Change status 'just fragmented' */ } } } else #endif { /* On the FAT12/16/32 volume */ ncl = scl; /* Start cluster */ for (;;) { ncl++; /* Next cluster */ if (ncl >= fs->n_fatent) { /* Check wrap-around */ ncl = 2; if (ncl > scl) return 0; /* No free cluster */ } cs = get_fat(obj, ncl); /* Get the cluster status */ if (cs == 0) break; /* Found a free cluster */ if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* An error occurred */ if (ncl == scl) return 0; /* No free cluster */ } } if (_FS_EXFAT && fs->fs_type == FS_EXFAT && obj->stat == 2) { /* Is it a contiguous chain? */ res = FR_OK; /* FAT does not need to be written */ } else { res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ if (res == FR_OK && clst) { res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ } } if (res == FR_OK) { /* Update FSINFO if function succeeded. */ fs->last_clst = ncl; if (fs->free_clst < fs->n_fatent - 2) fs->free_clst--; fs->fsi_flag |= 1; } else { ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Create error status */ } return ncl; /* Return new cluster number or error status */ } #endif /* !_FS_READONLY */ #if _USE_FASTSEEK /*-----------------------------------------------------------------------*/ /* FAT handling - Convert offset into cluster with link map table */ /*-----------------------------------------------------------------------*/ static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ FIL* fp, /* Pointer to the file object */ FSIZE_t ofs /* File offset to be converted to cluster# */ ) { DWORD cl, ncl, *tbl; FATFS *fs = fp->obj.fs; tbl = fp->cltbl + 1; /* Top of CLMT */ cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ for (;;) { ncl = *tbl++; /* Number of cluters in the fragment */ if (ncl == 0) return 0; /* End of table? (error) */ if (cl < ncl) break; /* In this fragment? */ cl -= ncl; tbl++; /* Next fragment */ } return cl + *tbl; /* Return the cluster number */ } #endif /* _USE_FASTSEEK */ /*-----------------------------------------------------------------------*/ /* Directory handling - Set directory index */ /*-----------------------------------------------------------------------*/ static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to directory object */ DWORD ofs /* Offset of directory table */ ) { DWORD csz, clst; FATFS *fs = dp->obj.fs; if (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ return FR_INT_ERR; } dp->dptr = ofs; /* Set current offset */ clst = dp->obj.sclust; /* Table start cluster (0:root) */ if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ clst = fs->dirbase; if (_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ } if (clst == 0) { /* Static table (root-directory in FAT12/16) */ if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ dp->sect = fs->dirbase; } else { /* Dynamic table (sub-directory or root-directory in FAT32+) */ csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ while (ofs >= csz) { /* Follow cluster chain */ clst = get_fat(&dp->obj, clst); /* Get next cluster */ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ ofs -= csz; } dp->sect = clust2sect(fs, clst); } dp->clust = clst; /* Current cluster# */ if (!dp->sect) return FR_INT_ERR; dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ return FR_OK; } /*-----------------------------------------------------------------------*/ /* Directory handling - Move directory table index next */ /*-----------------------------------------------------------------------*/ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ DIR* dp, /* Pointer to the directory object */ int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ ) { DWORD ofs, clst; FATFS *fs = dp->obj.fs; #if !_FS_READONLY UINT n; #endif ofs = dp->dptr + SZDIRE; /* Next entry */ if (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */ if (ofs % SS(fs) == 0) { /* Sector changed? */ dp->sect++; /* Next sector */ if (!dp->clust) { /* Static table */ if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ dp->sect = 0; return FR_NO_FILE; } } else { /* Dynamic table */ if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ if (clst <= 1) return FR_INT_ERR; /* Internal error */ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ if (clst >= fs->n_fatent) { /* Reached end of dynamic table */ #if !_FS_READONLY if (!stretch) { /* If no stretch, report EOT */ dp->sect = 0; return FR_NO_FILE; } clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ if (clst == 0) return FR_DENIED; /* No free cluster */ if (clst == 1) return FR_INT_ERR; /* Internal error */ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ /* Clean-up the stretched table */ if (_FS_EXFAT) dp->obj.stat |= 4; /* The directory needs to be updated */ if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */ fs->wflag = 1; if (sync_window(fs) != FR_OK) return FR_DISK_ERR; } fs->winsect -= n; /* Restore window offset */ #else if (!stretch) dp->sect = 0; /* If no stretch, report EOT (this is to suppress warning) */ dp->sect = 0; return FR_NO_FILE; /* Report EOT */ #endif } dp->clust = clst; /* Initialize data for new cluster */ dp->sect = clust2sect(fs, clst); } } } dp->dptr = ofs; /* Current entry */ dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ return FR_OK; } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Directory handling - Reserve a block of directory entries */ /*-----------------------------------------------------------------------*/ static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to the directory object */ UINT nent /* Number of contiguous entries to allocate */ ) { FRESULT res; UINT n; FATFS *fs = dp->obj.fs; res = dir_sdi(dp, 0); if (res == FR_OK) { n = 0; do { res = move_window(fs, dp->sect); if (res != FR_OK) break; #if _FS_EXFAT if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { #else if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { #endif if (++n == nent) break; /* A block of contiguous free entries is found */ } else { n = 0; /* Not a blank entry. Restart to search */ } res = dir_next(dp, 1); } while (res == FR_OK); /* Next entry with table stretch enabled */ } if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ return res; } #endif /* !_FS_READONLY */ /*-----------------------------------------------------------------------*/ /* FAT: Directory handling - Load/Store start cluster number */ /*-----------------------------------------------------------------------*/ static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ FATFS* fs, /* Pointer to the fs object */ const BYTE* dir /* Pointer to the key entry */ ) { DWORD cl; cl = ld_word(dir + DIR_FstClusLO); if (fs->fs_type == FS_FAT32) { cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; } return cl; } #if !_FS_READONLY static void st_clust ( FATFS* fs, /* Pointer to the fs object */ BYTE* dir, /* Pointer to the key entry */ DWORD cl /* Value to be set */ ) { st_word(dir + DIR_FstClusLO, (WORD)cl); if (fs->fs_type == FS_FAT32) { st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); } } #endif #if _USE_LFN != 0 /*------------------------------------------------------------------------*/ /* FAT-LFN: LFN handling */ /*------------------------------------------------------------------------*/ static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ /*--------------------------------------------------------*/ /* FAT-LFN: Compare a part of file name with an LFN entry */ /*--------------------------------------------------------*/ static int cmp_lfn ( /* 1:matched, 0:not matched */ const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ BYTE* dir /* Pointer to the directory entry containing the part of LFN */ ) { UINT i, s; WCHAR wc, uc; if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ if (wc) { if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ return 0; /* Not matched */ } wc = uc; } else { if (uc != 0xFFFF) return 0; /* Check filler */ } } if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ return 1; /* The part of LFN matched */ } #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT /*-----------------------------------------------------*/ /* FAT-LFN: Pick a part of file name from an LFN entry */ /*-----------------------------------------------------*/ static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ BYTE* dir /* Pointer to the LFN entry */ ) { UINT i, s; WCHAR wc, uc; if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ if (wc) { if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ lfnbuf[i++] = wc = uc; /* Store it */ } else { if (uc != 0xFFFF) return 0; /* Check filler */ } } if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ lfnbuf[i] = 0; } return 1; /* The part of LFN is valid */ } #endif #if !_FS_READONLY /*-----------------------------------------*/ /* FAT-LFN: Create an entry of LFN entries */ /*-----------------------------------------*/ static void put_lfn ( const WCHAR* lfn, /* Pointer to the LFN */ BYTE* dir, /* Pointer to the LFN entry to be created */ BYTE ord, /* LFN order (1-20) */ BYTE sum /* Checksum of the corresponding SFN */ ) { UINT i, s; WCHAR wc; dir[LDIR_Chksum] = sum; /* Set checksum */ dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ dir[LDIR_Type] = 0; st_word(dir + LDIR_FstClusLO, 0); i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ s = wc = 0; do { if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ st_word(dir + LfnOfs[s], wc); /* Put it */ if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ } while (++s < 13); if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ dir[LDIR_Ord] = ord; /* Set the LFN order */ } #endif /* !_FS_READONLY */ #endif /* _USE_LFN != 0 */ #if _USE_LFN != 0 && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* FAT-LFN: Create a Numbered SFN */ /*-----------------------------------------------------------------------*/ static void gen_numname ( BYTE* dst, /* Pointer to the buffer to store numbered SFN */ const BYTE* src, /* Pointer to SFN */ const WCHAR* lfn, /* Pointer to LFN */ UINT seq /* Sequence number */ ) { BYTE ns[8], c; UINT i, j; WCHAR wc; DWORD sr; mem_cpy(dst, src, 11); if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ sr = seq; while (*lfn) { /* Create a CRC */ wc = *lfn++; for (i = 0; i < 16; i++) { sr = (sr << 1) + (wc & 1); wc >>= 1; if (sr & 0x10000) sr ^= 0x11021; } } seq = (UINT)sr; } /* itoa (hexdecimal) */ i = 7; do { c = (BYTE)((seq % 16) + '0'); if (c > '9') c += 7; ns[i--] = c; seq /= 16; } while (seq); ns[i] = '~'; /* Append the number */ for (j = 0; j < i && dst[j] != ' '; j++) { if (IsDBCS1(dst[j])) { if (j == i - 1) break; j++; } } do { dst[j++] = (i < 8) ? ns[i++] : ' '; } while (j < 8); } #endif /* _USE_LFN != 0 && !_FS_READONLY */ #if _USE_LFN != 0 /*-----------------------------------------------------------------------*/ /* FAT-LFN: Calculate checksum of an SFN entry */ /*-----------------------------------------------------------------------*/ static BYTE sum_sfn ( const BYTE* dir /* Pointer to the SFN entry */ ) { BYTE sum = 0; UINT n = 11; do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); return sum; } #endif /* _USE_LFN != 0 */ #if _FS_EXFAT /*-----------------------------------------------------------------------*/ /* exFAT: Checksum */ /*-----------------------------------------------------------------------*/ static WORD xdir_sum ( /* Get checksum of the directoly block */ const BYTE* dir /* Directory entry block to be calculated */ ) { UINT i, szblk; WORD sum; szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; for (i = sum = 0; i < szblk; i++) { if (i == XDIR_SetSum) { /* Skip sum field */ i++; } else { sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; } } return sum; } static WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ const WCHAR* name /* File name to be calculated */ ) { WCHAR chr; WORD sum = 0; while ((chr = *name++) != 0) { chr = ff_wtoupper(chr); /* File name needs to be ignored case */ sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); } return sum; } #if !_FS_READONLY && _USE_MKFS static DWORD xsum32 ( BYTE dat, /* Data to be sumed */ DWORD sum /* Previous value */ ) { sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; return sum; } #endif #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 /*------------------------------------------------------*/ /* exFAT: Get object information from a directory block */ /*------------------------------------------------------*/ static void get_xdir_info ( BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ FILINFO* fno /* Buffer to store the extracted file information */ ) { UINT di, si; WCHAR w; #if !_LFN_UNICODE UINT nc; #endif /* Get file name */ #if _LFN_UNICODE if (dirb[XDIR_NumName] <= _MAX_LFN) { for (si = SZDIRE * 2, di = 0; di < dirb[XDIR_NumName]; si += 2, di++) { if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ w = ld_word(dirb + si); /* Get a character */ fno->fname[di] = w; /* Store it */ } } else { di = 0; /* Buffer overflow and inaccessible object */ } #else for (si = SZDIRE * 2, di = nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) { if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ w = ld_word(dirb + si); /* Get a character */ w = ff_convert(w, 0); /* Unicode -> OEM */ if (w == 0) { di = 0; break; } /* Could not be converted and inaccessible object */ if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ fno->fname[di++] = (char)(w >> 8); } if (di >= _MAX_LFN) { di = 0; break; } /* Buffer overflow and inaccessible object */ fno->fname[di++] = (char)w; } #endif if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object? */ fno->fname[di] = 0; /* Terminate file name */ fno->altname[0] = 0; /* No SFN */ fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ } #endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ /*-----------------------------------*/ /* exFAT: Get a directry entry block */ /*-----------------------------------*/ static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ ) { FRESULT res; UINT i, nent; BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ /* Load 85 entry */ res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; mem_cpy(dirb, dp->dir, SZDIRE); nent = dirb[XDIR_NumSec] + 1; /* Load C0 entry */ res = dir_next(dp, 0); if (res != FR_OK) return res; res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR; mem_cpy(dirb + SZDIRE, dp->dir, SZDIRE); /* Load C1 entries */ if (nent < 3 || nent > 19) return FR_NO_FILE; i = SZDIRE * 2; nent *= SZDIRE; do { res = dir_next(dp, 0); if (res != FR_OK) return res; res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR; mem_cpy(dirb + i, dp->dir, SZDIRE); i += SZDIRE; } while (i < nent); /* Sanity check */ if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; return FR_OK; } #if !_FS_READONLY || _FS_RPATH != 0 /*------------------------------------------------*/ /* exFAT: Load the object's directory entry block */ /*------------------------------------------------*/ static FRESULT load_obj_dir ( DIR* dp, /* Blank directory object to be used to access containing direcotry */ const _FDID* obj /* Object with containing directory information */ ) { FRESULT res; /* Open object containing directory */ dp->obj.fs = obj->fs; dp->obj.sclust = obj->c_scl; dp->obj.stat = (BYTE)obj->c_size; dp->obj.objsize = obj->c_size & 0xFFFFFF00; dp->blk_ofs = obj->c_ofs; res = dir_sdi(dp, dp->blk_ofs); /* Goto the block location */ if (res == FR_OK) { res = load_xdir(dp); /* Load the object's entry block */ } return res; } #endif #if !_FS_READONLY /*-----------------------------------------------*/ /* exFAT: Store the directory block to the media */ /*-----------------------------------------------*/ static FRESULT store_xdir ( DIR* dp /* Pointer to the direcotry object */ ) { FRESULT res; UINT nent; BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ /* Create set sum */ st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); nent = dirb[XDIR_NumSec] + 1; /* Store the set of directory to the volume */ res = dir_sdi(dp, dp->blk_ofs); while (res == FR_OK) { res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) break; mem_cpy(dp->dir, dirb, SZDIRE); dp->obj.fs->wflag = 1; if (--nent == 0) break; dirb += SZDIRE; res = dir_next(dp, 0); } return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; } /*-------------------------------------------*/ /* exFAT: Create a new directory enrty block */ /*-------------------------------------------*/ static void create_xdir ( BYTE* dirb, /* Pointer to the direcotry entry block buffer */ const WCHAR* lfn /* Pointer to the nul terminated file name */ ) { UINT i; BYTE nb, nc; WCHAR chr; mem_set(dirb, 0, 2 * SZDIRE); /* Initialize 85+C0 entry */ dirb[XDIR_Type] = 0x85; dirb[XDIR_Type + SZDIRE] = 0xC0; st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ i = SZDIRE * 2; /* C1 offset */ nc = 0; nb = 1; chr = 1; do { dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ do { /* Fill name field */ if (chr && (chr = lfn[nc]) != 0) nc++; /* Get a character if exist */ st_word(dirb + i, chr); i += 2; /* Store it */ } while (i % SZDIRE); nb++; } while (lfn[nc]); /* Fill next entry if any char follows */ dirb[XDIR_NumName] = nc; /* Set name length */ dirb[XDIR_NumSec] = nb; /* Set number of C0+C1s */ } #endif /* !_FS_READONLY */ #endif /* _FS_EXFAT */ #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT /*-----------------------------------------------------------------------*/ /* Read an object from the directory */ /*-----------------------------------------------------------------------*/ static FRESULT dir_read ( DIR* dp, /* Pointer to the directory object */ int vol /* Filtered by 0:file/directory or 1:volume label */ ) { FRESULT res = FR_NO_FILE; FATFS *fs = dp->obj.fs; BYTE a, c; #if _USE_LFN != 0 BYTE ord = 0xFF, sum = 0xFF; #endif while (dp->sect) { res = move_window(fs, dp->sect); if (res != FR_OK) break; c = dp->dir[DIR_Name]; /* Test for the entry type */ if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of the directory */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ if (_USE_LABEL && vol) { if (c == 0x83) break; /* Volume label entry? */ } else { if (c == 0x85) { /* Start of the file entry block? */ dp->blk_ofs = dp->dptr; /* Get location of the block */ res = load_xdir(dp); /* Load the entry block */ if (res == FR_OK) { dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ } break; } } } else #endif { /* On the FAT12/16/32 volume */ dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ #if _USE_LFN != 0 /* LFN configuration */ if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ ord = 0xFF; } else { if (a == AM_LFN) { /* An LFN entry is found */ if (c & LLEF) { /* Is it start of an LFN sequence? */ sum = dp->dir[LDIR_Chksum]; c &= (BYTE)~LLEF; ord = c; dp->blk_ofs = dp->dptr; } /* Check LFN validity and capture it */ ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } else { /* An SFN entry is found */ if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ } break; } } #else /* Non LFN configuration */ if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ break; } #endif } res = dir_next(dp, 0); /* Next entry */ if (res != FR_OK) break; } if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ return res; } #endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ /*-----------------------------------------------------------------------*/ /* Directory handling - Find an object in the directory */ /*-----------------------------------------------------------------------*/ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp /* Pointer to the directory object with the file name */ ) { FRESULT res; FATFS *fs = dp->obj.fs; BYTE c; #if _USE_LFN != 0 BYTE a, ord, sum; #endif res = dir_sdi(dp, 0); /* Rewind directory object */ if (res != FR_OK) return res; #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ BYTE nc; UINT di, ni; WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip the comparison if hash value mismatched */ for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ if ((di % SZDIRE) == 0) di += 2; if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; } if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ } return res; } #endif /* On the FAT12/16/32 volume */ #if _USE_LFN != 0 ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ #endif do { res = move_window(fs, dp->sect); if (res != FR_OK) break; c = dp->dir[DIR_Name]; if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ #if _USE_LFN != 0 /* LFN configuration */ dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } else { if (a == AM_LFN) { /* An LFN entry is found */ if (!(dp->fn[NSFLAG] & NS_NOLFN)) { if (c & LLEF) { /* Is it start of LFN sequence? */ sum = dp->dir[LDIR_Chksum]; c &= (BYTE)~LLEF; ord = c; /* LFN start order */ dp->blk_ofs = dp->dptr; /* Start offset of LFN */ } /* Check validity of the LFN entry and compare it with given name */ ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } } else { /* An SFN entry is found */ if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } } #else /* Non LFN configuration */ dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ #endif res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK); return res; } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Register an object to the directory */ /*-----------------------------------------------------------------------*/ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ DIR* dp /* Target directory with object name to be created */ ) { FRESULT res; FATFS *fs = dp->obj.fs; #if _USE_LFN != 0 /* LFN configuration */ UINT n, nlen, nent; BYTE sn[12], sum; if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ DIR dj; nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ res = dir_alloc(dp, nent); /* Allocate entries */ if (res != FR_OK) return res; dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */ if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */ dp->obj.stat &= 3; dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */ res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */ if (res != FR_OK) return res; res = load_obj_dir(&dj, &dp->obj); if (res != FR_OK) return res; /* Load the object status */ st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; res = store_xdir(&dj); /* Store the object status */ if (res != FR_OK) return res; } create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ return FR_OK; } #endif /* On the FAT12/16/32 volume */ mem_cpy(sn, dp->fn, 12); if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ for (n = 1; n < 100; n++) { gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ res = dir_find(dp); /* Check if the name collides with existing SFN */ if (res != FR_OK) break; } if (n == 100) return FR_DENIED; /* Abort if too many collisions */ if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ dp->fn[NSFLAG] = sn[NSFLAG]; } /* Create an SFN with/without LFNs. */ nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ res = dir_alloc(dp, nent); /* Allocate entries */ if (res == FR_OK && --nent) { /* Set LFN entry if needed */ res = dir_sdi(dp, dp->dptr - nent * SZDIRE); if (res == FR_OK) { sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ do { /* Store LFN entries in bottom first */ res = move_window(fs, dp->sect); if (res != FR_OK) break; put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); fs->wflag = 1; res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK && --nent); } } #else /* Non LFN configuration */ res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ #endif /* Set SFN entry */ if (res == FR_OK) { res = move_window(fs, dp->sect); if (res == FR_OK) { mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ #if _USE_LFN != 0 dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ #endif fs->wflag = 1; } } return res; } #endif /* !_FS_READONLY */ #if !_FS_READONLY && _FS_MINIMIZE == 0 /*-----------------------------------------------------------------------*/ /* Remove an object from the directory */ /*-----------------------------------------------------------------------*/ static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ DIR* dp /* Directory object pointing the entry to be removed */ ) { FRESULT res; FATFS *fs = dp->obj.fs; #if _USE_LFN != 0 /* LFN configuration */ DWORD last = dp->dptr; res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ if (res == FR_OK) { do { res = move_window(fs, dp->sect); if (res != FR_OK) break; /* Mark an entry 'deleted' */ if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ dp->dir[XDIR_Type] &= 0x7F; } else { /* On the FAT12/16/32 volume */ dp->dir[DIR_Name] = DDEM; } fs->wflag = 1; if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK); if (res == FR_NO_FILE) res = FR_INT_ERR; } #else /* Non LFN configuration */ res = move_window(fs, dp->sect); if (res == FR_OK) { dp->dir[DIR_Name] = DDEM; fs->wflag = 1; } #endif return res; } #endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */ #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 /*-----------------------------------------------------------------------*/ /* Get file information from directory entry */ /*-----------------------------------------------------------------------*/ static void get_fileinfo ( /* No return code */ DIR* dp, /* Pointer to the directory object */ FILINFO* fno /* Pointer to the file information to be filled */ ) { UINT i, j; TCHAR c; DWORD tm; #if _USE_LFN != 0 WCHAR w, lfv; FATFS *fs = dp->obj.fs; #endif fno->fname[0] = 0; /* Invaidate file info */ if (!dp->sect) return; /* Exit if read pointer has reached end of directory */ #if _USE_LFN != 0 /* LFN configuration */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ get_xdir_info(fs->dirbuf, fno); return; } else #endif { /* On the FAT12/16/32 volume */ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ i = j = 0; while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ #if !_LFN_UNICODE w = ff_convert(w, 0); /* Unicode -> OEM */ if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */ if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ fno->fname[i++] = (char)(w >> 8); } #endif if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ fno->fname[i++] = (TCHAR)w; } fno->fname[i] = 0; /* Terminate the LFN */ } } i = j = 0; lfv = fno->fname[i]; /* LFN is exist if non-zero */ while (i < 11) { /* Copy name body and extension */ c = (TCHAR)dp->dir[i++]; if (c == ' ') continue; /* Skip padding spaces */ if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ if (i == 9) { /* Insert a . if extension is exist */ if (!lfv) fno->fname[j] = '.'; fno->altname[j++] = '.'; } #if _LFN_UNICODE if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) { c = c << 8 | dp->dir[i++]; } c = ff_convert(c, 1); /* OEM -> Unicode */ if (!c) c = '?'; #endif fno->altname[j] = c; if (!lfv) { if (IsUpper(c) && (dp->dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) { c += 0x20; /* To lower */ } fno->fname[j] = c; } j++; } if (!lfv) { fno->fname[j] = 0; if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */ } fno->altname[j] = 0; /* Terminate the SFN */ #else /* Non-LFN configuration */ i = j = 0; while (i < 11) { /* Copy name body and extension */ c = (TCHAR)dp->dir[i++]; if (c == ' ') continue; /* Skip padding spaces */ if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */ fno->fname[j++] = c; } fno->fname[j] = 0; #endif fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); } #endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ #if _USE_FIND && _FS_MINIMIZE <= 1 /*-----------------------------------------------------------------------*/ /* Pattern matching */ /*-----------------------------------------------------------------------*/ static WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ ) { #if !_LFN_UNICODE WCHAR chr; chr = (BYTE)*(*ptr)++; /* Get a byte */ if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ #ifdef _EXCVT if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ #else if (IsDBCS1(chr) && IsDBCS2(**ptr)) { /* Get DBC 2nd byte if needed */ chr = chr << 8 | (BYTE)*(*ptr)++; } #endif return chr; #else return ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ #endif } static int pattern_matching ( /* 0:not matched, 1:matched */ const TCHAR* pat, /* Matching pattern */ const TCHAR* nam, /* String to be tested */ int skip, /* Number of pre-skip chars (number of ?s) */ int inf /* Infinite search (* specified) */ ) { const TCHAR *pp, *np; WCHAR pc, nc; int nm, nx; while (skip--) { /* Pre-skip name chars */ if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ } if (!*pat && inf) return 1; /* (short circuit) */ do { pp = pat; np = nam; /* Top of pattern and name to match */ for (;;) { if (*pp == '?' || *pp == '*') { /* Wildcard? */ nm = nx = 0; do { /* Analyze the wildcard chars */ if (*pp++ == '?') nm++; else nx = 1; } while (*pp == '?' || *pp == '*'); if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ nc = *np; break; /* Branch mismatched */ } pc = get_achar(&pp); /* Get a pattern char */ nc = get_achar(&np); /* Get a name char */ if (pc != nc) break; /* Branch mismatched? */ if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ } get_achar(&nam); /* nam++ */ } while (inf && nc); /* Retry until end of name if infinite search is specified */ return 0; } #endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ /*-----------------------------------------------------------------------*/ /* Pick a top segment and create the object name in directory form */ /*-----------------------------------------------------------------------*/ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ DIR* dp, /* Pointer to the directory object */ const TCHAR** path /* Pointer to pointer to the segment in the path string */ ) { #if _USE_LFN != 0 /* LFN configuration */ BYTE b, cf; WCHAR w, *lfn; UINT i, ni, si, di; const TCHAR *p; /* Create LFN in Unicode */ p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; for (;;) { w = p[si++]; /* Get a character */ if (w < ' ') break; /* Break if end of the path name */ if (w == '/' || w == '\\') { /* Break if a separator is found */ while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ break; } if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ #if !_LFN_UNICODE w &= 0xFF; if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ b = (BYTE)p[si++]; /* Get 2nd byte */ w = (w << 8) + b; /* Create a DBC */ if (!IsDBCS2(b)) return FR_INVALID_NAME; /* Reject invalid sequence */ } w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ if (!w) return FR_INVALID_NAME; /* Reject invalid code */ #endif if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ lfn[di++] = w; /* Store the Unicode character */ } *path = &p[si]; /* Return pointer to the next segment */ cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ #if _FS_RPATH != 0 if ((di == 1 && lfn[di - 1] == '.') || (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ lfn[di] = 0; for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ dp->fn[i] = (i < di) ? '.' : ' '; dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ return FR_OK; } #endif while (di) { /* Snip off trailing spaces and dots if exist */ w = lfn[di - 1]; if (w != ' ' && w != '.') break; di--; } lfn[di] = 0; /* LFN is created */ if (di == 0) return FR_INVALID_NAME; /* Reject nul name */ /* Create SFN in directory form */ mem_set(dp->fn, ' ', 11); for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ if (si) cf |= NS_LOSS | NS_LFN; while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ i = b = 0; ni = 8; for (;;) { w = lfn[si++]; /* Get an LFN character */ if (!w) break; /* Break on end of the LFN */ if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ cf |= NS_LOSS | NS_LFN; continue; } if (i >= ni || si == di) { /* Extension or end of SFN */ if (ni == 11) { /* Long extension */ cf |= NS_LOSS | NS_LFN; break; } if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ if (si > di) break; /* No extension */ si = di; i = 8; ni = 11; /* Enter extension section */ b <<= 2; continue; } if (w >= 0x80) { /* Non ASCII character */ #ifdef _EXCVT w = ff_convert(w, 0); /* Unicode -> OEM code */ if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */ #else w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ #endif cf |= NS_LFN; /* Force create LFN entry */ } if (_DF1S && w >= 0x100) { /* Is this DBC? (always false at SBCS cfg) */ if (i >= ni - 1) { cf |= NS_LOSS | NS_LFN; i = ni; continue; } dp->fn[i++] = (BYTE)(w >> 8); } else { /* SBC */ if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ } else { if (IsUpper(w)) { /* ASCII large capital */ b |= 2; } else { if (IsLower(w)) { /* ASCII small capital */ b |= 1; w -= 0x20; } } } } dp->fn[i++] = (BYTE)w; } if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ if (ni == 8) b <<= 2; if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */ if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ } dp->fn[NSFLAG] = cf; /* SFN is created */ return FR_OK; #else /* _USE_LFN != 0 : Non-LFN configuration */ BYTE c, d, *sfn; UINT ni, si, i; const char *p; /* Create file name in directory form */ p = *path; sfn = dp->fn; mem_set(sfn, ' ', 11); si = i = 0; ni = 8; #if _FS_RPATH != 0 if (p[si] == '.') { /* Is this a dot entry? */ for (;;) { c = (BYTE)p[si++]; if (c != '.' || si >= 3) break; sfn[i++] = c; } if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; *path = p + si; /* Return pointer to the next segment */ sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ return FR_OK; } #endif for (;;) { c = (BYTE)p[si++]; if (c <= ' ') break; /* Break if end of the path name */ if (c == '/' || c == '\\') { /* Break if a separator is found */ while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ break; } if (c == '.' || i >= ni) { /* End of body or over size? */ if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ i = 8; ni = 11; /* Goto extension */ continue; } if (c >= 0x80) { /* Extended character? */ #ifdef _EXCVT c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ #else #if !_DF1S return FR_INVALID_NAME; /* Reject extended characters (ASCII only cfg) */ #endif #endif } if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at SBCS cfg.) */ d = (BYTE)p[si++]; /* Get 2nd byte */ if (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ sfn[i++] = c; sfn[i++] = d; } else { /* SBC */ if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ if (IsLower(c)) c -= 0x20; /* To upper */ sfn[i++] = c; } } *path = p + si; /* Return pointer to the next segment */ if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ return FR_OK; #endif /* _USE_LFN != 0 */ } /*-----------------------------------------------------------------------*/ /* Follow a file path */ /*-----------------------------------------------------------------------*/ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ DIR* dp, /* Directory object to return last directory and found object */ const TCHAR* path /* Full-path string to find a file or directory */ ) { FRESULT res; BYTE ns; _FDID *obj = &dp->obj; FATFS *fs = obj->fs; #if _FS_RPATH != 0 if (*path != '/' && *path != '\\') { /* Without heading separator */ obj->sclust = fs->cdir; /* Start from the current directory */ } else #endif { /* With heading separator */ while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ obj->sclust = 0; /* Start from the root directory */ } #if _FS_EXFAT && _FS_RPATH != 0 if (fs->fs_type == FS_EXFAT && obj->sclust) { /* Retrieve the sub-directory status if needed */ DIR dj; obj->c_scl = fs->cdc_scl; obj->c_size = fs->cdc_size; obj->c_ofs = fs->cdc_ofs; res = load_obj_dir(&dj, obj); if (res != FR_OK) return res; obj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize); obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; } #endif if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ dp->fn[NSFLAG] = NS_NONAME; res = dir_sdi(dp, 0); } else { /* Follow path */ for (;;) { res = create_name(dp, &path); /* Get a segment name of the path */ if (res != FR_OK) break; res = dir_find(dp); /* Find an object with the segment name */ ns = dp->fn[NSFLAG]; if (res != FR_OK) { /* Failed to find the object */ if (res == FR_NO_FILE) { /* Object is not found */ if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ dp->fn[NSFLAG] = NS_NONAME; res = FR_OK; } else { /* Could not find the object */ if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ } } break; } if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ /* Get into the sub-directory */ if (!(obj->attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ res = FR_NO_PATH; break; } #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { obj->c_scl = obj->sclust; /* Save containing directory information for next dir */ obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; obj->c_ofs = dp->blk_ofs; obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */ obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); } else #endif { obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ } } } return res; } /*-----------------------------------------------------------------------*/ /* Load a sector and check if it is an FAT boot sector */ /*-----------------------------------------------------------------------*/ static BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ FATFS* fs, /* File system object */ DWORD sect /* Sector# (lba) to check if it is an FAT-VBR or not */ ) { fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { if ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ if (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0; /* Check "FAT3" string */ } #if _FS_EXFAT if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; #endif return 2; } /*-----------------------------------------------------------------------*/ /* Find logical drive and check if the volume is mounted */ /*-----------------------------------------------------------------------*/ static FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ FATFS* fs, /* Pointer to the file system object */ BYTE mode /* !=0: Check write protection for write access */ ) { BYTE fmt, *pt; DSTATUS stat; DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; WORD nrsv; UINT i; ENTER_FF(fs); /* Lock the volume */ mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ if (fs->fs_type) { /* If the volume has been mounted */ disk_ioctl(fs->drv, IOCTL_STATUS, &stat); if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ return FR_WRITE_PROTECTED; } return FR_OK; /* The file system object is valid */ } } /* The file system object is not valid. */ /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ fs->fs_type = 0; /* Clear the file system object */ disk_ioctl(fs->drv, IOCTL_INIT, &stat); /* Initialize the physical drive */ if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ } if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ return FR_WRITE_PROTECTED; } #if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; #endif /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ bsect = 0; fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ if (fmt == 2 || (fmt < 2 && LD2PT(fs) != 0)) { /* Not an FAT-VBR or forced partition number */ for (i = 0; i < 4; i++) { /* Get partition offset */ pt = fs->win + (MBR_Table + i * SZ_PTE); br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; } i = LD2PT(fs); /* Partition number: 0:auto, 1-4:forced */ if (i) i--; do { /* Find an FAT volume */ bsect = br[i]; fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ } while (!LD2PT(fs) && fmt >= 2 && ++i < 4); } if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ /* An FAT volume is found. Following code initializes the file system object */ #if _FS_EXFAT if (fmt == 1) { QWORD maxlba; for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */ if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ return FR_NO_FILESYSTEM; maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ fs->n_fatent = nclst + 2; /* Boundaries and Limits */ fs->volbase = bsect; fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); /* Check if bitmap location is in assumption (at the first cluster) */ if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; for (i = 0; i < SS(fs); i += SZDIRE) { if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ } if (i == SS(fs)) return FR_NO_FILESYSTEM; #if !_FS_READONLY fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ #endif fmt = FS_EXFAT; /* FAT sub-type */ } else #endif /* _FS_EXFAT */ { if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); fs->fsize = fasize; fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ fasize *= fs->n_fats; /* Number of sectors for FAT area */ fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ /* Determine the FAT sub type */ sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ fmt = FS_FAT32; if (nclst <= MAX_FAT16) fmt = FS_FAT16; if (nclst <= MAX_FAT12) fmt = FS_FAT12; /* Boundaries and Limits */ fs->n_fatent = nclst + 2; /* Number of FAT entries */ fs->volbase = bsect; /* Volume start sector */ fs->fatbase = bsect + nrsv; /* FAT start sector */ fs->database = bsect + sysect; /* Data start sector */ if (fmt == FS_FAT32) { if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ } else { if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM;/* (BPB_RootEntCnt must not be 0) */ fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); } if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ #if !_FS_READONLY /* Get FSINFO if available */ fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ fs->fsi_flag = 0x80; #if (_FS_NOFSINFO & 3) != 3 if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */ && ld_word(fs->win + BPB_FSInfo32) == 1 && move_window(fs, bsect + 1) == FR_OK) { fs->fsi_flag = 0; if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) { #if (_FS_NOFSINFO & 1) == 0 fs->free_clst = ld_dword(fs->win + FSI_Free_Count); #endif #if (_FS_NOFSINFO & 2) == 0 fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); #endif } } #endif /* (_FS_NOFSINFO & 3) != 3 */ #endif /* !_FS_READONLY */ } fs->fs_type = fmt; /* FAT sub-type */ fs->id = ++Fsid; /* File system mount ID */ #if _USE_LFN == 1 fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ #if _FS_EXFAT fs->dirbuf = DirBuf; /* Static directory block working buuffer */ #endif #endif #if _FS_RPATH != 0 fs->cdir = 0; /* Initialize current directory */ #endif #if _FS_LOCK != 0 /* Clear file lock semaphores */ clear_lock(fs); #endif return FR_OK; } /*-----------------------------------------------------------------------*/ /* Check if the file/directory object is valid or not */ /*-----------------------------------------------------------------------*/ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ _FDID* obj, /* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */ FATFS** fs /* Pointer to pointer to the owner file system object to return */ ) { FRESULT res; DSTATUS stat; if (!obj || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || disk_ioctl(obj->fs->drv, IOCTL_STATUS, &stat) != RES_OK || (stat & STA_NOINIT)) { *fs = 0; /* The object is invalid */ res = FR_INVALID_OBJECT; } else { *fs = obj->fs; /* Owner file sytem object */ ENTER_FF(obj->fs); /* Lock file system */ res = FR_OK; } return res; } /*--------------------------------------------------------------------------- Public Functions (FatFs API) ----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /* Mount/Unmount a Logical Drive */ /*-----------------------------------------------------------------------*/ FRESULT f_mount ( FATFS* fs /* Pointer to the file system object to mount */ ) { FRESULT res; fs->fs_type = 0; /* Clear new fs object */ #if _FS_REENTRANT /* Create sync object for the new volume */ if (!ff_cre_syncobj(fs, &fs->sobj)) return FR_INT_ERR; #endif res = find_volume(fs, 0); /* Force mounted the volume */ LEAVE_FF(fs, res); } FRESULT f_umount ( FATFS* fs /* Pointer to the file system object to unmount */ ) { #if _FS_LOCK clear_lock(fs); #endif #if _FS_REENTRANT /* Discard sync object of the current volume */ if (!ff_del_syncobj(fs->sobj)) return FR_INT_ERR; #endif fs->fs_type = 0; /* Clear old fs object */ return FR_OK; } /*-----------------------------------------------------------------------*/ /* Open or Create a File */ /*-----------------------------------------------------------------------*/ FRESULT f_open ( FATFS *fs, FIL* fp, /* Pointer to the blank file object */ const TCHAR* path, /* Pointer to the file name */ BYTE mode /* Access mode and file open mode flags */ ) { FRESULT res; DIR dj; #if !_FS_READONLY DWORD dw, cl, bcs, clst, sc; FSIZE_t ofs; #endif DEF_NAMBUF if (!fp) return FR_INVALID_OBJECT; /* Get logical drive */ mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; res = find_volume(fs, mode); if (res == FR_OK) { dj.obj.fs = fs; INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ #if !_FS_READONLY /* R/W configuration */ if (res == FR_OK) { if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ res = FR_INVALID_NAME; } #if _FS_LOCK != 0 else { res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); } #endif } /* Create or Open a file */ if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { if (res != FR_OK) { /* No file, create new */ if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ #if _FS_LOCK != 0 res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; #else res = dir_register(&dj); #endif mode |= FA_CREATE_ALWAYS; /* File is created */ } else { /* Any object is already existing */ if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ res = FR_DENIED; } else { if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ } } if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ dw = GET_FATTIME(); #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* Get current allocation info */ fp->obj.fs = fs; fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Initialize directory entry block */ st_dword(fs->dirbuf + XDIR_CrtTime, dw); /* Set created time */ fs->dirbuf[XDIR_CrtTime10] = 0; st_dword(fs->dirbuf + XDIR_ModTime, dw); /* Set modified time */ fs->dirbuf[XDIR_ModTime10] = 0; fs->dirbuf[XDIR_Attr] = AM_ARC; /* Reset attribute */ st_dword(fs->dirbuf + XDIR_FstClus, 0); /* Reset file allocation info */ st_qword(fs->dirbuf + XDIR_FileSize, 0); st_qword(fs->dirbuf + XDIR_ValidFileSize, 0); fs->dirbuf[XDIR_GenFlags] = 1; res = store_xdir(&dj); if (res == FR_OK && fp->obj.sclust) { /* Remove the cluster chain if exist */ res = remove_chain(&fp->obj, fp->obj.sclust, 0); fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ } } else #endif { /* Clean directory info */ st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */ st_dword(dj.dir + DIR_ModTime, dw); /* Set modified time */ dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ cl = ld_clust(fs, dj.dir); /* Get cluster chain */ st_clust(fs, dj.dir, 0); /* Reset file allocation info */ st_dword(dj.dir + DIR_FileSize, 0); fs->wflag = 1; if (cl) { /* Remove the cluster chain if exist */ dw = fs->winsect; res = remove_chain(&dj.obj, cl, 0); if (res == FR_OK) { res = move_window(fs, dw); fs->last_clst = cl - 1; /* Reuse the cluster hole */ } } } } } else { /* Open an existing file */ if (res == FR_OK) { /* Following succeeded */ if (dj.obj.attr & AM_DIR) { /* It is a directory */ res = FR_NO_FILE; } else { if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */ res = FR_DENIED; } } } } if (res == FR_OK) { if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ mode |= FA_MODIFIED; fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ fp->dir_ptr = dj.dir; #if _FS_LOCK != 0 fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); if (!fp->obj.lockid) res = FR_INT_ERR; #endif } #else /* R/O configuration */ if (res == FR_OK) { if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ res = FR_INVALID_NAME; } else { if (dj.obj.attr & AM_DIR) { /* It is a directory */ res = FR_NO_FILE; } } } #endif if (res == FR_OK) { #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get allocation info */ fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; fp->obj.c_scl = dj.obj.sclust; fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; fp->obj.c_ofs = dj.blk_ofs; } else #endif { fp->obj.sclust = ld_clust(fs, dj.dir); /* Get allocation info */ fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); } #if _USE_FASTSEEK fp->cltbl = 0; /* Disable fast seek mode */ #endif fp->obj.fs = fs; /* Validate the file object */ fp->obj.id = fs->id; fp->flag = mode; /* Set file access mode */ fp->err = 0; /* Clear error flag */ fp->sect = 0; /* Invalidate current data sector */ fp->fptr = 0; /* Set file pointer top of the file */ #if !_FS_READONLY #if !_FS_TINY mem_set(fp->buf, 0, _MAX_SS); /* Clear sector buffer */ #endif if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ fp->fptr = fp->obj.objsize; /* Offset to seek */ bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ clst = fp->obj.sclust; /* Follow the cluster chain */ for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { clst = get_fat(&fp->obj, clst); if (clst <= 1) res = FR_INT_ERR; if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; } fp->clust = clst; if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ if ((sc = clust2sect(fs, clst)) == 0) { res = FR_INT_ERR; } else { fp->sect = sc + (DWORD)(ofs / SS(fs)); #if !_FS_TINY if (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; #endif } } } #endif } FREE_NAMBUF(); } if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ /* Read File */ /*-----------------------------------------------------------------------*/ FRESULT f_read ( FIL* fp, /* Pointer to the file object */ void* buff, /* Pointer to data buffer */ UINT btr, /* Number of bytes to read */ UINT* br /* Pointer to number of bytes read */ ) { FRESULT res; FATFS *fs; DWORD clst, sect; FSIZE_t remain; UINT rcnt, cc, csect; BYTE *rbuff = (BYTE*)buff; *br = 0; /* Clear read byte counter */ res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ remain = fp->obj.objsize - fp->fptr; if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ for ( ; btr; /* Repeat until all data read */ rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ clst = fp->obj.sclust; /* Follow cluster chain from the origin */ } else { /* Middle or end of the file */ #if _USE_FASTSEEK if (fp->cltbl) { clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ } else #endif { clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ } } if (clst < 2) ABORT(fs, FR_INT_ERR); if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); fp->clust = clst; /* Update current cluster */ } sect = clust2sect(fs, fp->clust); /* Get current sector */ if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; cc = btr / SS(fs); /* When remaining bytes >= sector size, */ if (cc) { /* Read maximum contiguous sectors directly */ if (csect + cc > fs->csize) { /* Clip at cluster boundary */ cc = fs->csize - csect; } if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); #if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ #if _FS_TINY if (fs->wflag && fs->winsect - sect < cc) { mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); } #else if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); } #endif #endif rcnt = SS(fs) * cc; /* Number of bytes transferred */ continue; } #if !_FS_TINY if (fp->sect != sect) { /* Load data sector if not in cache */ #if !_FS_READONLY if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA_DIRTY; } #endif if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ } #endif fp->sect = sect; } rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ #if _FS_TINY if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #else mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #endif } LEAVE_FF(fs, FR_OK); } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Write File */ /*-----------------------------------------------------------------------*/ FRESULT f_write ( FIL* fp, /* Pointer to the file object */ const void* buff, /* Pointer to the data to be written */ UINT btw, /* Number of bytes to write */ UINT* bw /* Pointer to number of bytes written */ ) { FRESULT res; FATFS *fs; DWORD clst, sect; UINT wcnt, cc, csect; const BYTE *wbuff = (const BYTE*)buff; *bw = 0; /* Clear write byte counter */ res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ /* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */ if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); } for ( ; btw; /* Repeat until all data written */ wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) { if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ clst = fp->obj.sclust; /* Follow from the origin */ if (clst == 0) { /* If no cluster is allocated, */ clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ } } else { /* On the middle or end of the file */ #if _USE_FASTSEEK if (fp->cltbl) { clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ } else #endif { clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ } } if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ if (clst == 1) ABORT(fs, FR_INT_ERR); if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); fp->clust = clst; /* Update current cluster */ if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ } #if _FS_TINY if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ #else if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA_DIRTY; } #endif sect = clust2sect(fs, fp->clust); /* Get current sector */ if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; cc = btw / SS(fs); /* When remaining bytes >= sector size, */ if (cc) { /* Write maximum contiguous sectors directly */ if (csect + cc > fs->csize) { /* Clip at cluster boundary */ cc = fs->csize - csect; } if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); #if _FS_MINIMIZE <= 2 #if _FS_TINY if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); fs->wflag = 0; } #else if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); fp->flag &= (BYTE)~FA_DIRTY; } #endif #endif wcnt = SS(fs) * cc; /* Number of bytes transferred */ continue; } #if _FS_TINY if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); fs->winsect = sect; } #else if (fp->sect != sect && /* Fill sector cache with file data */ fp->fptr < fp->obj.objsize && disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { ABORT(fs, FR_DISK_ERR); } #endif fp->sect = sect; } wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ #if _FS_TINY if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fs->wflag = 1; #else mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fp->flag |= FA_DIRTY; #endif } fp->flag |= FA_MODIFIED; /* Set file change flag */ LEAVE_FF(fs, FR_OK); } /*-----------------------------------------------------------------------*/ /* Synchronize the File */ /*-----------------------------------------------------------------------*/ FRESULT f_sync ( FIL* fp /* Pointer to the file object */ ) { FRESULT res; FATFS *fs; DWORD tm; BYTE *dir; #if _FS_EXFAT DEF_NAMBUF #endif res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res == FR_OK) { if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ #if !_FS_TINY if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA_DIRTY; } #endif /* Update the directory entry */ tm = GET_FATTIME(); /* Modified time */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { res = fill_fat_chain(&fp->obj); /* Create FAT chain if needed */ if (res == FR_OK) { DIR dj; INIT_NAMBUF(fs); res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */ if (res == FR_OK) { fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ fs->dirbuf[XDIR_ModTime10] = 0; st_dword(fs->dirbuf + XDIR_AccTime, 0); res = store_xdir(&dj); /* Restore it to the directory */ if (res == FR_OK) { res = sync_fs(fs); fp->flag &= (BYTE)~FA_MODIFIED; } } FREE_NAMBUF(); } } else #endif { res = move_window(fs, fp->dir_sect); if (res == FR_OK) { dir = fp->dir_ptr; dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */ st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ st_dword(dir + DIR_ModTime, tm); /* Update modified time */ st_word(dir + DIR_LstAccDate, 0); fs->wflag = 1; res = sync_fs(fs); /* Restore it to the directory */ fp->flag &= (BYTE)~FA_MODIFIED; } } } } LEAVE_FF(fs, res); } #endif /* !_FS_READONLY */ /*-----------------------------------------------------------------------*/ /* Close File */ /*-----------------------------------------------------------------------*/ FRESULT f_close ( FIL* fp /* Pointer to the file object to be closed */ ) { FRESULT res; FATFS *fs; #if !_FS_READONLY res = f_sync(fp); /* Flush cached data */ if (res == FR_OK) #endif { res = validate(&fp->obj, &fs); /* Lock volume */ if (res == FR_OK) { #if _FS_LOCK != 0 res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ if (res == FR_OK) #endif { fp->obj.fs = 0; /* Invalidate file object */ } #if _FS_REENTRANT unlock_fs(fs, FR_OK); /* Unlock volume */ #endif } } return res; } #if _FS_RPATH >= 1 /*-----------------------------------------------------------------------*/ /* Change Current Directory or Current Drive, Get Current Directory */ /*-----------------------------------------------------------------------*/ FRESULT f_chdir ( FATFS *fs, const TCHAR* path /* Pointer to the directory path */ ) { FRESULT res; DIR dj; DEF_NAMBUF /* Get logical drive */ res = find_volume(fs, 0); if (res == FR_OK) { dj.obj.fs = fs; INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the path */ if (res == FR_OK) { /* Follow completed */ if (dj.fn[NSFLAG] & NS_NONAME) { fs->cdir = dj.obj.sclust; /* It is the start directory itself */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { fs->cdc_scl = dj.obj.c_scl; fs->cdc_size = dj.obj.c_size; fs->cdc_ofs = dj.obj.c_ofs; } #endif } else { if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; fs->cdc_ofs = dj.blk_ofs; } else #endif { fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ } } else { res = FR_NO_PATH; /* Reached but a file */ } } } FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; } LEAVE_FF(fs, res); } #if _FS_RPATH >= 2 FRESULT f_getcwd ( FATFS *fs, TCHAR* buff, /* Pointer to the directory path */ UINT len /* Size of path */ ) { FRESULT res; DIR dj; UINT i, n; DWORD ccl; TCHAR *tp; FILINFO fno; DEF_NAMBUF *buff = 0; /* Get logical drive */ res = find_volume(fs, 0); /* Get current volume */ if (res == FR_OK) { dj.obj.fs = fs; INIT_NAMBUF(fs); i = len; /* Bottom of buffer (directory stack base) */ if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ if (res != FR_OK) break; res = move_window(fs, dj.sect); if (res != FR_OK) break; dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ res = dir_sdi(&dj, 0); if (res != FR_OK) break; do { /* Find the entry links to the child directory */ res = dir_read(&dj, 0); if (res != FR_OK) break; if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ res = dir_next(&dj, 0); } while (res == FR_OK); if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ if (res != FR_OK) break; get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ for (n = 0; fno.fname[n]; n++) ; if (i < n + 3) { res = FR_NOT_ENOUGH_CORE; break; } while (n) buff[--i] = fno.fname[--n]; buff[--i] = '/'; } } tp = buff; if (res == FR_OK) { if (i == len) { /* Root-directory */ *tp++ = '/'; } else { /* Sub-directroy */ do /* Add stacked path str */ *tp++ = buff[i++]; while (i < len); } } *tp = 0; FREE_NAMBUF(); } LEAVE_FF(fs, res); } #endif /* _FS_RPATH >= 2 */ #endif /* _FS_RPATH >= 1 */ #if _FS_MINIMIZE <= 2 /*-----------------------------------------------------------------------*/ /* Seek File R/W Pointer */ /*-----------------------------------------------------------------------*/ FRESULT f_lseek ( FIL* fp, /* Pointer to the file object */ FSIZE_t ofs /* File pointer from top of file */ ) { FRESULT res; FATFS *fs; DWORD clst, bcs, nsect; FSIZE_t ifptr; #if _USE_FASTSEEK DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; #endif res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ #if _USE_FASTSEEK if (fp->cltbl) { /* Fast seek */ if (ofs == CREATE_LINKMAP) { /* Create CLMT */ tbl = fp->cltbl; tlen = *tbl++; ulen = 2; /* Given table size and required table size */ cl = fp->obj.sclust; /* Origin of the chain */ if (cl) { do { /* Get a fragment */ tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ do { pcl = cl; ncl++; cl = get_fat(&fp->obj, cl); if (cl <= 1) ABORT(fs, FR_INT_ERR); if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); } while (cl == pcl + 1); if (ulen <= tlen) { /* Store the length and top of the fragment */ *tbl++ = ncl; *tbl++ = tcl; } } while (cl < fs->n_fatent); /* Repeat until end of chain */ } *fp->cltbl = ulen; /* Number of items used */ if (ulen <= tlen) { *tbl = 0; /* Terminate table */ } else { res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ } } else { /* Fast seek */ if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ fp->fptr = ofs; /* Set file pointer */ if (ofs) { fp->clust = clmt_clust(fp, ofs - 1); dsc = clust2sect(fs, fp->clust); if (!dsc) ABORT(fs, FR_INT_ERR); dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ #if !_FS_TINY #if !_FS_READONLY if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA_DIRTY; } #endif if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ #endif fp->sect = dsc; } } } } else #endif /* Normal Seek */ { #if _FS_EXFAT if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */ #endif if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ ofs = fp->obj.objsize; } ifptr = fp->fptr; fp->fptr = nsect = 0; if (ofs) { bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ if (ifptr > 0 && (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ ofs -= fp->fptr; clst = fp->clust; } else { /* When seek to back cluster, */ clst = fp->obj.sclust; /* start from the first cluster */ #if !_FS_READONLY if (clst == 0) { /* If no cluster chain, create a new chain */ clst = create_chain(&fp->obj, 0); if (clst == 1) ABORT(fs, FR_INT_ERR); if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); fp->obj.sclust = clst; } #endif fp->clust = clst; } if (clst != 0) { while (ofs > bcs) { /* Cluster following loop */ ofs -= bcs; fp->fptr += bcs; #if !_FS_READONLY if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ fp->obj.objsize = fp->fptr; fp->flag |= FA_MODIFIED; } clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ if (clst == 0) { /* Clip file size in case of disk full */ ofs = 0; break; } } else #endif { clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ } if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); fp->clust = clst; } fp->fptr += ofs; if (ofs % SS(fs)) { nsect = clust2sect(fs, clst); /* Current sector */ if (!nsect) ABORT(fs, FR_INT_ERR); nsect += (DWORD)(ofs / SS(fs)); } } } if (!_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ fp->obj.objsize = fp->fptr; fp->flag |= FA_MODIFIED; } if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ #if !_FS_TINY #if !_FS_READONLY if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA_DIRTY; } #endif if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ #endif fp->sect = nsect; } } LEAVE_FF(fs, res); } #if _FS_MINIMIZE <= 1 /*-----------------------------------------------------------------------*/ /* Create a Directory Object */ /*-----------------------------------------------------------------------*/ FRESULT f_opendir ( FATFS *fs, DIR* dp, /* Pointer to directory object to create */ const TCHAR* path /* Pointer to the directory path */ ) { FRESULT res; _FDID *obj; DEF_NAMBUF if (!dp) return FR_INVALID_OBJECT; /* Get logical drive */ obj = &dp->obj; res = find_volume(fs, 0); if (res == FR_OK) { obj->fs = fs; INIT_NAMBUF(fs); res = follow_path(dp, path); /* Follow the path to the directory */ if (res == FR_OK) { /* Follow completed */ if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ if (obj->attr & AM_DIR) { /* This object is a sub-directory */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { obj->c_scl = obj->sclust; /* Save containing directory inforamation */ obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; obj->c_ofs = dp->blk_ofs; obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object location and status */ obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; } else #endif { obj->sclust = ld_clust(fs, dp->dir); /* Get object location */ } } else { /* This object is a file */ res = FR_NO_PATH; } } if (res == FR_OK) { obj->id = fs->id; res = dir_sdi(dp, 0); /* Rewind directory */ #if _FS_LOCK != 0 if (res == FR_OK) { if (obj->sclust) { obj->lockid = inc_lock(dp, 0); /* Lock the sub directory */ if (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES; } else { obj->lockid = 0; /* Root directory need not to be locked */ } } #endif } } FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; } if (res != FR_OK) obj->fs = 0; /* Invalidate the directory object if function faild */ LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ /* Close Directory */ /*-----------------------------------------------------------------------*/ FRESULT f_closedir ( DIR *dp /* Pointer to the directory object to be closed */ ) { FRESULT res; FATFS *fs; res = validate(&dp->obj, &fs); /* Check validity of the file object */ if (res == FR_OK) { #if _FS_LOCK != 0 if (dp->obj.lockid) { /* Decrement sub-directory open counter */ res = dec_lock(dp->obj.lockid); } if (res == FR_OK) #endif { dp->obj.fs = 0; /* Invalidate directory object */ } #if _FS_REENTRANT unlock_fs(fs, FR_OK); /* Unlock volume */ #endif } return res; } /*-----------------------------------------------------------------------*/ /* Read Directory Entries in Sequence */ /*-----------------------------------------------------------------------*/ FRESULT f_readdir ( DIR* dp, /* Pointer to the open directory object */ FILINFO* fno /* Pointer to file information to return */ ) { FRESULT res; FATFS *fs; DEF_NAMBUF res = validate(&dp->obj, &fs); /* Check validity of the directory object */ if (res == FR_OK) { if (!fno) { res = dir_sdi(dp, 0); /* Rewind the directory object */ } else { INIT_NAMBUF(fs); res = dir_read(dp, 0); /* Read an item */ if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ if (res == FR_OK) { /* A valid entry is found */ get_fileinfo(dp, fno); /* Get the object information */ res = dir_next(dp, 0); /* Increment index for next */ if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ } FREE_NAMBUF(); } } LEAVE_FF(fs, res); } #if _USE_FIND /*-----------------------------------------------------------------------*/ /* Find Next File */ /*-----------------------------------------------------------------------*/ FRESULT f_findnext ( DIR* dp, /* Pointer to the open directory object */ FILINFO* fno /* Pointer to the file information structure */ ) { FRESULT res; for (;;) { res = f_readdir(dp, fno); /* Get a directory item */ if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ #if _USE_LFN != 0 && _USE_FIND == 2 if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ #endif } return res; } /*-----------------------------------------------------------------------*/ /* Find First File */ /*-----------------------------------------------------------------------*/ FRESULT f_findfirst ( DIR* dp, /* Pointer to the blank directory object */ FILINFO* fno, /* Pointer to the file information structure */ const TCHAR* path, /* Pointer to the directory to open */ const TCHAR* pattern /* Pointer to the matching pattern */ ) { FRESULT res; dp->pat = pattern; /* Save pointer to pattern string */ res = f_opendir(dp, path); /* Open the target directory */ if (res == FR_OK) { res = f_findnext(dp, fno); /* Find the first item */ } return res; } #endif /* _USE_FIND */ #if _FS_MINIMIZE == 0 /*-----------------------------------------------------------------------*/ /* Get File Status */ /*-----------------------------------------------------------------------*/ FRESULT f_stat ( FATFS *fs, const TCHAR* path, /* Pointer to the file path */ FILINFO* fno /* Pointer to file information to return */ ) { FRESULT res; DIR dj; DEF_NAMBUF /* Get logical drive */ res = find_volume(fs, 0); dj.obj.fs = fs; if (res == FR_OK) { INIT_NAMBUF(dj.obj.fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK) { /* Follow completed */ if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ res = FR_INVALID_NAME; } else { /* Found an object */ if (fno) get_fileinfo(&dj, fno); } } FREE_NAMBUF(); } LEAVE_FF(dj.obj.fs, res); } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Get Number of Free Clusters */ /*-----------------------------------------------------------------------*/ FRESULT f_getfree ( FATFS *fs, DWORD* nclst /* Pointer to a variable to return number of free clusters */ ) { FRESULT res; DWORD nfree, clst, sect, stat; UINT i; BYTE *p; _FDID obj; /* Get logical drive */ res = find_volume(fs, 0); if (res == FR_OK) { /* If free_clst is valid, return it without full cluster scan */ if (fs->free_clst <= fs->n_fatent - 2) { *nclst = fs->free_clst; } else { /* Get number of free clusters */ nfree = 0; if (fs->fs_type == FS_FAT12) { /* FAT12: Sector unalighed FAT entries */ clst = 2; obj.fs = fs; do { stat = get_fat(&obj, clst); if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } if (stat == 1) { res = FR_INT_ERR; break; } if (stat == 0) nfree++; } while (++clst < fs->n_fatent); } else { #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan bitmap table */ BYTE bm; UINT b; clst = fs->n_fatent - 2; sect = fs->database; i = 0; do { if (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break; for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { if (!(bm & 1)) nfree++; bm >>= 1; } i = (i + 1) % SS(fs); } while (clst); } else #endif { /* FAT16/32: Sector alighed FAT entries */ clst = fs->n_fatent; sect = fs->fatbase; i = 0; p = 0; do { if (i == 0) { res = move_window(fs, sect++); if (res != FR_OK) break; p = fs->win; i = SS(fs); } if (fs->fs_type == FS_FAT16) { if (ld_word(p) == 0) nfree++; p += 2; i -= 2; } else { if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++; p += 4; i -= 4; } } while (--clst); } } *nclst = nfree; /* Return the free clusters */ fs->free_clst = nfree; /* Now free_clst is valid */ fs->fsi_flag |= 1; /* FSInfo is to be updated */ } } LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ /* Truncate File */ /*-----------------------------------------------------------------------*/ FRESULT f_truncate ( FIL* fp /* Pointer to the file object */ ) { FRESULT res; FATFS *fs; DWORD ncl; res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ if (fp->obj.objsize > fp->fptr) { if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ res = remove_chain(&fp->obj, fp->obj.sclust, 0); fp->obj.sclust = 0; } else { /* When truncate a part of the file, remove remaining clusters */ ncl = get_fat(&fp->obj, fp->clust); res = FR_OK; if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; if (ncl == 1) res = FR_INT_ERR; if (res == FR_OK && ncl < fs->n_fatent) { res = remove_chain(&fp->obj, ncl, fp->clust); } } fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */ fp->flag |= FA_MODIFIED; #if !_FS_TINY if (res == FR_OK && (fp->flag & FA_DIRTY)) { if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { res = FR_DISK_ERR; } else { fp->flag &= (BYTE)~FA_DIRTY; } } #endif if (res != FR_OK) ABORT(fs, res); } LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ /* Delete a File/Directory */ /*-----------------------------------------------------------------------*/ FRESULT f_unlink ( FATFS *fs, const TCHAR* path /* Pointer to the file or directory path */ ) { FRESULT res; DIR dj, sdj; DWORD dclst = 0; #if _FS_EXFAT _FDID obj; #endif DEF_NAMBUF /* Get logical drive */ res = find_volume(fs, FA_WRITE); dj.obj.fs = fs; if (res == FR_OK) { INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { res = FR_INVALID_NAME; /* Cannot remove dot entry */ } #if _FS_LOCK != 0 if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ #endif if (res == FR_OK) { /* The object is accessible */ if (dj.fn[NSFLAG] & NS_NONAME) { res = FR_INVALID_NAME; /* Cannot remove the origin directory */ } else { if (dj.obj.attr & AM_RDO) { res = FR_DENIED; /* Cannot remove R/O object */ } } if (res == FR_OK) { #if _FS_EXFAT obj.fs = fs; if (fs->fs_type == FS_EXFAT) { obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus); obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; } else #endif { dclst = ld_clust(fs, dj.dir); } if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory ? */ #if _FS_RPATH != 0 if (dclst == fs->cdir) { /* Is it the current directory? */ res = FR_DENIED; } else #endif { sdj.obj.fs = fs; /* Open the sub-directory */ sdj.obj.sclust = dclst; #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { sdj.obj.objsize = obj.objsize; sdj.obj.stat = obj.stat; } #endif res = dir_sdi(&sdj, 0); if (res == FR_OK) { res = dir_read(&sdj, 0); /* Read an item */ if (res == FR_OK) res = FR_DENIED; /* Not empty? */ if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ } } } } if (res == FR_OK) { res = dir_remove(&dj); /* Remove the directory entry */ if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */ #if _FS_EXFAT res = remove_chain(&obj, dclst, 0); #else res = remove_chain(&dj.obj, dclst, 0); #endif } if (res == FR_OK) res = sync_fs(fs); } } FREE_NAMBUF(); } LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ /* Create a Directory */ /*-----------------------------------------------------------------------*/ FRESULT f_mkdir ( FATFS *fs, const TCHAR* path /* Pointer to the directory path */ ) { FRESULT res; DIR dj; BYTE *dir; UINT n; DWORD dsc, dcl, pcl, tm; DEF_NAMBUF /* Get logical drive */ res = find_volume(fs, FA_WRITE); dj.obj.fs = fs; if (res == FR_OK) { INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { res = FR_INVALID_NAME; } if (res == FR_NO_FILE) { /* Can create a new directory */ dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */ dj.obj.objsize = (DWORD)fs->csize * SS(fs); res = FR_OK; if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ if (dcl == 1) res = FR_INT_ERR; if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; if (res == FR_OK) res = sync_window(fs); /* Flush FAT */ tm = GET_FATTIME(); if (res == FR_OK) { /* Initialize the new directory table */ dsc = clust2sect(fs, dcl); dir = fs->win; mem_set(dir, 0, SS(fs)); if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ dir[DIR_Name] = '.'; dir[DIR_Attr] = AM_DIR; st_dword(dir + DIR_ModTime, tm); st_clust(fs, dir, dcl); mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0; st_clust(fs, dir + SZDIRE, pcl); } for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */ fs->winsect = dsc++; fs->wflag = 1; res = sync_window(fs); if (res != FR_OK) break; mem_set(dir, 0, SS(fs)); } } if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ if (res == FR_OK) { #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag (contiguous) */ fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ res = store_xdir(&dj); } else #endif { dir = dj.dir; st_dword(dir + DIR_ModTime, tm); /* Created time */ st_clust(fs, dir, dcl); /* Table start cluster */ dir[DIR_Attr] = AM_DIR; /* Attribute */ fs->wflag = 1; } if (res == FR_OK) res = sync_fs(fs); } else { remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ } } FREE_NAMBUF(); } LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ /* Rename a File/Directory */ /*-----------------------------------------------------------------------*/ FRESULT f_rename ( FATFS *fs, const TCHAR* path_old, /* Pointer to the object name to be renamed */ const TCHAR* path_new /* Pointer to the new name */ ) { FRESULT res; DIR djo, djn; BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir; DWORD dw; DEF_NAMBUF res = find_volume(fs, FA_WRITE); if (res == FR_OK) { djo.obj.fs = fs; INIT_NAMBUF(fs); res = follow_path(&djo, path_old); /* Check old object */ if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ #if _FS_LOCK != 0 if (res == FR_OK) res = chk_lock(&djo, 2); #endif if (res == FR_OK) { /* Object to be renamed is found */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* At exFAT */ BYTE nf, nn; WORD nh; mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ mem_cpy(&djn, &djo, sizeof djo); res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ if (res == FR_OK) { /* Is new name already in use by any other object? */ res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; } if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; nh = ld_word(fs->dirbuf + XDIR_NameHash); mem_cpy(fs->dirbuf, buf, SZDIRE * 2); fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; st_word(fs->dirbuf + XDIR_NameHash, nh); /* Start of critical section where any interruption can cause a cross-link */ res = store_xdir(&djn); } } } else #endif { /* At FAT12/FAT16/FAT32 */ mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */ mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ if (res == FR_OK) { /* Is new name already in use by any other object? */ res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; } if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { dir = djn.dir; /* Copy information about object except name */ mem_cpy(dir + 13, buf + 2, 19); dir[DIR_Attr] = buf[0] | AM_ARC; fs->wflag = 1; if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ dw = clust2sect(fs, ld_clust(fs, dir)); if (!dw) { res = FR_INT_ERR; } else { /* Start of critical section where any interruption can cause a cross-link */ res = move_window(fs, dw); dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ if (res == FR_OK && dir[1] == '.') { st_clust(fs, dir, djn.obj.sclust); fs->wflag = 1; } } } } } } if (res == FR_OK) { res = dir_remove(&djo); /* Remove old entry */ if (res == FR_OK) { res = sync_fs(fs); } } /* End of critical section */ } FREE_NAMBUF(); } LEAVE_FF(fs, res); } #endif /* !_FS_READONLY */ #endif /* _FS_MINIMIZE == 0 */ #endif /* _FS_MINIMIZE <= 1 */ #endif /* _FS_MINIMIZE <= 2 */ #if _USE_CHMOD && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Change Attribute */ /*-----------------------------------------------------------------------*/ FRESULT f_chmod ( FATFS *fs, const TCHAR* path, /* Pointer to the file path */ BYTE attr, /* Attribute bits */ BYTE mask /* Attribute mask to change */ ) { FRESULT res; DIR dj; DEF_NAMBUF res = find_volume(fs, FA_WRITE); /* Get logical drive */ dj.obj.fs = fs; if (res == FR_OK) { INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ if (res == FR_OK) { mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ res = store_xdir(&dj); } else #endif { dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ fs->wflag = 1; } if (res == FR_OK) res = sync_fs(fs); } FREE_NAMBUF(); } LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ /* Change Timestamp */ /*-----------------------------------------------------------------------*/ FRESULT f_utime ( FATFS *fs, const TCHAR* path, /* Pointer to the file/directory name */ const FILINFO* fno /* Pointer to the time stamp to be set */ ) { FRESULT res; DIR dj; DEF_NAMBUF res = find_volume(fs, FA_WRITE); /* Get logical drive */ dj.obj.fs = fs; if (res == FR_OK) { INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ if (res == FR_OK) { #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); res = store_xdir(&dj); } else #endif { st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); fs->wflag = 1; } if (res == FR_OK) res = sync_fs(fs); } FREE_NAMBUF(); } LEAVE_FF(fs, res); } #endif /* _USE_CHMOD && !_FS_READONLY */ #if _USE_LABEL /*-----------------------------------------------------------------------*/ /* Get Volume Label */ /*-----------------------------------------------------------------------*/ FRESULT f_getlabel ( FATFS *fs, TCHAR* label, /* Pointer to a buffer to return the volume label */ DWORD* vsn /* Pointer to a variable to return the volume serial number */ ) { FRESULT res; DIR dj; UINT si, di; #if _LFN_UNICODE || _FS_EXFAT WCHAR w; #endif /* Get logical drive */ res = find_volume(fs, 0); /* Get volume label */ if (res == FR_OK && label) { dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ res = dir_sdi(&dj, 0); if (res == FR_OK) { res = dir_read(&dj, 1); /* Find a volume label entry */ if (res == FR_OK) { #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ w = ld_word(dj.dir + XDIR_Label + si * 2); #if _LFN_UNICODE label[di++] = w; #else w = ff_convert(w, 0); /* Unicode -> OEM */ if (w == 0) w = '?'; /* Replace wrong character */ if (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8); label[di++] = (char)w; #endif } label[di] = 0; } else #endif { si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */ do { #if _LFN_UNICODE w = (si < 11) ? dj.dir[si++] : ' '; if (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) { w = w << 8 | dj.dir[si++]; } label[di++] = ff_convert(w, 1); /* OEM -> Unicode */ #else label[di++] = dj.dir[si++]; #endif } while (di < 11); do { /* Truncate trailing spaces */ label[di] = 0; if (di == 0) break; } while (label[--di] == ' '); } } } if (res == FR_NO_FILE) { /* No label entry and return nul string */ label[0] = 0; res = FR_OK; } } /* Get volume serial number */ if (res == FR_OK && vsn) { res = move_window(fs, fs->volbase); if (res == FR_OK) { switch (fs->fs_type) { case FS_EXFAT: di = BPB_VolIDEx; break; case FS_FAT32: di = BS_VolID32; break; default: di = BS_VolID; } *vsn = ld_dword(fs->win + di); } } LEAVE_FF(fs, res); } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Set Volume Label */ /*-----------------------------------------------------------------------*/ FRESULT f_setlabel ( FATFS *fs, const TCHAR* label /* Pointer to the volume label to set */ ) { FRESULT res; DIR dj; BYTE dirvn[22]; UINT i, j, slen; WCHAR w; static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; /* Get logical drive */ res = find_volume(fs, FA_WRITE); if (res != FR_OK) LEAVE_FF(fs, res); dj.obj.fs = fs; /* Get length of given volume label */ for (slen = 0; (UINT)label[slen] >= ' '; slen++) { } /* Get name length */ #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ w = label[i++]; #if !_LFN_UNICODE if (IsDBCS1(w)) { w = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; } w = ff_convert(w, 1); #endif if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } st_word(dirvn + j, w); j += 2; } slen = j; } else #endif { /* On the FAT12/16/32 volume */ for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ if (slen) { /* Is there a volume label to be set? */ dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ do { #if _LFN_UNICODE w = ff_convert(ff_wtoupper(label[i++]), 0); #else w = (BYTE)label[i++]; if (IsDBCS1(w)) { w = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; } #if _USE_LFN != 0 w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); #else if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ #ifdef _EXCVT if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ #else if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ #endif #endif #endif if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8); dirvn[j++] = (BYTE)w; } while (i < slen); while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */ if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ } } /* Set volume label */ dj.obj.sclust = 0; /* Open root directory */ res = dir_sdi(&dj, 0); if (res == FR_OK) { res = dir_read(&dj, 1); /* Get volume label entry */ if (res == FR_OK) { if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */ mem_cpy(dj.dir + XDIR_Label, dirvn, slen); } else { if (slen) { mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ } else { dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ } } fs->wflag = 1; res = sync_fs(fs); } else { /* No volume label entry is found or error */ if (res == FR_NO_FILE) { res = FR_OK; if (slen) { /* Create a volume label entry */ res = dir_alloc(&dj, 1); /* Allocate an entry */ if (res == FR_OK) { mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); mem_cpy(dj.dir + XDIR_Label, dirvn, slen); } else { dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ mem_cpy(dj.dir, dirvn, 11); } fs->wflag = 1; res = sync_fs(fs); } } } } } LEAVE_FF(fs, res); } #endif /* !_FS_READONLY */ #endif /* _USE_LABEL */ #if _USE_EXPAND && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Allocate a Contiguous Blocks to the File */ /*-----------------------------------------------------------------------*/ FRESULT f_expand ( FIL* fp, /* Pointer to the file object */ FSIZE_t fsz, /* File size to be expanded to */ BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ ) { FRESULT res; FATFS *fs; DWORD n, clst, stcl, scl, ncl, tcl, lclst; res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); #if _FS_EXFAT if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ #endif n = (DWORD)fs->csize * SS(fs); /* Cluster size */ tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ stcl = fs->last_clst; lclst = 0; if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; if (res == FR_OK) { if (opt) { res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ lclst = scl + tcl - 1; } else { lclst = scl - 1; } } } else #endif { scl = clst = stcl; ncl = 0; for (;;) { /* Find a contiguous cluster block */ n = get_fat(&fp->obj, clst); if (++clst >= fs->n_fatent) clst = 2; if (n == 1) { res = FR_INT_ERR; break; } if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } if (n == 0) { /* Is it a free cluster? */ if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ } else { scl = clst; ncl = 0; /* Not a free cluster */ } if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ } if (res == FR_OK) { if (opt) { for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); if (res != FR_OK) break; lclst = clst; } } else { lclst = scl - 1; } } } if (res == FR_OK) { fs->last_clst = lclst; /* Set suggested start cluster to start next */ if (opt) { fp->obj.sclust = scl; /* Update object allocation information */ fp->obj.objsize = fsz; if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ fp->flag |= FA_MODIFIED; if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ fs->free_clst -= tcl; fs->fsi_flag |= 1; } } } LEAVE_FF(fs, res); } #endif /* _USE_EXPAND && !_FS_READONLY */ #if _USE_FORWARD /*-----------------------------------------------------------------------*/ /* Forward data to the stream directly */ /*-----------------------------------------------------------------------*/ FRESULT f_forward ( FIL* fp, /* Pointer to the file object */ UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ UINT btf, /* Number of bytes to forward */ UINT* bf /* Pointer to number of bytes forwarded */ ) { FRESULT res; FATFS *fs; DWORD clst, sect; FSIZE_t remain; UINT rcnt, csect; BYTE *dbuf; *bf = 0; /* Clear transfer byte counter */ res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ remain = fp->obj.objsize - fp->fptr; if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ if (csect == 0) { /* On the cluster boundary? */ clst = (fp->fptr == 0) ? /* On the top of the file? */ fp->obj.sclust : get_fat(&fp->obj, fp->clust); if (clst <= 1) ABORT(fs, FR_INT_ERR); if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); fp->clust = clst; /* Update current cluster */ } } sect = clust2sect(fs, fp->clust); /* Get current data sector */ if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; #if _FS_TINY if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ dbuf = fs->win; #else if (fp->sect != sect) { /* Fill sector cache with file data */ #if !_FS_READONLY if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA_DIRTY; } #endif if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); } dbuf = fp->buf; #endif fp->sect = sect; rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ if (!rcnt) ABORT(fs, FR_INT_ERR); } LEAVE_FF(fs, FR_OK); } #endif /* _USE_FORWARD */ #if _USE_MKFS && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Create FAT file system on the logical drive */ /*-----------------------------------------------------------------------*/ FRESULT f_mkfs ( FATFS *fs, BYTE opt, /* Format option */ DWORD au, /* Size of allocation unit [byte] */ void* work, /* Pointer to working buffer */ UINT len /* Size of working buffer */ ) { const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */ const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */ static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */ static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ BYTE fmt, sys, *buf, *pte, part; void *pdrv; WORD ss; DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ UINT i; DSTATUS stat; #if _USE_TRIM || _FS_EXFAT DWORD tbl[3]; #endif /* Check mounted drive and clear work area */ fs->fs_type = 0; /* Clear mounted volume */ pdrv = fs->drv; /* Physical drive */ part = LD2PT(fs); /* Partition (0:create as new, 1-4:get from partition table) */ /* Check physical drive status */ disk_ioctl(pdrv, IOCTL_INIT, &stat); if (stat & STA_NOINIT) return FR_NOT_READY; if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ #if _MAX_SS != _MIN_SS /* Get sector size of the medium */ if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; #else ss = _MAX_SS; #endif if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ au /= ss; /* Cluster size in unit of sector */ /* Get working buffer */ buf = (BYTE*)work; /* Working buffer */ sz_buf = len / ss; /* Size of working buffer (sector) */ szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ if (!szb_buf) return FR_MKFS_ABORTED; /* Determine where the volume to be located (b_vol, sz_vol) */ if (_MULTI_PARTITION && part != 0) { /* Get partition information from partition table in the MBR */ if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */ pte = buf + (MBR_Table + (part - 1) * SZ_PTE); if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ } else { /* Create a single-partition in this function */ if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ if (sz_vol < b_vol) return FR_MKFS_ABORTED; sz_vol -= b_vol; /* Volume size */ } if (sz_vol < 50) return FR_MKFS_ABORTED; /* Check if volume size is >=50s */ /* Pre-determine the FAT type */ do { if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ fmt = FS_EXFAT; break; } } if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ if (opt & FM_FAT32) { /* FAT32 possible? */ if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ fmt = FS_FAT32; break; } } if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ fmt = FS_FAT16; } while (0); #if _FS_EXFAT if (fmt == FS_EXFAT) { /* Create an exFAT volume */ DWORD szb_bit, szb_case, sum, nb, cl; WCHAR ch, si; UINT j, st; BYTE b; if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ #if _USE_TRIM tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ disk_ioctl(pdrv, CTRL_TRIM, tbl); #endif /* Determine FAT location, data location and number of clusters */ if (!au) { /* au auto-selection */ au = 8; if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ } b_fat = b_vol + 32; /* FAT start at offset 32 */ sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ /* Create a compressed up-case table */ sect = b_data + au * tbl[0]; /* Table start sector */ sum = 0; /* Table checksum to be stored in the 82 entry */ st = si = i = j = szb_case = 0; do { switch (st) { case 0: ch = ff_wtoupper(si); /* Get an up-case char */ if (ch != si) { si++; break; /* Store the up-case char if exist */ } for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ if (j >= 128) { ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ } st = 1; /* Do not compress short run */ /* continue */ case 1: ch = si++; /* Fill the short run */ if (--j == 0) st = 0; break; default: ch = (WCHAR)j; si += j; /* Number of chars to skip */ st = 0; } sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); i += 2; szb_case += 2; if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */ n = (i + ss - 1) / ss; if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; sect += n; i = 0; } } while (si); tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ tbl[2] = 1; /* Number of root dir clusters */ /* Initialize the allocation bitmap */ sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ do { mem_set(buf, 0, szb_buf); for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; sect += n; nsect -= n; } while (nsect); /* Initialize the FAT */ sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ j = nb = cl = 0; do { mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ if (cl == 0) { /* Set entry 0 and 1 */ st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; } do { /* Create chains of bitmap, up-case and root dir */ while (nb && i < szb_buf) { /* Create a chain */ st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); i += 4; cl++; nb--; } if (!nb && j < 3) nb = tbl[j++]; /* Next chain */ } while (nb && i < szb_buf); n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; sect += n; nsect -= n; } while (nsect); /* Initialize the root directory */ mem_set(buf, 0, szb_buf); buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ st_dword(buf + SZDIRE * 1 + 20, 2); st_dword(buf + SZDIRE * 1 + 24, szb_bit); buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ st_dword(buf + SZDIRE * 2 + 4, sum); st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); st_dword(buf + SZDIRE * 2 + 24, szb_case); sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ do { /* Fill root directory sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; mem_set(buf, 0, ss); sect += n; nsect -= n; } while (nsect); /* Create two set of the exFAT VBR blocks */ sect = b_vol; for (n = 0; n < 2; n++) { /* Main record (+0) */ mem_set(buf, 0, ss); mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ st_word(buf + BPB_FSVerEx, 0x100); /* File system version (1.00) */ for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ buf[BPB_NumFATsEx] = 1; /* Number of FATs */ buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ for (i = sum = 0; i < ss; i++) { /* VBR checksum */ if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); } if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; /* Extended bootstrap record (+1..+8) */ mem_set(buf, 0, ss); st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ for (j = 1; j < 9; j++) { for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; } /* OEM/Reserved record (+9..+10) */ mem_set(buf, 0, ss); for ( ; j < 11; j++) { for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; } /* Sum record (+11) */ for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; } } else #endif /* _FS_EXFAT */ { /* Create an FAT12/16/32 volume */ do { pau = au; /* Pre-determine number of clusters and FAT sub-type */ if (fmt == FS_FAT32) { /* FAT32 volume */ if (!pau) { /* au auto-selection */ n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ } n_clst = sz_vol / pau; /* Number of clusters */ sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ sz_rsv = 32; /* Number of reserved sectors */ sz_dir = 0; /* No static directory */ if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; } else { /* FAT12/16 volume */ if (!pau) { /* au auto-selection */ n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ } n_clst = sz_vol / pau; if (n_clst > MAX_FAT12) { n = n_clst * 2 + 4; /* FAT size [byte] */ } else { fmt = FS_FAT12; n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ } sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ sz_rsv = 1; /* Number of reserved sectors */ sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ } b_fat = b_vol + sz_rsv; /* FAT base */ b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ /* Align data base to erase block boundary (for flash memory media) */ n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ sz_rsv += n; b_fat += n; } else { /* FAT12/16: Expand FAT size */ sz_fat += n / n_fats; } /* Determine number of clusters and final check of validity of the FAT sub-type */ if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; if (fmt == FS_FAT32) { if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ return FR_MKFS_ABORTED; } } if (fmt == FS_FAT16) { if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ if (!au && (pau * 2) <= 64) { au = pau * 2; continue; /* Adjust cluster size and retry */ } if ((opt & FM_FAT32)) { fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ } if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ return FR_MKFS_ABORTED; } if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ return FR_MKFS_ABORTED; } } if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ /* Ok, it is the valid cluster configuration */ break; } while (1); #if _USE_TRIM tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ disk_ioctl(pdrv, CTRL_TRIM, tbl); #endif /* Create FAT VBR */ mem_set(buf, 0, ss); mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ if (sz_vol < 0x10000) { st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ } else { st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ } buf[BPB_Media] = 0xF8; /* Media descriptor byte */ st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ if (fmt == FS_FAT32) { st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig32] = 0x29; /* Extended boot signature */ mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ } else { st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig] = 0x29; /* Extended boot signature */ mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ } st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ /* Create FSINFO record if needed */ if (fmt == FS_FAT32) { disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ mem_set(buf, 0, ss); st_dword(buf + FSI_LeadSig, 0x41615252); st_dword(buf + FSI_StrucSig, 0x61417272); st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ st_word(buf + BS_55AA, 0xAA55); disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ } /* Initialize FAT area */ mem_set(buf, 0, (UINT)szb_buf); sect = b_fat; /* FAT start sector */ for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ if (fmt == FS_FAT32) { st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ } else { st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ } nsect = sz_fat; /* Number of FAT sectors */ do { /* Fill FAT sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; mem_set(buf, 0, ss); sect += n; nsect -= n; } while (nsect); } /* Initialize root directory (fill with zero) */ nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ do { n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; sect += n; nsect -= n; } while (nsect); } /* Determine system ID in the partition table */ if (_FS_EXFAT && fmt == FS_EXFAT) { sys = 0x07; /* HPFS/NTFS/exFAT */ } else { if (fmt == FS_FAT32) { sys = 0x0C; /* FAT32X */ } else { if (sz_vol >= 0x10000) { sys = 0x06; /* FAT12/16 (>=64KS) */ } else { sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */ } } } if (_MULTI_PARTITION && part != 0) { /* Update system ID in the partition table */ if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system type */ if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ } else { if (!(opt & FM_SFD)) { /* Create partition table in FDISK format */ mem_set(buf, 0, ss); st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ pte[PTE_Boot] = 0; /* Boot indicator */ pte[PTE_StHead] = 1; /* Start head */ pte[PTE_StSec] = 1; /* Start sector */ pte[PTE_StCyl] = 0; /* Start cylinder */ pte[PTE_System] = sys; /* System type */ n = (b_vol + sz_vol) / (63 * 255); /* (End CHS is incorrect) */ pte[PTE_EdHead] = 254; /* End head */ pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ } } if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; return FR_OK; } #if _MULTI_PARTITION /*-----------------------------------------------------------------------*/ /* Create partition table on the physical drive */ /*-----------------------------------------------------------------------*/ FRESULT f_fdisk ( void *pdrv, /* Physical drive number */ const DWORD* szt, /* Pointer to the size table for each partitions */ void* work /* Pointer to the working buffer */ ) { UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; DSTATUS stat; DWORD sz_disk, sz_part, s_part; disk_ioctl(pdrv, IOCTL_INIT, &stat); if (stat & STA_NOINIT) return FR_NOT_READY; if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; /* Determine the CHS without any care of the drive geometry */ for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; if (n == 256) n--; e_hd = n - 1; sz_cyl = 63 * n; tot_cyl = sz_disk / sz_cyl; /* Create partition table */ mem_set(buf, 0, _MAX_SS); p = buf + MBR_Table; b_cyl = 0; for (i = 0; i < 4; i++, p += SZ_PTE) { p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; if (!p_cyl) continue; s_part = (DWORD)sz_cyl * b_cyl; sz_part = (DWORD)sz_cyl * p_cyl; if (i == 0) { /* Exclude first track of cylinder 0 */ s_hd = 1; s_part += 63; sz_part -= 63; } else { s_hd = 0; } e_cyl = b_cyl + p_cyl - 1; if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; /* Set partition table */ p[1] = s_hd; /* Start head */ p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ p[3] = (BYTE)b_cyl; /* Start cylinder */ p[4] = 0x06; /* System type (temporary setting) */ p[5] = e_hd; /* End head */ p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ p[7] = (BYTE)e_cyl; /* End cylinder */ st_dword(p + 8, s_part); /* Start sector in LBA */ st_dword(p + 12, sz_part); /* Partition size */ /* Next partition */ b_cyl += p_cyl; } st_word(p, 0xAA55); /* Write it to the MBR */ return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; } #endif /* _MULTI_PARTITION */ #endif /* _USE_MKFS && !_FS_READONLY */ ================================================ FILE: lib/oofatfs/ff.h ================================================ /* This file is part of ooFatFs, a customised version of FatFs * See https://github.com/micropython/oofatfs for details */ /*----------------------------------------------------------------------------/ / FatFs - Generic FAT file system module R0.12b / /-----------------------------------------------------------------------------/ / / Copyright (C) 2016, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided / that the following condition is met: / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. / / This software is provided by the copyright holder and contributors "AS IS" / and any warranties related to this software are DISCLAIMED. / The copyright owner or contributors be NOT LIABLE for any damages caused / by use of this software. /----------------------------------------------------------------------------*/ #ifndef _FATFS #define _FATFS 68020 /* Revision ID */ #ifdef __cplusplus extern "C" { #endif #include /* This type MUST be 8-bit */ typedef uint8_t BYTE; /* These types MUST be 16-bit */ typedef int16_t SHORT; typedef uint16_t WORD; typedef uint16_t WCHAR; /* These types MUST be 16-bit or 32-bit */ typedef int INT; typedef unsigned int UINT; /* These types MUST be 32-bit */ typedef int32_t LONG; typedef uint32_t DWORD; /* This type MUST be 64-bit (Remove this for C89 compatibility) */ typedef uint64_t QWORD; #include FFCONF_H /* FatFs configuration options */ #if _FATFS != _FFCONF #error Wrong configuration file (ffconf.h). #endif /* Definitions of volume management */ #if _MULTI_PARTITION /* Multiple partition configuration */ #define LD2PT(fs) (fs->part) /* Get partition index */ #else /* Single partition configuration */ #define LD2PT(fs) 0 /* Find first valid partition or in SFD */ #endif /* Type of path name strings on FatFs API */ #if _LFN_UNICODE /* Unicode (UTF-16) string */ #if _USE_LFN == 0 #error _LFN_UNICODE must be 0 at non-LFN cfg. #endif #ifndef _INC_TCHAR typedef WCHAR TCHAR; #define _T(x) L ## x #define _TEXT(x) L ## x #endif #else /* ANSI/OEM string */ #ifndef _INC_TCHAR typedef char TCHAR; #define _T(x) x #define _TEXT(x) x #endif #endif /* Type of file size variables */ #if _FS_EXFAT #if _USE_LFN == 0 #error LFN must be enabled when enable exFAT #endif typedef QWORD FSIZE_t; #else typedef DWORD FSIZE_t; #endif /* File system object structure (FATFS) */ typedef struct { void *drv; // block device underlying this filesystem #if _MULTI_PARTITION /* Multiple partition configuration */ BYTE part; // Partition: 0:Auto detect, 1-4:Forced partition #endif BYTE fs_type; /* File system type (0:N/A) */ BYTE n_fats; /* Number of FATs (1 or 2) */ BYTE wflag; /* win[] flag (b0:dirty) */ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ WORD id; /* File system mount ID */ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ WORD csize; /* Cluster size [sectors] */ #if _MAX_SS != _MIN_SS WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ #endif #if _USE_LFN != 0 WCHAR* lfnbuf; /* LFN working buffer */ #endif #if _FS_EXFAT BYTE* dirbuf; /* Directory entry block scratchpad buffer */ #endif #if _FS_REENTRANT _SYNC_t sobj; /* Identifier of sync object */ #endif #if !_FS_READONLY DWORD last_clst; /* Last allocated cluster */ DWORD free_clst; /* Number of free clusters */ #endif #if _FS_RPATH != 0 DWORD cdir; /* Current directory start cluster (0:root) */ #if _FS_EXFAT DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ #endif #endif DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ DWORD fsize; /* Size of an FAT [sectors] */ DWORD volbase; /* Volume base sector */ DWORD fatbase; /* FAT base sector */ DWORD dirbase; /* Root directory base sector/cluster */ DWORD database; /* Data base sector */ DWORD winsect; /* Current sector appearing in the win[] */ BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ } FATFS; /* Object ID and allocation information (_FDID) */ typedef struct { FATFS* fs; /* Pointer to the owner file system object */ WORD id; /* Owner file system mount ID */ BYTE attr; /* Object attribute */ BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */ DWORD sclust; /* Object start cluster (0:no cluster or root directory) */ FSIZE_t objsize; /* Object size (valid when sclust != 0) */ #if _FS_EXFAT DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */ DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */ #endif #if _FS_LOCK != 0 UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ #endif } _FDID; /* File object structure (FIL) */ typedef struct { _FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ BYTE flag; /* File status flags */ BYTE err; /* Abort flag (error code) */ FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */ DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ #if !_FS_READONLY DWORD dir_sect; /* Sector number containing the directory entry */ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ #endif #if _USE_FASTSEEK DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ #endif #if !_FS_TINY BYTE buf[_MAX_SS]; /* File private data read/write window */ #endif } FIL; /* Directory object structure (FF_DIR) */ typedef struct { _FDID obj; /* Object identifier */ DWORD dptr; /* Current read/write offset */ DWORD clust; /* Current cluster */ DWORD sect; /* Current sector */ BYTE* dir; /* Pointer to the directory item in the win[] */ BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ #if _USE_LFN != 0 DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ #endif #if _USE_FIND const TCHAR* pat; /* Pointer to the name matching pattern */ #endif } FF_DIR; /* File information structure (FILINFO) */ typedef struct { FSIZE_t fsize; /* File size */ WORD fdate; /* Modified date */ WORD ftime; /* Modified time */ BYTE fattrib; /* File attribute */ #if _USE_LFN != 0 TCHAR altname[13]; /* Altenative file name */ TCHAR fname[_MAX_LFN + 1]; /* Primary file name */ #else TCHAR fname[13]; /* File name */ #endif } FILINFO; /* File function return code (FRESULT) */ typedef enum { FR_OK = 0, /* (0) Succeeded */ FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ FR_INT_ERR, /* (2) Assertion failed */ FR_NOT_READY, /* (3) The physical drive cannot work */ FR_NO_FILE, /* (4) Could not find the file */ FR_NO_PATH, /* (5) Could not find the path */ FR_INVALID_NAME, /* (6) The path name format is invalid */ FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ FR_EXIST, /* (8) Access denied due to prohibited access */ FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ FR_NOT_ENABLED, /* (12) The volume has no work area */ FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ } FRESULT; /*--------------------------------------------------------------*/ /* FatFs module application interface */ FRESULT f_open (FATFS *fs, FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ FRESULT f_close (FIL* fp); /* Close an open file object */ FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ FRESULT f_truncate (FIL* fp); /* Truncate the file */ FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ FRESULT f_opendir (FATFS *fs, FF_DIR* dp, const TCHAR* path); /* Open a directory */ FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */ FRESULT f_readdir (FF_DIR* dp, FILINFO* fno); /* Read a directory item */ FRESULT f_findfirst (FF_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ FRESULT f_findnext (FF_DIR* dp, FILINFO* fno); /* Find next file */ FRESULT f_mkdir (FATFS *fs, const TCHAR* path); /* Create a sub directory */ FRESULT f_unlink (FATFS *fs, const TCHAR* path); /* Delete an existing file or directory */ FRESULT f_rename (FATFS *fs, const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ FRESULT f_stat (FATFS *fs, const TCHAR* path, FILINFO* fno); /* Get file status */ FRESULT f_chmod (FATFS *fs, const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ FRESULT f_utime (FATFS *fs, const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ FRESULT f_chdir (FATFS *fs, const TCHAR* path); /* Change current directory */ FRESULT f_getcwd (FATFS *fs, TCHAR* buff, UINT len); /* Get current directory */ FRESULT f_getfree (FATFS *fs, DWORD* nclst); /* Get number of free clusters on the drive */ FRESULT f_getlabel (FATFS *fs, TCHAR* label, DWORD* vsn); /* Get volume label */ FRESULT f_setlabel (FATFS *fs, const TCHAR* label); /* Set volume label */ FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ FRESULT f_mount (FATFS* fs); /* Mount/Unmount a logical drive */ FRESULT f_umount (FATFS* fs); /* Unmount a logical drive */ FRESULT f_mkfs (FATFS *fs, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ FRESULT f_fdisk (void *pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) #define f_error(fp) ((fp)->err) #define f_tell(fp) ((fp)->fptr) #define f_size(fp) ((fp)->obj.objsize) #define f_rewind(fp) f_lseek((fp), 0) #define f_rewinddir(dp) f_readdir((dp), 0) #ifndef EOF #define EOF (-1) #endif /*--------------------------------------------------------------*/ /* Additional user defined functions */ /* RTC function */ #if !_FS_READONLY && !_FS_NORTC DWORD get_fattime (void); #endif /* Unicode support functions */ #if _USE_LFN != 0 /* Unicode - OEM code conversion */ WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ #if _USE_LFN == 3 /* Memory functions */ void* ff_memalloc (UINT msize); /* Allocate memory block */ void ff_memfree (void* mblock); /* Free memory block */ #endif #endif /* Sync functions */ #if _FS_REENTRANT int ff_cre_syncobj (FATFS *fatfs, _SYNC_t* sobj); /* Create a sync object */ int ff_req_grant (_SYNC_t sobj); /* Lock sync object */ void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */ int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ #endif /*--------------------------------------------------------------*/ /* Flags and offset address */ /* File access mode and open method flags (3rd argument of f_open) */ #define FA_READ 0x01 #define FA_WRITE 0x02 #define FA_OPEN_EXISTING 0x00 #define FA_CREATE_NEW 0x04 #define FA_CREATE_ALWAYS 0x08 #define FA_OPEN_ALWAYS 0x10 #define FA_OPEN_APPEND 0x30 /* Fast seek controls (2nd argument of f_lseek) */ #define CREATE_LINKMAP ((FSIZE_t)0 - 1) /* Format options (2nd argument of f_mkfs) */ #define FM_FAT 0x01 #define FM_FAT32 0x02 #define FM_EXFAT 0x04 #define FM_ANY 0x07 #define FM_SFD 0x08 /* Filesystem type (FATFS.fs_type) */ #define FS_FAT12 1 #define FS_FAT16 2 #define FS_FAT32 3 #define FS_EXFAT 4 /* File attribute bits for directory entry (FILINFO.fattrib) */ #define AM_RDO 0x01 /* Read only */ #define AM_HID 0x02 /* Hidden */ #define AM_SYS 0x04 /* System */ #define AM_DIR 0x10 /* Directory */ #define AM_ARC 0x20 /* Archive */ #ifdef __cplusplus } #endif #endif /* _FATFS */ ================================================ FILE: lib/oofatfs/ffconf.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * Original file from: * FatFs - FAT file system module configuration file R0.12a (C)ChaN, 2016 * * 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/mpconfig.h" /*---------------------------------------------------------------------------/ / FatFs - FAT file system module configuration file /---------------------------------------------------------------------------*/ #define _FFCONF 68020 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations /---------------------------------------------------------------------------*/ #define _FS_READONLY 0 /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) / Read-only configuration removes writing API functions, f_write(), f_sync(), / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() / and optional writing functions as well. */ #define _FS_MINIMIZE 0 /* This option defines minimization level to remove some basic API functions. / / 0: All basic functions are enabled. / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() / are removed. / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. / 3: f_lseek() function is removed in addition to 2. */ #define _USE_STRFUNC 0 /* This option switches string functions, f_gets(), f_putc(), f_puts() and / f_printf(). / / 0: Disable string functions. / 1: Enable without LF-CRLF conversion. / 2: Enable with LF-CRLF conversion. */ #define _USE_FIND 0 /* This option switches filtered directory read functions, f_findfirst() and / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ #define _USE_MKFS 1 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ #define _USE_FASTSEEK 0 /* This option switches fast seek function. (0:Disable or 1:Enable) */ #define _USE_EXPAND 0 /* This option switches f_expand function. (0:Disable or 1:Enable) */ #define _USE_CHMOD 1 /* This option switches attribute manipulation functions, f_chmod() and f_utime(). / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ #ifdef MICROPY_FATFS_USE_LABEL #define _USE_LABEL (MICROPY_FATFS_USE_LABEL) #else #define _USE_LABEL 0 #endif /* This option switches volume label functions, f_getlabel() and f_setlabel(). / (0:Disable or 1:Enable) */ #define _USE_FORWARD 0 /* This option switches f_forward() function. (0:Disable or 1:Enable) */ /*---------------------------------------------------------------------------/ / Locale and Namespace Configurations /---------------------------------------------------------------------------*/ #ifdef MICROPY_FATFS_LFN_CODE_PAGE #define _CODE_PAGE (MICROPY_FATFS_LFN_CODE_PAGE) #else #define _CODE_PAGE 1 #endif /* This option specifies the OEM code page to be used on the target system. / Incorrect setting of the code page can cause a file open failure. / / 1 - ASCII (No extended character. Non-LFN cfg. only) / 437 - U.S. / 720 - Arabic / 737 - Greek / 771 - KBL / 775 - Baltic / 850 - Latin 1 / 852 - Latin 2 / 855 - Cyrillic / 857 - Turkish / 860 - Portuguese / 861 - Icelandic / 862 - Hebrew / 863 - Canadian French / 864 - Arabic / 865 - Nordic / 866 - Russian / 869 - Greek 2 / 932 - Japanese (DBCS) / 936 - Simplified Chinese (DBCS) / 949 - Korean (DBCS) / 950 - Traditional Chinese (DBCS) */ #ifdef MICROPY_FATFS_ENABLE_LFN #define _USE_LFN (MICROPY_FATFS_ENABLE_LFN) #else #define _USE_LFN 0 #endif #ifdef MICROPY_FATFS_MAX_LFN #define _MAX_LFN (MICROPY_FATFS_MAX_LFN) #else #define _MAX_LFN 255 #endif /* The _USE_LFN switches the support of long file name (LFN). / / 0: Disable support of LFN. _MAX_LFN has no effect. / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / 2: Enable LFN with dynamic working buffer on the STACK. / 3: Enable LFN with dynamic working buffer on the HEAP. / / To enable the LFN, Unicode handling functions (option/unicode.c) must be added / to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and / additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. / It should be set 255 to support full featured LFN operations. / When use stack for the working buffer, take care on stack overflow. When use heap / memory for the working buffer, memory management functions, ff_memalloc() and / ff_memfree(), must be added to the project. */ #define _LFN_UNICODE 0 /* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) / To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. / This option also affects behavior of string I/O functions. */ #define _STRF_ENCODE 3 /* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). / / 0: ANSI/OEM / 1: UTF-16LE / 2: UTF-16BE / 3: UTF-8 / / This option has no effect when _LFN_UNICODE == 0. */ #ifdef MICROPY_FATFS_RPATH #define _FS_RPATH (MICROPY_FATFS_RPATH) #else #define _FS_RPATH 0 #endif /* This option configures support of relative path. / / 0: Disable relative path and remove related functions. / 1: Enable relative path. f_chdir() and f_chdrive() are available. / 2: f_getcwd() function is available in addition to 1. */ /*---------------------------------------------------------------------------/ / Drive/Volume Configurations /---------------------------------------------------------------------------*/ #define _VOLUMES 1 /* Number of volumes (logical drives) to be used. */ #define _STR_VOLUME_ID 0 #define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" /* _STR_VOLUME_ID switches string support of volume ID. / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / number in the path name. _VOLUME_STRS defines the drive ID strings for each / logical drives. Number of items must be equal to _VOLUMES. Valid characters for / the drive ID strings are: A-Z and 0-9. */ #ifdef MICROPY_FATFS_MULTI_PARTITION #define _MULTI_PARTITION (MICROPY_FATFS_MULTI_PARTITION) #else #define _MULTI_PARTITION 0 #endif /* This option switches support of multi-partition on a physical drive. / By default (0), each logical drive number is bound to the same physical drive / number and only an FAT volume found on the physical drive will be mounted. / When multi-partition is enabled (1), each logical drive number can be bound to / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / funciton will be available. */ #define _MIN_SS 512 #ifdef MICROPY_FATFS_MAX_SS #define _MAX_SS (MICROPY_FATFS_MAX_SS) #else #define _MAX_SS 512 #endif /* These options configure the range of sector size to be supported. (512, 1024, / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and / harddisk. But a larger value may be required for on-board flash memory and some / type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured / to variable sector size and GET_SECTOR_SIZE command must be implemented to the / disk_ioctl() function. */ #define _USE_TRIM 0 /* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) / To enable Trim function, also CTRL_TRIM command should be implemented to the / disk_ioctl() function. */ #define _FS_NOFSINFO 0 /* If you need to know correct free space on the FAT32 volume, set bit 0 of this / option, and f_getfree() function at first time after volume mount will force / a full FAT scan. Bit 1 controls the use of last allocated cluster number. / / bit0=0: Use free cluster count in the FSINFO if available. / bit0=1: Do not trust free cluster count in the FSINFO. / bit1=0: Use last allocated cluster number in the FSINFO if available. / bit1=1: Do not trust last allocated cluster number in the FSINFO. */ /*---------------------------------------------------------------------------/ / System Configurations /---------------------------------------------------------------------------*/ #define _FS_TINY 1 /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) / At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. / Instead of private sector buffer eliminated from the file object, common sector / buffer in the file system object (FATFS) is used for the file data transfer. */ #ifdef MICROPY_FATFS_EXFAT #define _FS_EXFAT (MICROPY_FATFS_EXFAT) #else #define _FS_EXFAT 0 #endif /* This option switches support of exFAT file system. (0:Disable or 1:Enable) / When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) / Note that enabling exFAT discards C89 compatibility. */ #ifdef MICROPY_FATFS_NORTC #define _FS_NORTC (MICROPY_FATFS_NORTC) #else #define _FS_NORTC 0 #endif #define _NORTC_MON 1 #define _NORTC_MDAY 1 #define _NORTC_YEAR 2016 /* The option _FS_NORTC switches timestamp functiton. If the system does not have / any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable / the timestamp function. All objects modified by FatFs will have a fixed timestamp / defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. / To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be / added to the project to get current time form real-time clock. _NORTC_MON, / _NORTC_MDAY and _NORTC_YEAR have no effect. / These options have no effect at read-only configuration (_FS_READONLY = 1). */ #define _FS_LOCK 0 /* The option _FS_LOCK switches file lock function to control duplicated file open / and illegal operation to open objects. This option must be 0 when _FS_READONLY / is 1. / / 0: Disable file lock function. To avoid volume corruption, application program / should avoid illegal open, remove and rename to the open objects. / >0: Enable file lock function. The value defines how many files/sub-directories / can be opened simultaneously under file lock control. Note that the file / lock control is independent of re-entrancy. */ #ifdef MICROPY_FATFS_REENTRANT #define _FS_REENTRANT (MICROPY_FATFS_REENTRANT) #else #define _FS_REENTRANT 0 #endif // milliseconds #ifdef MICROPY_FATFS_TIMEOUT #define _FS_TIMEOUT (MICROPY_FATFS_TIMEOUT) #else #define _FS_TIMEOUT 1000 #endif #ifdef MICROPY_FATFS_SYNC_T #define _SYNC_t MICROPY_FATFS_SYNC_T #else #define _SYNC_t HANDLE #endif /* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() / and f_fdisk() function, are always not re-entrant. Only file/directory access / to the same volume is under control of this function. / / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. / 1: Enable re-entrancy. Also user provided synchronization handlers, / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() / function, must be added to the project. Samples are available in / option/syscall.c. / / The _FS_TIMEOUT defines timeout period in unit of time tick. / The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, / SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be / included somewhere in the scope of ff.h. */ /* #include // O/S definitions */ /*--- End of configuration options ---*/ ================================================ FILE: lib/oofatfs/option/ccsbcs.c ================================================ /*------------------------------------------------------------------------*/ /* Unicode - Local code bidirectional converter (C)ChaN, 2015 */ /* (SBCS code pages) */ /*------------------------------------------------------------------------*/ /* 437 U.S. / 720 Arabic / 737 Greek / 771 KBL / 775 Baltic / 850 Latin 1 / 852 Latin 2 / 855 Cyrillic / 857 Turkish / 860 Portuguese / 861 Icelandic / 862 Hebrew / 863 Canadian French / 864 Arabic / 865 Nordic / 866 Russian / 869 Greek 2 */ #include "../ff.h" #if _CODE_PAGE == 437 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 720 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 737 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 771 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP771(0x80-0xFF) to Unicode conversion table */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 775 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 850 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 852 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 855 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 857 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 860 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP860(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 861 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP861(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 862 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 863 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP863(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0, 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 864 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP864(0x80-0xFF) to Unicode conversion table */ 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000, 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000 }; #elif _CODE_PAGE == 865 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP865(0x80-0xFF) to Unicode conversion table */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 866 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 }; #elif _CODE_PAGE == 869 #define _TBLDEF 1 static const WCHAR Tbl[] = { /* CP869(0x80-0xFF) to Unicode conversion table */ 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0 }; #endif #if !_TBLDEF || !_USE_LFN #error This file is not needed at current configuration. Remove from the project. #endif WCHAR ff_convert ( /* Converted character, Returns zero on error */ WCHAR chr, /* Character code to be converted */ UINT dir /* 0: Unicode to OEM code, 1: OEM code to Unicode */ ) { WCHAR c; if (chr < 0x80) { /* ASCII */ c = chr; } else { if (dir) { /* OEM code to Unicode */ c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80]; } else { /* Unicode to OEM code */ for (c = 0; c < 0x80; c++) { if (chr == Tbl[c]) break; } c = (c + 0x80) & 0xFF; } } return c; } WCHAR ff_wtoupper ( /* Returns upper converted character */ WCHAR chr /* Unicode character to be upper converted (BMP only) */ ) { /* Compressed upper conversion table */ static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ /* Basic Latin */ 0x0061,0x031A, /* Latin-1 Supplement */ 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, /* Latin Extended-A */ 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, /* Latin Extended-B */ 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, /* IPA Extensions */ 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, /* Greek, Coptic */ 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, /* Cyrillic */ 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, /* Armenian */ 0x0561,0x0426, 0x0000 }; static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ /* Phonetic Extensions */ 0x1D7D,0x0001,0x2C63, /* Latin Extended Additional */ 0x1E00,0x0196, 0x1EA0,0x015A, /* Greek Extended */ 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, /* Letterlike Symbols */ 0x214E,0x0001,0x2132, /* Number forms */ 0x2170,0x0210, 0x2184,0x0001,0x2183, /* Enclosed Alphanumerics */ 0x24D0,0x051A, 0x2C30,0x042F, /* Latin Extended-C */ 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, /* Coptic */ 0x2C80,0x0164, /* Georgian Supplement */ 0x2D00,0x0826, /* Full-width */ 0xFF41,0x031A, 0x0000 }; const WCHAR *p; WCHAR bc, nc, cmd; p = chr < 0x1000 ? cvt1 : cvt2; for (;;) { bc = *p++; /* Get block base */ if (!bc || chr < bc) break; nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ if (chr < bc + nc) { /* In the block? */ switch (cmd) { case 0: chr = p[chr - bc]; break; /* Table conversion */ case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ case 2: chr -= 16; break; /* Shift -16 */ case 3: chr -= 32; break; /* Shift -32 */ case 4: chr -= 48; break; /* Shift -48 */ case 5: chr -= 26; break; /* Shift -26 */ case 6: chr += 8; break; /* Shift +8 */ case 7: chr -= 80; break; /* Shift -80 */ case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ } break; } if (!cmd) p += nc; } return chr; } ================================================ FILE: lib/oofatfs/option/unicode.c ================================================ #include "../ff.h" #if _USE_LFN != 0 #if _CODE_PAGE == 932 /* Japanese Shift_JIS */ #include "cc932.c" #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ #include "cc936.c" #elif _CODE_PAGE == 949 /* Korean */ #include "cc949.c" #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ #include "cc950.c" #else /* Single Byte Character-Set */ #include "ccsbcs.c" #endif #endif ================================================ FILE: lib/timeutils/timeutils.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2015 Daniel Campora * * 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 "lib/timeutils/timeutils.h" // LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately // after Feb 29. We calculate seconds as a signed integer relative to that. // // Our timebase is relative to 2000-01-01. #define LEAPOCH ((31 + 29) * 86400) #define DAYS_PER_400Y (365*400 + 97) #define DAYS_PER_100Y (365*100 + 24) #define DAYS_PER_4Y (365*4 + 1) STATIC const uint16_t days_since_jan1[]= { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; bool timeutils_is_leap_year(mp_uint_t year) { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; } // month is one based mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month) { mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1]; if (month == 2 && timeutils_is_leap_year(year)) { mdays++; } return mdays; } // compute the day of the year, between 1 and 366 // month should be between 1 and 12, date should start at 1 mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { mp_uint_t yday = days_since_jan1[month - 1] + date; if (month >= 3 && timeutils_is_leap_year(year)) { yday += 1; } return yday; } void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm) { // The following algorithm was adapted from musl's __secs_to_tm and adapted // for differences in MicroPython's timebase. mp_int_t seconds = t - LEAPOCH; mp_int_t days = seconds / 86400; seconds %= 86400; if (seconds < 0) { seconds += 86400; days -= 1; } tm->tm_hour = seconds / 3600; tm->tm_min = seconds / 60 % 60; tm->tm_sec = seconds % 60; mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2) if (wday < 0) { wday += 7; } tm->tm_wday = wday; mp_int_t qc_cycles = days / DAYS_PER_400Y; days %= DAYS_PER_400Y; if (days < 0) { days += DAYS_PER_400Y; qc_cycles--; } mp_int_t c_cycles = days / DAYS_PER_100Y; if (c_cycles == 4) { c_cycles--; } days -= (c_cycles * DAYS_PER_100Y); mp_int_t q_cycles = days / DAYS_PER_4Y; if (q_cycles == 25) { q_cycles--; } days -= q_cycles * DAYS_PER_4Y; mp_int_t years = days / 365; if (years == 4) { years--; } days -= (years * 365); /* We will compute tm_yday at the very end mp_int_t leap = !years && (q_cycles || !c_cycles); tm->tm_yday = days + 31 + 28 + leap; if (tm->tm_yday >= 365 + leap) { tm->tm_yday -= 365 + leap; } tm->tm_yday++; // Make one based */ tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; // Note: days_in_month[0] corresponds to March STATIC const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; mp_int_t month; for (month = 0; days_in_month[month] <= days; month++) { days -= days_in_month[month]; } tm->tm_mon = month + 2; if (tm->tm_mon >= 12) { tm->tm_mon -= 12; tm->tm_year++; } tm->tm_mday = days + 1; // Make one based tm->tm_mon++; // Make one based tm->tm_yday = timeutils_year_day(tm->tm_year, tm->tm_mon, tm->tm_mday); } // returns the number of seconds, as an integer, since 2000-01-01 mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { return second + minute * 60 + hour * 3600 + (timeutils_year_day(year, month, date) - 1 + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001 - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 ) * 86400 + (year - 2000) * 31536000; } mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { // Normalize the tuple. This allows things like: // // tm_tomorrow = list(time.localtime()) // tm_tomorrow[2] += 1 # Adds 1 to mday // tomorrow = time.mktime(tm_tomorrow) // // And not have to worry about all the weird overflows. // // You can subtract dates/times this way as well. minutes += seconds / 60; if ((seconds = seconds % 60) < 0) { seconds += 60; minutes--; } hours += minutes / 60; if ((minutes = minutes % 60) < 0) { minutes += 60; hours--; } mday += hours / 24; if ((hours = hours % 24) < 0) { hours += 24; mday--; } month--; // make month zero based year += month / 12; if ((month = month % 12) < 0) { month += 12; year--; } month++; // back to one based while (mday < 1) { if (--month == 0) { month = 12; year--; } mday += timeutils_days_in_month(year, month); } while ((mp_uint_t)mday > timeutils_days_in_month(year, month)) { mday -= timeutils_days_in_month(year, month); if (++month == 13) { month = 1; year++; } } return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds); } ================================================ FILE: lib/timeutils/timeutils.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2015 Daniel Campora * * 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_TIMEUTILS_TIMEUTILS_H #define MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H typedef struct _timeutils_struct_time_t { uint16_t tm_year; // i.e. 2014 uint8_t tm_mon; // 1..12 uint8_t tm_mday; // 1..31 uint8_t tm_hour; // 0..23 uint8_t tm_min; // 0..59 uint8_t tm_sec; // 0..59 uint8_t tm_wday; // 0..6 0 = Monday uint16_t tm_yday; // 1..366 } timeutils_struct_time_t; bool timeutils_is_leap_year(mp_uint_t year); mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month); mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date); void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm); mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second); mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds); #endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H ================================================ FILE: lib/utils/gchelper.h ================================================ /* * 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]; #endif #endif void gc_helper_collect_regs_and_stack(void); #endif // MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H ================================================ FILE: lib/utils/gchelper_m0.s ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 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. */ .syntax unified .cpu cortex-m0 .thumb .section .text .align 2 .global gc_helper_get_regs_and_sp .type gc_helper_get_regs_and_sp, %function @ uint gc_helper_get_regs_and_sp(r0=uint regs[10]) gc_helper_get_regs_and_sp: @ store registers into given array str r4, [r0, #0] str r5, [r0, #4] str r6, [r0, #8] str r7, [r0, #12] mov r1, r8 str r1, [r0, #16] mov r1, r9 str r1, [r0, #20] mov r1, r10 str r1, [r0, #24] mov r1, r11 str r1, [r0, #28] mov r1, r12 str r1, [r0, #32] mov r1, r13 str r1, [r0, #36] @ return the sp mov r0, sp bx lr .size gc_helper_get_regs_and_sp, .-gc_helper_get_regs_and_sp ================================================ FILE: lib/utils/gchelper_m3.s ================================================ /* * 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. */ .syntax unified .cpu cortex-m3 .thumb .section .text .align 2 .global gc_helper_get_regs_and_sp .type gc_helper_get_regs_and_sp, %function @ uint gc_helper_get_regs_and_sp(r0=uint regs[10]) gc_helper_get_regs_and_sp: @ store registers into given array str r4, [r0], #4 str r5, [r0], #4 str r6, [r0], #4 str r7, [r0], #4 str r8, [r0], #4 str r9, [r0], #4 str r10, [r0], #4 str r11, [r0], #4 str r12, [r0], #4 str r13, [r0], #4 @ return the sp mov r0, sp bx lr .size gc_helper_get_regs_and_sp, .-gc_helper_get_regs_and_sp ================================================ FILE: lib/utils/interrupt_char.c ================================================ /* * 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 "py/obj.h" #include "py/mpstate.h" #if MICROPY_KBD_EXCEPTION int mp_interrupt_char = -1; void mp_hal_set_interrupt_char(int c) { mp_interrupt_char = c; } #endif ================================================ FILE: lib/utils/interrupt_char.h ================================================ /* * 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_LIB_UTILS_INTERRUPT_CHAR_H #define MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H extern int mp_interrupt_char; void mp_hal_set_interrupt_char(int c); #endif // MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H ================================================ FILE: lib/utils/mpirq.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2015 Daniel Campora * 2018 Tobias Badertscher * * 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" #include "py/gc.h" #include "lib/utils/mpirq.h" #if MICROPY_ENABLE_SCHEDULER /****************************************************************************** DECLARE PUBLIC DATA ******************************************************************************/ const mp_arg_t mp_irq_init_args[] = { { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_trigger, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, }; /****************************************************************************** DECLARE PRIVATE DATA ******************************************************************************/ /****************************************************************************** DEFINE PUBLIC FUNCTIONS ******************************************************************************/ mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) { mp_irq_obj_t *self = m_new0(mp_irq_obj_t, 1); mp_irq_init(self, methods, parent); return self; } void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent) { self->base.type = &mp_irq_type; self->methods = (mp_irq_methods_t *)methods; self->parent = parent; self->handler = mp_const_none; self->ishard = false; } void mp_irq_handler(mp_irq_obj_t *self) { if (self->handler != mp_const_none) { if (self->ishard) { // When executing code within a handler we must lock the scheduler to // prevent any scheduled callbacks from running, and lock the GC to // prevent any memory allocations. mp_sched_lock(); gc_lock(); nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_call_function_1(self->handler, self->parent); nlr_pop(); } else { // Uncaught exception; disable the callback so that it doesn't run again self->methods->trigger(self->parent, 0); self->handler = mp_const_none; printf("Uncaught exception in IRQ callback handler\n"); mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } gc_unlock(); mp_sched_unlock(); } else { // Schedule call to user function mp_sched_schedule(self->handler, self->parent); } } } /******************************************************************************/ // MicroPython bindings STATIC mp_obj_t mp_irq_flags(mp_obj_t self_in) { mp_irq_obj_t *self = MP_OBJ_TO_PTR(self_in); return mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_FLAGS)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_irq_flags_obj, mp_irq_flags); STATIC mp_obj_t mp_irq_trigger(size_t n_args, const mp_obj_t *args) { mp_irq_obj_t *self = MP_OBJ_TO_PTR(args[0]); mp_obj_t ret_obj = mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_TRIGGERS)); if (n_args == 2) { // Set trigger self->methods->trigger(self->parent, mp_obj_get_int(args[1])); } return ret_obj; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_irq_trigger_obj, 1, 2, mp_irq_trigger); STATIC mp_obj_t mp_irq_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, 0, false); mp_irq_handler(MP_OBJ_TO_PTR(self_in)); return mp_const_none; } STATIC const mp_rom_map_elem_t mp_irq_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_flags), MP_ROM_PTR(&mp_irq_flags_obj) }, { MP_ROM_QSTR(MP_QSTR_trigger), MP_ROM_PTR(&mp_irq_trigger_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_irq_locals_dict, mp_irq_locals_dict_table); const mp_obj_type_t mp_irq_type = { { &mp_type_type }, .name = MP_QSTR_irq, .call = mp_irq_call, .locals_dict = (mp_obj_dict_t *)&mp_irq_locals_dict, }; #endif // MICROPY_ENABLE_SCHEDULER ================================================ FILE: lib/utils/mpirq.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2015 Daniel Campora * * 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_MPIRQ_H #define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H #include "py/runtime.h" /****************************************************************************** DEFINE CONSTANTS ******************************************************************************/ enum { MP_IRQ_ARG_INIT_handler = 0, MP_IRQ_ARG_INIT_trigger, MP_IRQ_ARG_INIT_hard, MP_IRQ_ARG_INIT_NUM_ARGS, }; /****************************************************************************** DEFINE TYPES ******************************************************************************/ typedef mp_uint_t (*mp_irq_trigger_fun_t)(mp_obj_t self, mp_uint_t trigger); typedef mp_uint_t (*mp_irq_info_fun_t)(mp_obj_t self, mp_uint_t info_type); enum { MP_IRQ_INFO_FLAGS, MP_IRQ_INFO_TRIGGERS, }; typedef struct _mp_irq_methods_t { mp_irq_trigger_fun_t trigger; mp_irq_info_fun_t info; } mp_irq_methods_t; typedef struct _mp_irq_obj_t { mp_obj_base_t base; mp_irq_methods_t *methods; mp_obj_t parent; mp_obj_t handler; bool ishard; } mp_irq_obj_t; /****************************************************************************** DECLARE EXPORTED DATA ******************************************************************************/ extern const mp_arg_t mp_irq_init_args[]; extern const mp_obj_type_t mp_irq_type; /****************************************************************************** DECLARE PUBLIC FUNCTIONS ******************************************************************************/ mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent); void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent); void mp_irq_handler(mp_irq_obj_t *self); #endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H ================================================ FILE: lib/utils/printf.c ================================================ /* * 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 #include #include #include "py/obj.h" #include "py/mphal.h" #if MICROPY_PY_BUILTINS_FLOAT #include "py/formatfloat.h" #endif #if MICROPY_DEBUG_PRINTERS int DEBUG_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); int ret = mp_vprintf(MICROPY_DEBUG_PRINTER, fmt, ap); va_end(ap); return ret; } #endif #if MICROPY_USE_INTERNAL_PRINTF #undef putchar // Some stdlibs have a #define for putchar int printf(const char *fmt, ...); int vprintf(const char *fmt, va_list ap); int putchar(int c); int puts(const char *s); int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); int snprintf(char *str, size_t size, const char *fmt, ...); int printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); int ret = mp_vprintf(&mp_plat_print, fmt, ap); va_end(ap); return ret; } int vprintf(const char *fmt, va_list ap) { return mp_vprintf(&mp_plat_print, fmt, ap); } // need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a') int putchar(int c) { char chr = c; mp_hal_stdout_tx_strn_cooked(&chr, 1); return chr; } // need this because gcc optimises printf("string\n") -> puts("string") int puts(const char *s) { mp_hal_stdout_tx_strn_cooked(s, strlen(s)); char chr = '\n'; mp_hal_stdout_tx_strn_cooked(&chr, 1); return 1; } typedef struct _strn_print_env_t { char *cur; size_t remain; } strn_print_env_t; STATIC void strn_print_strn(void *data, const char *str, size_t len) { strn_print_env_t *strn_print_env = data; if (len > strn_print_env->remain) { len = strn_print_env->remain; } memcpy(strn_print_env->cur, str, len); strn_print_env->cur += len; strn_print_env->remain -= len; } #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 9 // uClibc requires this alias to be defined, or there may be link errors // when linkings against it statically. // GCC 9 gives a warning about missing attributes so it's excluded until // uClibc+GCC9 support is needed. int __GI_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) __attribute__((weak, alias("vsnprintf"))); #endif int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { strn_print_env_t strn_print_env = {str, size}; mp_print_t print = {&strn_print_env, strn_print_strn}; int len = mp_vprintf(&print, fmt, ap); // add terminating null byte if (size > 0) { if (strn_print_env.remain == 0) { strn_print_env.cur[-1] = 0; } else { strn_print_env.cur[0] = 0; } } return len; } int snprintf(char *str, size_t size, const char *fmt, ...) { va_list ap; va_start(ap, fmt); int ret = vsnprintf(str, size, fmt, ap); va_end(ap); return ret; } #endif // MICROPY_USE_INTERNAL_PRINTF ================================================ FILE: lib/utils/pyexec.c ================================================ /* * 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/compile.h" #include "py/runtime.h" #include "py/repl.h" #include "py/gc.h" #include "py/frozenmod.h" #include "py/mphal.h" #if MICROPY_HW_ENABLE_USB #include "irq.h" #include "usb.h" #endif #include "lib/mp-readline/readline.h" #include "lib/utils/pyexec.h" #include "genhdr/mpversion.h" pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; int pyexec_system_exit = 0; #if MICROPY_REPL_INFO STATIC bool repl_display_debugging_info = 0; #endif #define EXEC_FLAG_PRINT_EOF (1) #define EXEC_FLAG_ALLOW_DEBUGGING (2) #define EXEC_FLAG_IS_REPL (4) #define EXEC_FLAG_RERAISE (8) #define EXEC_FLAG_SOURCE_IS_RAW_CODE (16) #define EXEC_FLAG_SOURCE_IS_VSTR (32) #define EXEC_FLAG_SOURCE_IS_FILENAME (64) // parses, compiles and executes the code in the lexer // frees the lexer before returning // EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output // EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code // EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile) STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input_kind, int exec_flags) { int ret = 0; #if MICROPY_REPL_INFO uint32_t start = 0; #endif // by default a SystemExit exception returns 0 pyexec_system_exit = 0; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t module_fun; #if MICROPY_MODULE_FROZEN_MPY if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { // source is a raw_code object, create the function module_fun = mp_make_function_from_raw_code(source, MP_OBJ_NULL, MP_OBJ_NULL); } else #endif { #if MICROPY_ENABLE_COMPILER mp_lexer_t *lex; if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { const vstr_t *vstr = source; lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { lex = mp_lexer_new_from_file(source); } else { lex = (mp_lexer_t *)source; } // source is a lexer, parse and compile the script qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); #else mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); #endif } // execute code mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us #if MICROPY_REPL_INFO start = mp_hal_ticks_ms(); #endif mp_call_function_0(module_fun); mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(true); // handle any pending exceptions (and any callbacks) nlr_pop(); ret = 1; if (exec_flags & EXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } } else { // uncaught exception mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(false); // clear any pending exceptions (and run any callbacks) // print EOF after normal output if (exec_flags & EXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } // check for SystemExit if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { // at the moment, the value of SystemExit is unused ret = pyexec_system_exit; } else { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); ret = 0; } } #if MICROPY_REPL_INFO // display debugging info if wanted if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) { mp_uint_t ticks = mp_hal_ticks_ms() - start; // TODO implement a function that does this properly printf("took " UINT_FMT " ms\n", ticks); // qstr info { 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); printf("qstr:\n n_pool=%u\n n_qstr=%u\n " "n_str_data_bytes=%u\n n_total_bytes=%u\n", (unsigned)n_pool, (unsigned)n_qstr, (unsigned)n_str_data_bytes, (unsigned)n_total_bytes); } #if MICROPY_ENABLE_GC // run collection and print GC info gc_collect(); gc_dump_info(); #endif } #endif if (exec_flags & EXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } return ret; } #if MICROPY_ENABLE_COMPILER #if MICROPY_REPL_EVENT_DRIVEN typedef struct _repl_t { // This structure originally also held current REPL line, // but it was moved to MP_STATE_VM(repl_line) as containing // root pointer. Still keep structure in case more state // will be added later. // vstr_t line; bool cont_line; bool paste_mode; } repl_t; repl_t repl; STATIC int pyexec_raw_repl_process_char(int c); STATIC int pyexec_friendly_repl_process_char(int c); void pyexec_event_repl_init(void) { MP_STATE_VM(repl_line) = vstr_new(32); repl.cont_line = false; repl.paste_mode = false; // no prompt before printing friendly REPL banner or entering raw REPL readline_init(MP_STATE_VM(repl_line), ""); if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { pyexec_raw_repl_process_char(CHAR_CTRL_A); } else { pyexec_friendly_repl_process_char(CHAR_CTRL_B); } } STATIC int pyexec_raw_repl_process_char(int c) { if (c == CHAR_CTRL_A) { // reset raw REPL mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n"); goto reset; } else if (c == CHAR_CTRL_B) { // change to friendly REPL pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; vstr_reset(MP_STATE_VM(repl_line)); repl.cont_line = false; repl.paste_mode = false; pyexec_friendly_repl_process_char(CHAR_CTRL_B); return 0; } else if (c == CHAR_CTRL_C) { // clear line vstr_reset(MP_STATE_VM(repl_line)); return 0; } else if (c == CHAR_CTRL_D) { // input finished } else { // let through any other raw 8-bit value vstr_add_byte(MP_STATE_VM(repl_line), c); return 0; } // indicate reception of command mp_hal_stdout_tx_str("OK"); if (MP_STATE_VM(repl_line)->len == 0) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(MP_STATE_VM(repl_line)); return PYEXEC_FORCED_EXIT; } int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } reset: vstr_reset(MP_STATE_VM(repl_line)); mp_hal_stdout_tx_str(">"); return 0; } STATIC int pyexec_friendly_repl_process_char(int c) { if (repl.paste_mode) { if (c == CHAR_CTRL_C) { // cancel everything mp_hal_stdout_tx_str("\r\n"); goto input_restart; } else if (c == CHAR_CTRL_D) { // end of input mp_hal_stdout_tx_str("\r\n"); int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } goto input_restart; } else { // add char to buffer and echo vstr_add_byte(MP_STATE_VM(repl_line), c); if (c == '\r') { mp_hal_stdout_tx_str("\r\n=== "); } else { char buf[1] = {c}; mp_hal_stdout_tx_strn(buf, 1); } return 0; } } int ret = readline_process_char(c); if (!repl.cont_line) { if (ret == CHAR_CTRL_A) { // change to raw REPL pyexec_mode_kind = PYEXEC_MODE_RAW_REPL; mp_hal_stdout_tx_str("\r\n"); pyexec_raw_repl_process_char(CHAR_CTRL_A); return 0; } else if (ret == CHAR_CTRL_B) { // reset friendly REPL mp_hal_stdout_tx_str("\r\n"); mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n"); #if MICROPY_PY_BUILTINS_HELP mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); #endif goto input_restart; } else if (ret == CHAR_CTRL_C) { // break mp_hal_stdout_tx_str("\r\n"); goto input_restart; } else if (ret == CHAR_CTRL_D) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(MP_STATE_VM(repl_line)); return PYEXEC_FORCED_EXIT; } else if (ret == CHAR_CTRL_E) { // paste mode mp_hal_stdout_tx_str("\r\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\r\n=== "); vstr_reset(MP_STATE_VM(repl_line)); repl.paste_mode = true; return 0; } if (ret < 0) { return 0; } if (!mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) { goto exec; } vstr_add_byte(MP_STATE_VM(repl_line), '\n'); repl.cont_line = true; readline_note_newline("... "); return 0; } else { if (ret == CHAR_CTRL_C) { // cancel everything mp_hal_stdout_tx_str("\r\n"); repl.cont_line = false; goto input_restart; } else if (ret == CHAR_CTRL_D) { // stop entering compound statement goto exec; } if (ret < 0) { return 0; } if (mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) { vstr_add_byte(MP_STATE_VM(repl_line), '\n'); readline_note_newline("... "); return 0; } exec:; int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } input_restart: vstr_reset(MP_STATE_VM(repl_line)); repl.cont_line = false; repl.paste_mode = false; readline_init(MP_STATE_VM(repl_line), ">>> "); return 0; } } uint8_t pyexec_repl_active; int pyexec_event_repl_process_char(int c) { pyexec_repl_active = 1; int res; if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { res = pyexec_raw_repl_process_char(c); } else { res = pyexec_friendly_repl_process_char(c); } pyexec_repl_active = 0; return res; } #else // MICROPY_REPL_EVENT_DRIVEN int pyexec_raw_repl(void) { vstr_t line; vstr_init(&line, 32); raw_repl_reset: mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n"); for (;;) { vstr_reset(&line); mp_hal_stdout_tx_str(">"); for (;;) { int c = mp_hal_stdin_rx_chr(); if (c == CHAR_CTRL_A) { // reset raw REPL goto raw_repl_reset; } else if (c == CHAR_CTRL_B) { // change to friendly REPL mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; return 0; } else if (c == CHAR_CTRL_C) { // clear line vstr_reset(&line); } else if (c == CHAR_CTRL_D) { // input finished break; } else { // let through any other raw 8-bit value vstr_add_byte(&line, c); } } // indicate reception of command mp_hal_stdout_tx_str("OK"); if (line.len == 0) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); return PYEXEC_FORCED_EXIT; } int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } } } int pyexec_friendly_repl(void) { vstr_t line; vstr_init(&line, 32); #if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD // in host mode, we enable the LCD for the repl mp_obj_t lcd_o = mp_call_function_0(mp_load_name(qstr_from_str("LCD"))); mp_call_function_1(mp_load_attr(lcd_o, qstr_from_str("light")), mp_const_true); #endif friendly_repl_reset: mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n"); #if MICROPY_PY_BUILTINS_HELP mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); #endif // to test ctrl-C /* { uint32_t x[4] = {0x424242, 0xdeaddead, 0x242424, 0xdeadbeef}; for (;;) { nlr_buf_t nlr; printf("pyexec_repl: %p\n", x); mp_hal_set_interrupt_char(CHAR_CTRL_C); if (nlr_push(&nlr) == 0) { for (;;) { } } else { printf("break\n"); } } } */ for (;;) { input_restart: #if MICROPY_HW_ENABLE_USB if (usb_vcp_is_enabled()) { // If the user gets to here and interrupts are disabled then // they'll never see the prompt, traceback etc. The USB REPL needs // interrupts to be enabled or no transfers occur. So we try to // do the user a favor and reenable interrupts. if (query_irq() == IRQ_STATE_DISABLED) { enable_irq(IRQ_STATE_ENABLED); mp_hal_stdout_tx_str("MPY: enabling IRQs\r\n"); } } #endif // If the GC is locked at this point there is no way out except a reset, // so force the GC to be unlocked to help the user debug what went wrong. if (MP_STATE_MEM(gc_lock_depth) != 0) { MP_STATE_MEM(gc_lock_depth) = 0; } vstr_reset(&line); int ret = readline(&line, ">>> "); mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; if (ret == CHAR_CTRL_A) { // change to raw REPL mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_RAW_REPL; return 0; } else if (ret == CHAR_CTRL_B) { // reset friendly REPL mp_hal_stdout_tx_str("\r\n"); goto friendly_repl_reset; } else if (ret == CHAR_CTRL_C) { // break mp_hal_stdout_tx_str("\r\n"); continue; } else if (ret == CHAR_CTRL_D) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); return PYEXEC_FORCED_EXIT; } else if (ret == CHAR_CTRL_E) { // paste mode mp_hal_stdout_tx_str("\r\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\r\n=== "); vstr_reset(&line); for (;;) { char c = mp_hal_stdin_rx_chr(); if (c == CHAR_CTRL_C) { // cancel everything mp_hal_stdout_tx_str("\r\n"); goto input_restart; } else if (c == CHAR_CTRL_D) { // end of input mp_hal_stdout_tx_str("\r\n"); break; } else { // add char to buffer and echo vstr_add_byte(&line, c); if (c == '\r') { mp_hal_stdout_tx_str("\r\n=== "); } else { mp_hal_stdout_tx_strn(&c, 1); } } } parse_input_kind = MP_PARSE_FILE_INPUT; } else if (vstr_len(&line) == 0) { continue; } else { // got a line with non-zero length, see if it needs continuing while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { vstr_add_byte(&line, '\n'); ret = readline(&line, "... "); if (ret == CHAR_CTRL_C) { // cancel everything mp_hal_stdout_tx_str("\r\n"); goto input_restart; } else if (ret == CHAR_CTRL_D) { // stop entering compound statement break; } } } ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } } } #endif // MICROPY_REPL_EVENT_DRIVEN #endif // MICROPY_ENABLE_COMPILER int pyexec_file(const char *filename) { return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_FILENAME); } int pyexec_str(vstr_t *str) { return parse_compile_execute(str, MP_PARSE_FILE_INPUT, EXEC_FLAG_RERAISE | EXEC_FLAG_SOURCE_IS_VSTR); } int pyexec_file_if_exists(const char *filename) { #if MICROPY_MODULE_FROZEN if (mp_frozen_stat(filename) == MP_IMPORT_STAT_FILE) { return pyexec_frozen_module(filename); } #endif if (mp_import_stat(filename) != MP_IMPORT_STAT_FILE) { return 1; // success (no file is the same as an empty file executing without fail) } return pyexec_file(filename); } #if MICROPY_MODULE_FROZEN int pyexec_frozen_module(const char *name) { void *frozen_data; int frozen_type = mp_find_frozen_module(name, strlen(name), &frozen_data); switch (frozen_type) { #if MICROPY_MODULE_FROZEN_STR case MP_FROZEN_STR: return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, 0); #endif #if MICROPY_MODULE_FROZEN_MPY case MP_FROZEN_MPY: return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_RAW_CODE); #endif default: printf("could not find module '%s'\n", name); return false; } } #endif #if MICROPY_REPL_INFO mp_obj_t pyb_set_repl_info(mp_obj_t o_value) { repl_display_debugging_info = mp_obj_get_int(o_value); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj, pyb_set_repl_info); #endif ================================================ FILE: lib/utils/pyexec.h ================================================ /* * 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_LIB_UTILS_PYEXEC_H #define MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H #include "py/obj.h" typedef enum { PYEXEC_MODE_FRIENDLY_REPL, PYEXEC_MODE_RAW_REPL, } pyexec_mode_kind_t; extern pyexec_mode_kind_t pyexec_mode_kind; // Set this to the value (eg PYEXEC_FORCED_EXIT) that will be propagated through // the pyexec functions if a SystemExit exception is raised by the running code. // It will reset to 0 at the start of each execution (eg each REPL entry). extern int pyexec_system_exit; #define PYEXEC_FORCED_EXIT (0x100) #define PYEXEC_SWITCH_MODE (0x200) int pyexec_raw_repl(void); int pyexec_friendly_repl(void); int pyexec_file(const char *filename); int pyexec_file_if_exists(const char *filename); int pyexec_frozen_module(const char *name); void pyexec_event_repl_init(void); int pyexec_event_repl_process_char(int c); extern uint8_t pyexec_repl_active; #if MICROPY_REPL_INFO mp_obj_t pyb_set_repl_info(mp_obj_t o_value); MP_DECLARE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj); #endif #endif // MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H ================================================ FILE: lib/utils/stdout_helpers.c ================================================ #include #include #include "py/mpconfig.h" #include "py/mphal.h" /* * Extra stdout functions * These can be either optimized for a particular port, or reference * implementation below can be used. */ // Send "cooked" string of given length, where every occurrence of // LF character is replaced with CR LF. void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { while (len--) { if (*str == '\n') { mp_hal_stdout_tx_strn("\r", 1); } mp_hal_stdout_tx_strn(str++, 1); } } // Send zero-terminated string void mp_hal_stdout_tx_str(const char *str) { mp_hal_stdout_tx_strn(str, strlen(str)); } ================================================ FILE: lib/utils/sys_stdio_mphal.c ================================================ /* * 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 "py/obj.h" #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" // TODO make stdin, stdout and stderr writable objects so they can // be changed by Python code. This requires some changes, as these // objects are in a read-only module (py/modsys.c). /******************************************************************************/ // MicroPython bindings #define STDIO_FD_IN (0) #define STDIO_FD_OUT (1) #define STDIO_FD_ERR (2) typedef struct _sys_stdio_obj_t { mp_obj_base_t base; int fd; } sys_stdio_obj_t; #if MICROPY_PY_SYS_STDIO_BUFFER STATIC const sys_stdio_obj_t stdio_buffer_obj; #endif void stdio_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", self->fd); } STATIC mp_uint_t stdio_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->fd == STDIO_FD_IN) { for (uint i = 0; i < size; i++) { int c = mp_hal_stdin_rx_chr(); if (c == '\r') { c = '\n'; } ((byte *)buf)[i] = c; } return size; } else { *errcode = MP_EPERM; return MP_STREAM_ERROR; } } STATIC mp_uint_t stdio_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->fd == STDIO_FD_OUT || self->fd == STDIO_FD_ERR) { mp_hal_stdout_tx_strn_cooked(buf, size); return size; } else { *errcode = MP_EPERM; return MP_STREAM_ERROR; } } // TODO NOT IMPLEMENT STDIO_IOCTL ON RT-THREAD YET // STATIC mp_uint_t stdio_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { // (void)self_in; // if (request == MP_STREAM_POLL) { // return mp_hal_stdio_poll(arg); // } else { // *errcode = MP_EINVAL; // return MP_STREAM_ERROR; // } // } STATIC mp_obj_t stdio_obj___exit__(size_t n_args, const mp_obj_t *args) { return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stdio_obj___exit___obj, 4, 4, stdio_obj___exit__); // TODO gc hook to close the file if not already closed STATIC const mp_rom_map_elem_t stdio_locals_dict_table[] = { #if MICROPY_PY_SYS_STDIO_BUFFER { MP_ROM_QSTR(MP_QSTR_buffer), MP_ROM_PTR(&stdio_buffer_obj) }, #endif { 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_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj)}, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&stdio_obj___exit___obj) }, }; STATIC MP_DEFINE_CONST_DICT(stdio_locals_dict, stdio_locals_dict_table); STATIC const mp_stream_p_t stdio_obj_stream_p = { .read = stdio_read, .write = stdio_write, //.ioctl = stdio_ioctl, .is_text = true, }; STATIC const mp_obj_type_t stdio_obj_type = { { &mp_type_type }, .name = MP_QSTR_FileIO, // TODO .make_new? .print = stdio_obj_print, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &stdio_obj_stream_p, .locals_dict = (mp_obj_dict_t *)&stdio_locals_dict, }; const sys_stdio_obj_t mp_sys_stdin_obj = {{&stdio_obj_type}, .fd = STDIO_FD_IN}; const sys_stdio_obj_t mp_sys_stdout_obj = {{&stdio_obj_type}, .fd = STDIO_FD_OUT}; const sys_stdio_obj_t mp_sys_stderr_obj = {{&stdio_obj_type}, .fd = STDIO_FD_ERR}; #if MICROPY_PY_SYS_STDIO_BUFFER STATIC mp_uint_t stdio_buffer_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { for (uint i = 0; i < size; i++) { ((byte *)buf)[i] = mp_hal_stdin_rx_chr(); } return size; } STATIC mp_uint_t stdio_buffer_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { mp_hal_stdout_tx_strn(buf, size); return size; } STATIC const mp_stream_p_t stdio_buffer_obj_stream_p = { .read = stdio_buffer_read, .write = stdio_buffer_write, .ioctl = stdio_ioctl, .is_text = false, }; STATIC const mp_obj_type_t stdio_buffer_obj_type = { { &mp_type_type }, .name = MP_QSTR_FileIO, .print = stdio_obj_print, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &stdio_buffer_obj_stream_p, .locals_dict = (mp_obj_dict_t *)&stdio_locals_dict, }; STATIC const sys_stdio_obj_t stdio_buffer_obj = {{&stdio_buffer_obj_type}, .fd = 0}; // fd unused #endif ================================================ FILE: port/frozen_mpy.c ================================================ #include "py/mpconfig.h" #include "py/objint.h" #include "py/objstr.h" #include "py/emitglue.h" #include "py/nativeglue.h" #if MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE != 0 #error "incompatible MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE" #endif #if MICROPY_LONGINT_IMPL != 2 #error "incompatible MICROPY_LONGINT_IMPL" #endif #if MICROPY_PY_BUILTINS_FLOAT typedef struct _mp_obj_float_t { mp_obj_base_t base; mp_float_t value; } mp_obj_float_t; #endif #if MICROPY_PY_BUILTINS_COMPLEX typedef struct _mp_obj_complex_t { mp_obj_base_t base; mp_float_t real; mp_float_t imag; } mp_obj_complex_t; #endif enum { MP_QSTR_frozentest_dot_py = MP_QSTRnumber_of, MP_QSTR_uPy, //MP_QSTR_i, }; extern const qstr_pool_t mp_qstr_const_pool; const qstr_pool_t mp_qstr_frozen_const_pool = { (qstr_pool_t*)&mp_qstr_const_pool, // previous pool MP_QSTRnumber_of, // previous pool size 3, // allocated entries 3, // used entries { (const byte*)"\xfe\x0d" "frozentest.py", (const byte*)"\xf9\x03" "uPy", (const byte*)"\xcc\x01" "i", }, }; // frozen bytecode for file frozentest.py, scope frozentest_ STATIC const byte fun_data_frozentest__lt_module_gt_[85] = { 0x10, 0x16, MP_QSTR__lt_module_gt_ & 0xff, MP_QSTR__lt_module_gt_ >> 8, MP_QSTR_frozentest_dot_py & 0xff, MP_QSTR_frozentest_dot_py >> 8, 0x29, 0x28, 0x28, 0x28, 0x2b, 0x28, 0x00, 0x11, MP_QSTR_print & 0xff, MP_QSTR_print >> 8, 0x10, MP_QSTR_uPy & 0xff, MP_QSTR_uPy >> 8, 0x34, 0x01, 0x59, 0x11, MP_QSTR_print & 0xff, MP_QSTR_print >> 8, 0x23, 0x00, 0x34, 0x01, 0x59, 0x11, MP_QSTR_print & 0xff, MP_QSTR_print >> 8, 0x23, 0x01, 0x34, 0x01, 0x59, 0x11, MP_QSTR_print & 0xff, MP_QSTR_print >> 8, 0x23, 0x02, 0x34, 0x01, 0x59, 0x11, MP_QSTR_print & 0xff, MP_QSTR_print >> 8, 0x22, 0xba, 0xef, 0x9a, 0x15, 0x34, 0x01, 0x59, 0x80, 0x42, 0x0f, 0x80, 0x57, 0x16, MP_QSTR_i & 0xff, MP_QSTR_i >> 8, 0x11, MP_QSTR_print & 0xff, MP_QSTR_print >> 8, 0x11, MP_QSTR_i & 0xff, MP_QSTR_i >> 8, 0x34, 0x01, 0x59, 0x81, 0xe5, 0x57, 0x84, 0xd7, 0x43, 0xeb, 0x7f, 0x59, 0x51, 0x63, }; STATIC const mp_obj_str_t const_obj_frozentest__lt_module_gt__0 = {{&mp_type_str}, 246, 34, (const byte*)"\x61\x20\x6c\x6f\x6e\x67\x20\x73\x74\x72\x69\x6e\x67\x20\x74\x68\x61\x74\x20\x69\x73\x20\x6e\x6f\x74\x20\x69\x6e\x74\x65\x72\x6e\x65\x64"}; STATIC const mp_obj_str_t const_obj_frozentest__lt_module_gt__1 = {{&mp_type_str}, 200, 38, (const byte*)"\x61\x20\x73\x74\x72\x69\x6e\x67\x20\x74\x68\x61\x74\x20\x68\x61\x73\x20\x75\x6e\x69\x63\x6f\x64\x65\x20\xce\xb1\xce\xb2\xce\xb3\x20\x63\x68\x61\x72\x73"}; STATIC const mp_obj_str_t const_obj_frozentest__lt_module_gt__2 = {{&mp_type_bytes}, 57, 11, (const byte*)"\x62\x79\x74\x65\x73\x20\x31\x32\x33\x34\x01"}; STATIC const mp_rom_obj_t const_table_data_frozentest__lt_module_gt_[3] = { MP_ROM_PTR(&const_obj_frozentest__lt_module_gt__0), MP_ROM_PTR(&const_obj_frozentest__lt_module_gt__1), MP_ROM_PTR(&const_obj_frozentest__lt_module_gt__2), }; const mp_raw_code_t raw_code_frozentest__lt_module_gt_ = { .kind = MP_CODE_BYTECODE, .scope_flags = 0x00, .n_pos_args = 0, .fun_data = fun_data_frozentest__lt_module_gt_, .const_table = (mp_uint_t*)const_table_data_frozentest__lt_module_gt_, #if MICROPY_PERSISTENT_CODE_SAVE .fun_data_len = 85, .n_obj = 3, .n_raw_code = 0, #if MICROPY_PY_SYS_SETTRACE .prelude = { .n_state = 3, .n_exc_stack = 0, .scope_flags = 0, .n_pos_args = 0, .n_kwonly_args = 0, .n_def_pos_args = 0, .qstr_block_name = MP_QSTR__lt_module_gt_, .qstr_source_file = MP_QSTR_frozentest_dot_py, .line_info = fun_data_frozentest__lt_module_gt_ + 0, .opcodes = fun_data_frozentest__lt_module_gt_ + 13, }, .line_of_definition = 0, #endif #if MICROPY_EMIT_MACHINE_CODE .prelude_offset = 0, .n_qstr = 0, .qstr_link = NULL, #endif #endif #if MICROPY_EMIT_MACHINE_CODE .type_sig = 0, #endif }; const char mp_frozen_mpy_names[] = { "frozentest.py\0" "\0"}; const mp_raw_code_t *const mp_frozen_mpy_content[] = { &raw_code_frozentest__lt_module_gt_, }; ================================================ FILE: port/gccollect.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "py/mpstate.h" #include "py/gc.h" void gc_collect(void) { gc_collect_start(); #if MICROPY_PY_THREAD // trace root pointers from any threads mp_thread_gc_others(); #else // gc the main thread stack gc_collect_root(rt_thread_self()->stack_addr, ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)rt_thread_self()->stack_addr) / 4); #endif gc_collect_end(); // gc_dump_info(); } ================================================ FILE: port/genhdr/gen_qstr.py ================================================ # # This file is part of the MicroPython project, http://micropython.org/ # # The MIT License (MIT) # # Copyright (c) 2017 SummerGift # # 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. # from __future__ import print_function import re import sys import platform if platform.python_version_tuple()[0] == '2': bytes_cons = lambda val, enc=None: bytearray(val) from htmlentitydefs import codepoint2name elif platform.python_version_tuple()[0] == '3': bytes_cons = bytes from html.entities import codepoint2name # end compatibility code # this must match the equivalent function in qstr.c def compute_hash(qstr, bytes_hash): hash = 5381 for b in qstr: hash = (hash * 33) ^ b # Make sure that valid hash is never zero, zero means "hash not computed" return (hash & ((1 << (8 * bytes_hash)) - 1)) or 1 def make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr): #print("qstr:",qstr) #print("cfg_bytes_hash:",cfg_bytes_hash) #print("cfg_bytes_len:",cfg_bytes_len) qbytes = bytes_cons(qstr, 'utf8') #print("qbytes:",qbytes) qlen = len(qbytes) qhash = compute_hash(qbytes, cfg_bytes_hash) #print("qlen:",qlen) #print("cfg_bytes_hash:",cfg_bytes_hash) #print("qhash:",qhash) if all(32 <= ord(c) <= 126 and c != '\\' and c != '"' for c in qstr): # qstr is all printable ASCII so render it as-is (for easier debugging) qdata = qstr else: # qstr contains non-printable codes so render entire thing as hex pairs qdata = ''.join(('\\x%02x' % b) for b in qbytes) if qlen >= (1 << (8 * cfg_bytes_len)): print('qstr is too long:', qstr) assert False qlen_str = ('\\x%02x' * cfg_bytes_len) % tuple(((qlen >> (8 * i)) & 0xff) for i in range(cfg_bytes_len)) qhash_str = ('\\x%02x' * cfg_bytes_hash) % tuple(((qhash >> (8 * i)) & 0xff) for i in range(cfg_bytes_hash)) return 'QDEF(MP_QSTR_%s, (const byte*)"%s%s" "%s")' % (qdata,qhash_str, qlen_str, qdata) if __name__ == "__main__": print ("This program will gen qstr for micropython:") for arg in sys.argv: if arg == 'gen_qstr.py': continue qstr_computer = make_bytes(1, 1, arg) print (qstr_computer) ================================================ FILE: port/genhdr/moduledefs.h ================================================ ================================================ FILE: port/genhdr/mpversion.h ================================================ // This file was generated by py/makeversionhdr.py #define MICROPY_GIT_TAG "v1.13-148-ged7ddd4" #define MICROPY_GIT_HASH "ed7ddd4" #define MICROPY_BUILD_DATE "2020-11-03" ================================================ FILE: port/genhdr/qstrdefs.generated.h ================================================ // This file was automatically generated by makeqstrdata.py QDEF(MP_QSTRnull, (const byte*)"\x00\x00" "") QDEF(MP_QSTR_, (const byte*)"\x05\x00" "") QDEF(MP_QSTR___abs__, (const byte*)"\x95\x07" "__abs__") QDEF(MP_QSTR___add__, (const byte*)"\xc4\x07" "__add__") QDEF(MP_QSTR___and__, (const byte*)"\x0e\x07" "__and__") QDEF(MP_QSTR___bool__, (const byte*)"\x2b\x08" "__bool__") QDEF(MP_QSTR___build_class__, (const byte*)"\x42\x0f" "__build_class__") QDEF(MP_QSTR___call__, (const byte*)"\xa7\x08" "__call__") QDEF(MP_QSTR___class__, (const byte*)"\x2b\x09" "__class__") QDEF(MP_QSTR___contains__, (const byte*)"\xc6\x0c" "__contains__") QDEF(MP_QSTR___del__, (const byte*)"\x68\x07" "__del__") QDEF(MP_QSTR___delitem__, (const byte*)"\xfd\x0b" "__delitem__") QDEF(MP_QSTR___divmod__, (const byte*)"\x78\x0a" "__divmod__") QDEF(MP_QSTR___enter__, (const byte*)"\x6d\x09" "__enter__") QDEF(MP_QSTR___eq__, (const byte*)"\x71\x06" "__eq__") QDEF(MP_QSTR___exit__, (const byte*)"\x45\x08" "__exit__") QDEF(MP_QSTR___file__, (const byte*)"\x03\x08" "__file__") QDEF(MP_QSTR___floordiv__, (const byte*)"\x46\x0c" "__floordiv__") QDEF(MP_QSTR___ge__, (const byte*)"\xa7\x06" "__ge__") QDEF(MP_QSTR___getattr__, (const byte*)"\x40\x0b" "__getattr__") QDEF(MP_QSTR___getitem__, (const byte*)"\x26\x0b" "__getitem__") QDEF(MP_QSTR___gt__, (const byte*)"\xb6\x06" "__gt__") QDEF(MP_QSTR___hash__, (const byte*)"\xf7\x08" "__hash__") QDEF(MP_QSTR___iadd__, (const byte*)"\x6d\x08" "__iadd__") QDEF(MP_QSTR___import__, (const byte*)"\x38\x0a" "__import__") QDEF(MP_QSTR___init__, (const byte*)"\x5f\x08" "__init__") QDEF(MP_QSTR___invert__, (const byte*)"\xf7\x0a" "__invert__") QDEF(MP_QSTR___isub__, (const byte*)"\x08\x08" "__isub__") QDEF(MP_QSTR___iter__, (const byte*)"\xcf\x08" "__iter__") QDEF(MP_QSTR___le__, (const byte*)"\xcc\x06" "__le__") QDEF(MP_QSTR___len__, (const byte*)"\xe2\x07" "__len__") QDEF(MP_QSTR___lshift__, (const byte*)"\x09\x0a" "__lshift__") QDEF(MP_QSTR___lt__, (const byte*)"\x5d\x06" "__lt__") QDEF(MP_QSTR___main__, (const byte*)"\x8e\x08" "__main__") QDEF(MP_QSTR___mod__, (const byte*)"\x63\x07" "__mod__") QDEF(MP_QSTR___module__, (const byte*)"\xff\x0a" "__module__") QDEF(MP_QSTR___mul__, (const byte*)"\x31\x07" "__mul__") QDEF(MP_QSTR___name__, (const byte*)"\xe2\x08" "__name__") QDEF(MP_QSTR___neg__, (const byte*)"\x69\x07" "__neg__") QDEF(MP_QSTR___new__, (const byte*)"\x79\x07" "__new__") QDEF(MP_QSTR___next__, (const byte*)"\x02\x08" "__next__") QDEF(MP_QSTR___or__, (const byte*)"\x38\x06" "__or__") QDEF(MP_QSTR___path__, (const byte*)"\xc8\x08" "__path__") QDEF(MP_QSTR___pos__, (const byte*)"\x29\x07" "__pos__") QDEF(MP_QSTR___pow__, (const byte*)"\x2d\x07" "__pow__") QDEF(MP_QSTR___qualname__, (const byte*)"\x6b\x0c" "__qualname__") QDEF(MP_QSTR___repl_print__, (const byte*)"\x01\x0e" "__repl_print__") QDEF(MP_QSTR___repr__, (const byte*)"\x10\x08" "__repr__") QDEF(MP_QSTR___reversed__, (const byte*)"\x61\x0c" "__reversed__") QDEF(MP_QSTR___rshift__, (const byte*)"\x57\x0a" "__rshift__") QDEF(MP_QSTR___setitem__, (const byte*)"\x32\x0b" "__setitem__") QDEF(MP_QSTR___str__, (const byte*)"\xd0\x07" "__str__") QDEF(MP_QSTR___sub__, (const byte*)"\x21\x07" "__sub__") QDEF(MP_QSTR___traceback__, (const byte*)"\x4f\x0d" "__traceback__") QDEF(MP_QSTR___truediv__, (const byte*)"\x88\x0b" "__truediv__") QDEF(MP_QSTR___xor__, (const byte*)"\x20\x07" "__xor__") QDEF(MP_QSTR__star_, (const byte*)"\x8f\x01" "*") QDEF(MP_QSTR__, (const byte*)"\xfa\x01" "_") QDEF(MP_QSTR__slash_, (const byte*)"\x8a\x01" "/") QDEF(MP_QSTR__percent__hash_o, (const byte*)"\x6c\x03" "%#o") QDEF(MP_QSTR__percent__hash_x, (const byte*)"\x7b\x03" "%#x") QDEF(MP_QSTR__brace_open__colon__hash_b_brace_close_, (const byte*)"\x58\x05" "{:#b}") QDEF(MP_QSTR__0x0a_, (const byte*)"\xaf\x01" "\x0a") QDEF(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded, (const byte*)"\x73\x20" "maximum recursion depth exceeded") QDEF(MP_QSTR__lt_module_gt_, (const byte*)"\xbd\x08" "") QDEF(MP_QSTR__lt_lambda_gt_, (const byte*)"\x80\x08" "") QDEF(MP_QSTR__lt_listcomp_gt_, (const byte*)"\xd4\x0a" "") QDEF(MP_QSTR__lt_dictcomp_gt_, (const byte*)"\xcc\x0a" "") QDEF(MP_QSTR__lt_setcomp_gt_, (const byte*)"\x54\x09" "") QDEF(MP_QSTR__lt_genexpr_gt_, (const byte*)"\x34\x09" "") QDEF(MP_QSTR__lt_string_gt_, (const byte*)"\x52\x08" "") QDEF(MP_QSTR__lt_stdin_gt_, (const byte*)"\xe3\x07" "") QDEF(MP_QSTR_utf_hyphen_8, (const byte*)"\xb7\x05" "utf-8") QDEF(MP_QSTR_ALT_OD, (const byte*)"\x88\x06" "ALT_OD") QDEF(MP_QSTR_ALT_PP, (const byte*)"\xe3\x06" "ALT_PP") QDEF(MP_QSTR_ANALOG, (const byte*)"\xaf\x06" "ANALOG") QDEF(MP_QSTR_ARRAY, (const byte*)"\x5c\x05" "ARRAY") QDEF(MP_QSTR_ArithmeticError, (const byte*)"\x2d\x0f" "ArithmeticError") QDEF(MP_QSTR_AssertionError, (const byte*)"\x97\x0e" "AssertionError") QDEF(MP_QSTR_AttributeError, (const byte*)"\x21\x0e" "AttributeError") QDEF(MP_QSTR_BFINT16, (const byte*)"\x95\x07" "BFINT16") QDEF(MP_QSTR_BFINT32, (const byte*)"\x53\x07" "BFINT32") QDEF(MP_QSTR_BFINT8, (const byte*)"\x4a\x06" "BFINT8") QDEF(MP_QSTR_BFUINT16, (const byte*)"\x40\x08" "BFUINT16") QDEF(MP_QSTR_BFUINT32, (const byte*)"\x06\x08" "BFUINT32") QDEF(MP_QSTR_BFUINT8, (const byte*)"\xbf\x07" "BFUINT8") QDEF(MP_QSTR_BF_LEN, (const byte*)"\x19\x06" "BF_LEN") QDEF(MP_QSTR_BF_POS, (const byte*)"\x52\x06" "BF_POS") QDEF(MP_QSTR_BIG_ENDIAN, (const byte*)"\xff\x0a" "BIG_ENDIAN") QDEF(MP_QSTR_BaseException, (const byte*)"\x07\x0d" "BaseException") QDEF(MP_QSTR_BytesIO, (const byte*)"\x1a\x07" "BytesIO") QDEF(MP_QSTR_DEBUG, (const byte*)"\x34\x05" "DEBUG") QDEF(MP_QSTR_DEEPSLEEP_RESET, (const byte*)"\x14\x0f" "DEEPSLEEP_RESET") QDEF(MP_QSTR_DecompIO, (const byte*)"\x93\x08" "DecompIO") QDEF(MP_QSTR_EOFError, (const byte*)"\x91\x08" "EOFError") QDEF(MP_QSTR_Ellipsis, (const byte*)"\xf0\x08" "Ellipsis") QDEF(MP_QSTR_Exception, (const byte*)"\xf2\x09" "Exception") QDEF(MP_QSTR_FLOAT32, (const byte*)"\xb4\x07" "FLOAT32") QDEF(MP_QSTR_FLOAT64, (const byte*)"\x17\x07" "FLOAT64") QDEF(MP_QSTR_GeneratorExit, (const byte*)"\x16\x0d" "GeneratorExit") QDEF(MP_QSTR_HARD_RESET, (const byte*)"\xb0\x0a" "HARD_RESET") QDEF(MP_QSTR_IN, (const byte*)"\x22\x02" "IN") QDEF(MP_QSTR_INT16, (const byte*)"\x91\x05" "INT16") QDEF(MP_QSTR_INT32, (const byte*)"\x57\x05" "INT32") QDEF(MP_QSTR_INT64, (const byte*)"\xf4\x05" "INT64") QDEF(MP_QSTR_INT8, (const byte*)"\xce\x04" "INT8") QDEF(MP_QSTR_IRQ_FALLING, (const byte*)"\x37\x0b" "IRQ_FALLING") QDEF(MP_QSTR_IRQ_RISING, (const byte*)"\x78\x0a" "IRQ_RISING") QDEF(MP_QSTR_IRQ_RISING_FALLING, (const byte*)"\x60\x12" "IRQ_RISING_FALLING") QDEF(MP_QSTR_ImportError, (const byte*)"\x20\x0b" "ImportError") QDEF(MP_QSTR_IndentationError, (const byte*)"\x5c\x10" "IndentationError") QDEF(MP_QSTR_IndexError, (const byte*)"\x83\x0a" "IndexError") QDEF(MP_QSTR_KeyError, (const byte*)"\xea\x08" "KeyError") QDEF(MP_QSTR_KeyboardInterrupt, (const byte*)"\xaf\x11" "KeyboardInterrupt") QDEF(MP_QSTR_LITTLE_ENDIAN, (const byte*)"\xbf\x0d" "LITTLE_ENDIAN") QDEF(MP_QSTR_LookupError, (const byte*)"\xff\x0b" "LookupError") QDEF(MP_QSTR_MemoryError, (const byte*)"\xdc\x0b" "MemoryError") QDEF(MP_QSTR_NATIVE, (const byte*)"\x04\x06" "NATIVE") QDEF(MP_QSTR_NameError, (const byte*)"\xba\x09" "NameError") QDEF(MP_QSTR_NoneType, (const byte*)"\x17\x08" "NoneType") QDEF(MP_QSTR_NotImplementedError, (const byte*)"\xc6\x13" "NotImplementedError") QDEF(MP_QSTR_OSError, (const byte*)"\xa1\x07" "OSError") QDEF(MP_QSTR_OUT_OD, (const byte*)"\x1f\x06" "OUT_OD") QDEF(MP_QSTR_OUT_PP, (const byte*)"\x34\x06" "OUT_PP") QDEF(MP_QSTR_OrderedDict, (const byte*)"\xf0\x0b" "OrderedDict") QDEF(MP_QSTR_OverflowError, (const byte*)"\x81\x0d" "OverflowError") QDEF(MP_QSTR_PTR, (const byte*)"\xb3\x03" "PTR") QDEF(MP_QSTR_PULL_DOWN, (const byte*)"\xad\x09" "PULL_DOWN") QDEF(MP_QSTR_PULL_NONE, (const byte*)"\x55\x09" "PULL_NONE") QDEF(MP_QSTR_PULL_UP, (const byte*)"\xba\x07" "PULL_UP") QDEF(MP_QSTR_PWRON_RESET, (const byte*)"\xdb\x0b" "PWRON_RESET") QDEF(MP_QSTR_Pin, (const byte*)"\x12\x03" "Pin") QDEF(MP_QSTR_PinBase, (const byte*)"\x47\x07" "PinBase") QDEF(MP_QSTR_RuntimeError, (const byte*)"\x61\x0c" "RuntimeError") QDEF(MP_QSTR_SOFT_RESET, (const byte*)"\x01\x0a" "SOFT_RESET") QDEF(MP_QSTR_Signal, (const byte*)"\x9b\x06" "Signal") QDEF(MP_QSTR_StopIteration, (const byte*)"\xea\x0d" "StopIteration") QDEF(MP_QSTR_StringIO, (const byte*)"\x76\x08" "StringIO") QDEF(MP_QSTR_SyntaxError, (const byte*)"\x94\x0b" "SyntaxError") QDEF(MP_QSTR_SystemExit, (const byte*)"\x20\x0a" "SystemExit") QDEF(MP_QSTR_TypeError, (const byte*)"\x25\x09" "TypeError") QDEF(MP_QSTR_UINT16, (const byte*)"\xc4\x06" "UINT16") QDEF(MP_QSTR_UINT32, (const byte*)"\x82\x06" "UINT32") QDEF(MP_QSTR_UINT64, (const byte*)"\x61\x06" "UINT64") QDEF(MP_QSTR_UINT8, (const byte*)"\xbb\x05" "UINT8") QDEF(MP_QSTR_UnicodeError, (const byte*)"\x22\x0c" "UnicodeError") QDEF(MP_QSTR_VOID, (const byte*)"\x31\x04" "VOID") QDEF(MP_QSTR_ValueError, (const byte*)"\x96\x0a" "ValueError") QDEF(MP_QSTR_WDT_RESET, (const byte*)"\x08\x09" "WDT_RESET") QDEF(MP_QSTR_ZeroDivisionError, (const byte*)"\xb6\x11" "ZeroDivisionError") QDEF(MP_QSTR_a2b_base64, (const byte*)"\x3c\x0a" "a2b_base64") QDEF(MP_QSTR_abs, (const byte*)"\x95\x03" "abs") QDEF(MP_QSTR_acos, (const byte*)"\x1b\x04" "acos") QDEF(MP_QSTR_acosh, (const byte*)"\x13\x05" "acosh") QDEF(MP_QSTR_add, (const byte*)"\x44\x03" "add") QDEF(MP_QSTR_addressof, (const byte*)"\x5a\x09" "addressof") QDEF(MP_QSTR_all, (const byte*)"\x44\x03" "all") QDEF(MP_QSTR_any, (const byte*)"\x13\x03" "any") QDEF(MP_QSTR_append, (const byte*)"\x6b\x06" "append") QDEF(MP_QSTR_args, (const byte*)"\xc2\x04" "args") QDEF(MP_QSTR_array, (const byte*)"\x7c\x05" "array") QDEF(MP_QSTR_asin, (const byte*)"\x50\x04" "asin") QDEF(MP_QSTR_asinh, (const byte*)"\x38\x05" "asinh") QDEF(MP_QSTR_atan, (const byte*)"\x1f\x04" "atan") QDEF(MP_QSTR_atan2, (const byte*)"\xcd\x05" "atan2") QDEF(MP_QSTR_atanh, (const byte*)"\x97\x05" "atanh") QDEF(MP_QSTR_b2a_base64, (const byte*)"\x3c\x0a" "b2a_base64") QDEF(MP_QSTR_bin, (const byte*)"\xe0\x03" "bin") QDEF(MP_QSTR_bool, (const byte*)"\xeb\x04" "bool") QDEF(MP_QSTR_bound_method, (const byte*)"\x97\x0c" "bound_method") QDEF(MP_QSTR_builtins, (const byte*)"\xf7\x08" "builtins") QDEF(MP_QSTR_bytearray, (const byte*)"\x76\x09" "bytearray") QDEF(MP_QSTR_bytearray_at, (const byte*)"\x9c\x0c" "bytearray_at") QDEF(MP_QSTR_bytecode, (const byte*)"\x22\x08" "bytecode") QDEF(MP_QSTR_bytes, (const byte*)"\x5c\x05" "bytes") QDEF(MP_QSTR_bytes_at, (const byte*)"\xb6\x08" "bytes_at") QDEF(MP_QSTR_callable, (const byte*)"\x0d\x08" "callable") QDEF(MP_QSTR_ceil, (const byte*)"\x06\x04" "ceil") QDEF(MP_QSTR_center, (const byte*)"\x4e\x06" "center") QDEF(MP_QSTR_choice, (const byte*)"\x2e\x06" "choice") QDEF(MP_QSTR_chr, (const byte*)"\xdc\x03" "chr") QDEF(MP_QSTR_classmethod, (const byte*)"\xb4\x0b" "classmethod") QDEF(MP_QSTR_clear, (const byte*)"\x7c\x05" "clear") QDEF(MP_QSTR_close, (const byte*)"\x33\x05" "close") QDEF(MP_QSTR_closure, (const byte*)"\x74\x07" "closure") QDEF(MP_QSTR_cmath, (const byte*)"\xb6\x05" "cmath") QDEF(MP_QSTR_code, (const byte*)"\x68\x04" "code") QDEF(MP_QSTR_collect, (const byte*)"\x9b\x07" "collect") QDEF(MP_QSTR_compile, (const byte*)"\xf4\x07" "compile") QDEF(MP_QSTR_complex, (const byte*)"\xc5\x07" "complex") QDEF(MP_QSTR_const, (const byte*)"\xc0\x05" "const") QDEF(MP_QSTR_copy, (const byte*)"\xe0\x04" "copy") QDEF(MP_QSTR_copysign, (const byte*)"\x33\x08" "copysign") QDEF(MP_QSTR_cos, (const byte*)"\x7a\x03" "cos") QDEF(MP_QSTR_cosh, (const byte*)"\xd2\x04" "cosh") QDEF(MP_QSTR_count, (const byte*)"\xa6\x05" "count") QDEF(MP_QSTR_current_tid, (const byte*)"\xca\x0b" "current_tid") QDEF(MP_QSTR_decompress, (const byte*)"\x62\x0a" "decompress") QDEF(MP_QSTR_deepsleep, (const byte*)"\x9e\x09" "deepsleep") QDEF(MP_QSTR_default, (const byte*)"\xce\x07" "default") QDEF(MP_QSTR_degrees, (const byte*)"\x02\x07" "degrees") QDEF(MP_QSTR_delay, (const byte*)"\x50\x05" "delay") QDEF(MP_QSTR_deleter, (const byte*)"\x6e\x07" "deleter") QDEF(MP_QSTR_dict, (const byte*)"\x3f\x04" "dict") QDEF(MP_QSTR_dict_view, (const byte*)"\x2d\x09" "dict_view") QDEF(MP_QSTR_difference, (const byte*)"\x72\x0a" "difference") QDEF(MP_QSTR_difference_update, (const byte*)"\x9c\x11" "difference_update") QDEF(MP_QSTR_digest, (const byte*)"\xcd\x06" "digest") QDEF(MP_QSTR_dir, (const byte*)"\xfa\x03" "dir") QDEF(MP_QSTR_disable, (const byte*)"\x91\x07" "disable") QDEF(MP_QSTR_disable_irq, (const byte*)"\x04\x0b" "disable_irq") QDEF(MP_QSTR_discard, (const byte*)"\x0f\x07" "discard") QDEF(MP_QSTR_divmod, (const byte*)"\xb8\x06" "divmod") QDEF(MP_QSTR_doc, (const byte*)"\x2d\x03" "doc") QDEF(MP_QSTR_dump, (const byte*)"\xe9\x04" "dump") QDEF(MP_QSTR_dumps, (const byte*)"\x7a\x05" "dumps") QDEF(MP_QSTR_e, (const byte*)"\xc0\x01" "e") QDEF(MP_QSTR_elapsed_micros, (const byte*)"\x39\x0e" "elapsed_micros") QDEF(MP_QSTR_elapsed_millis, (const byte*)"\x8e\x0e" "elapsed_millis") QDEF(MP_QSTR_enable, (const byte*)"\x04\x06" "enable") QDEF(MP_QSTR_enable_irq, (const byte*)"\x91\x0a" "enable_irq") QDEF(MP_QSTR_end, (const byte*)"\x0a\x03" "end") QDEF(MP_QSTR_endswith, (const byte*)"\x1b\x08" "endswith") QDEF(MP_QSTR_enumerate, (const byte*)"\x71\x09" "enumerate") QDEF(MP_QSTR_erf, (const byte*)"\x94\x03" "erf") QDEF(MP_QSTR_erfc, (const byte*)"\x77\x04" "erfc") QDEF(MP_QSTR_eval, (const byte*)"\x9b\x04" "eval") QDEF(MP_QSTR_exec, (const byte*)"\x1e\x04" "exec") QDEF(MP_QSTR_execfile, (const byte*)"\x58\x08" "execfile") QDEF(MP_QSTR_exp, (const byte*)"\xc8\x03" "exp") QDEF(MP_QSTR_expm1, (const byte*)"\x74\x05" "expm1") QDEF(MP_QSTR_extend, (const byte*)"\x63\x06" "extend") QDEF(MP_QSTR_fabs, (const byte*)"\x93\x04" "fabs") QDEF(MP_QSTR_fault_debug, (const byte*)"\x61\x0b" "fault_debug") QDEF(MP_QSTR_filter, (const byte*)"\x25\x06" "filter") QDEF(MP_QSTR_find, (const byte*)"\x01\x04" "find") QDEF(MP_QSTR_float, (const byte*)"\x35\x05" "float") QDEF(MP_QSTR_floor, (const byte*)"\x7d\x05" "floor") QDEF(MP_QSTR_flush, (const byte*)"\x61\x05" "flush") QDEF(MP_QSTR_fmod, (const byte*)"\xe5\x04" "fmod") QDEF(MP_QSTR_format, (const byte*)"\x26\x06" "format") QDEF(MP_QSTR_freq, (const byte*)"\xe5\x04" "freq") QDEF(MP_QSTR_frexp, (const byte*)"\x1c\x05" "frexp") QDEF(MP_QSTR_from_bytes, (const byte*)"\x35\x0a" "from_bytes") QDEF(MP_QSTR_fromkeys, (const byte*)"\x37\x08" "fromkeys") QDEF(MP_QSTR_frozenset, (const byte*)"\xed\x09" "frozenset") QDEF(MP_QSTR_function, (const byte*)"\x27\x08" "function") QDEF(MP_QSTR_gamma, (const byte*)"\x02\x05" "gamma") QDEF(MP_QSTR_gc, (const byte*)"\x61\x02" "gc") QDEF(MP_QSTR_generator, (const byte*)"\x96\x09" "generator") QDEF(MP_QSTR_get, (const byte*)"\x33\x03" "get") QDEF(MP_QSTR_getattr, (const byte*)"\xc0\x07" "getattr") QDEF(MP_QSTR_getrandbits, (const byte*)"\x66\x0b" "getrandbits") QDEF(MP_QSTR_getter, (const byte*)"\x90\x06" "getter") QDEF(MP_QSTR_getvalue, (const byte*)"\x78\x08" "getvalue") QDEF(MP_QSTR_globals, (const byte*)"\x9d\x07" "globals") QDEF(MP_QSTR_group, (const byte*)"\xba\x05" "group") QDEF(MP_QSTR_hard_reset, (const byte*)"\xd0\x0a" "hard_reset") QDEF(MP_QSTR_hasattr, (const byte*)"\x8c\x07" "hasattr") QDEF(MP_QSTR_hash, (const byte*)"\xb7\x04" "hash") QDEF(MP_QSTR_heap_lock, (const byte*)"\xad\x09" "heap_lock") QDEF(MP_QSTR_heap_unlock, (const byte*)"\x56\x0b" "heap_unlock") QDEF(MP_QSTR_heapify, (const byte*)"\xaf\x07" "heapify") QDEF(MP_QSTR_heappop, (const byte*)"\xd6\x07" "heappop") QDEF(MP_QSTR_heappush, (const byte*)"\x87\x08" "heappush") QDEF(MP_QSTR_help, (const byte*)"\x94\x04" "help") QDEF(MP_QSTR_hex, (const byte*)"\x70\x03" "hex") QDEF(MP_QSTR_hexlify, (const byte*)"\x2a\x07" "hexlify") QDEF(MP_QSTR_id, (const byte*)"\x28\x02" "id") QDEF(MP_QSTR_idle, (const byte*)"\xa1\x04" "idle") QDEF(MP_QSTR_imag, (const byte*)"\x47\x04" "imag") QDEF(MP_QSTR_index, (const byte*)"\x7b\x05" "index") QDEF(MP_QSTR_info, (const byte*)"\xeb\x04" "info") QDEF(MP_QSTR_init, (const byte*)"\x1f\x04" "init") QDEF(MP_QSTR_input, (const byte*)"\x73\x05" "input") QDEF(MP_QSTR_insert, (const byte*)"\x12\x06" "insert") QDEF(MP_QSTR_int, (const byte*)"\x16\x03" "int") QDEF(MP_QSTR_intersection, (const byte*)"\x28\x0c" "intersection") QDEF(MP_QSTR_intersection_update, (const byte*)"\x06\x13" "intersection_update") QDEF(MP_QSTR_invert, (const byte*)"\xb7\x06" "invert") QDEF(MP_QSTR_is_preempt_thread, (const byte*)"\x1a\x11" "is_preempt_thread") QDEF(MP_QSTR_isalpha, (const byte*)"\xeb\x07" "isalpha") QDEF(MP_QSTR_isdigit, (const byte*)"\xa8\x07" "isdigit") QDEF(MP_QSTR_isdisjoint, (const byte*)"\xf7\x0a" "isdisjoint") QDEF(MP_QSTR_isenabled, (const byte*)"\x9a\x09" "isenabled") QDEF(MP_QSTR_isfinite, (const byte*)"\xa6\x08" "isfinite") QDEF(MP_QSTR_isinf, (const byte*)"\x3e\x05" "isinf") QDEF(MP_QSTR_isinstance, (const byte*)"\xb6\x0a" "isinstance") QDEF(MP_QSTR_islower, (const byte*)"\xfc\x07" "islower") QDEF(MP_QSTR_isnan, (const byte*)"\x9e\x05" "isnan") QDEF(MP_QSTR_isspace, (const byte*)"\x5b\x07" "isspace") QDEF(MP_QSTR_issubclass, (const byte*)"\xb5\x0a" "issubclass") QDEF(MP_QSTR_issubset, (const byte*)"\xb9\x08" "issubset") QDEF(MP_QSTR_issuperset, (const byte*)"\xfc\x0a" "issuperset") QDEF(MP_QSTR_isupper, (const byte*)"\xdd\x07" "isupper") QDEF(MP_QSTR_items, (const byte*)"\xe3\x05" "items") QDEF(MP_QSTR_iter, (const byte*)"\x8f\x04" "iter") QDEF(MP_QSTR_iterator, (const byte*)"\x47\x08" "iterator") QDEF(MP_QSTR_join, (const byte*)"\xa7\x04" "join") QDEF(MP_QSTR_kbd_intr, (const byte*)"\xf6\x08" "kbd_intr") QDEF(MP_QSTR_keepends, (const byte*)"\x62\x08" "keepends") QDEF(MP_QSTR_key, (const byte*)"\x32\x03" "key") QDEF(MP_QSTR_keys, (const byte*)"\x01\x04" "keys") QDEF(MP_QSTR_ldexp, (const byte*)"\x40\x05" "ldexp") QDEF(MP_QSTR_len, (const byte*)"\x62\x03" "len") QDEF(MP_QSTR_lgamma, (const byte*)"\xce\x06" "lgamma") QDEF(MP_QSTR_list, (const byte*)"\x27\x04" "list") QDEF(MP_QSTR_little, (const byte*)"\x89\x06" "little") QDEF(MP_QSTR_load, (const byte*)"\x63\x04" "load") QDEF(MP_QSTR_loads, (const byte*)"\xb0\x05" "loads") QDEF(MP_QSTR_locals, (const byte*)"\x3b\x06" "locals") QDEF(MP_QSTR_log, (const byte*)"\x21\x03" "log") QDEF(MP_QSTR_log10, (const byte*)"\x40\x05" "log10") QDEF(MP_QSTR_log2, (const byte*)"\x73\x04" "log2") QDEF(MP_QSTR_lower, (const byte*)"\xc6\x05" "lower") QDEF(MP_QSTR_lstrip, (const byte*)"\xe5\x06" "lstrip") QDEF(MP_QSTR_machine, (const byte*)"\x60\x07" "machine") QDEF(MP_QSTR_map, (const byte*)"\xb9\x03" "map") QDEF(MP_QSTR_match, (const byte*)"\x96\x05" "match") QDEF(MP_QSTR_math, (const byte*)"\x35\x04" "math") QDEF(MP_QSTR_max, (const byte*)"\xb1\x03" "max") QDEF(MP_QSTR_mem, (const byte*)"\x20\x03" "mem") QDEF(MP_QSTR_mem_alloc, (const byte*)"\x52\x09" "mem_alloc") QDEF(MP_QSTR_mem_free, (const byte*)"\xcb\x08" "mem_free") QDEF(MP_QSTR_mem_info, (const byte*)"\xd1\x08" "mem_info") QDEF(MP_QSTR_memoryview, (const byte*)"\x69\x0a" "memoryview") QDEF(MP_QSTR_micropython, (const byte*)"\x0b\x0b" "micropython") QDEF(MP_QSTR_micros, (const byte*)"\xac\x06" "micros") QDEF(MP_QSTR_millis, (const byte*)"\x5b\x06" "millis") QDEF(MP_QSTR_min, (const byte*)"\xaf\x03" "min") QDEF(MP_QSTR_mode, (const byte*)"\x26\x04" "mode") QDEF(MP_QSTR_modf, (const byte*)"\x25\x04" "modf") QDEF(MP_QSTR_module, (const byte*)"\xbf\x06" "module") QDEF(MP_QSTR_modules, (const byte*)"\xec\x07" "modules") QDEF(MP_QSTR_mount, (const byte*)"\xa8\x05" "mount") QDEF(MP_QSTR_name, (const byte*)"\xa2\x04" "name") QDEF(MP_QSTR_namedtuple, (const byte*)"\x1e\x0a" "namedtuple") QDEF(MP_QSTR_next, (const byte*)"\x42\x04" "next") QDEF(MP_QSTR_object, (const byte*)"\x90\x06" "object") QDEF(MP_QSTR_oct, (const byte*)"\xfd\x03" "oct") QDEF(MP_QSTR_off, (const byte*)"\x8a\x03" "off") QDEF(MP_QSTR_on, (const byte*)"\x64\x02" "on") QDEF(MP_QSTR_open, (const byte*)"\xd1\x04" "open") QDEF(MP_QSTR_opt_level, (const byte*)"\x87\x09" "opt_level") QDEF(MP_QSTR_ord, (const byte*)"\x1c\x03" "ord") QDEF(MP_QSTR_partition, (const byte*)"\x87\x09" "partition") QDEF(MP_QSTR_peektime, (const byte*)"\x8b\x08" "peektime") QDEF(MP_QSTR_phase, (const byte*)"\x6a\x05" "phase") QDEF(MP_QSTR_pi, (const byte*)"\x1c\x02" "pi") QDEF(MP_QSTR_pin, (const byte*)"\xf2\x03" "pin") QDEF(MP_QSTR_polar, (const byte*)"\x05\x05" "polar") QDEF(MP_QSTR_pop, (const byte*)"\x2a\x03" "pop") QDEF(MP_QSTR_popitem, (const byte*)"\xbf\x07" "popitem") QDEF(MP_QSTR_pow, (const byte*)"\x2d\x03" "pow") QDEF(MP_QSTR_print, (const byte*)"\x54\x05" "print") QDEF(MP_QSTR_property, (const byte*)"\xc2\x08" "property") QDEF(MP_QSTR_pull, (const byte*)"\x80\x04" "pull") QDEF(MP_QSTR_push, (const byte*)"\xbb\x04" "push") QDEF(MP_QSTR_pyb, (const byte*)"\xee\x03" "pyb") QDEF(MP_QSTR_qstr_info, (const byte*)"\xb0\x09" "qstr_info") QDEF(MP_QSTR_radians, (const byte*)"\x87\x07" "radians") QDEF(MP_QSTR_randint, (const byte*)"\xaf\x07" "randint") QDEF(MP_QSTR_random, (const byte*)"\xbe\x06" "random") QDEF(MP_QSTR_randrange, (const byte*)"\xa3\x09" "randrange") QDEF(MP_QSTR_range, (const byte*)"\x1a\x05" "range") QDEF(MP_QSTR_read, (const byte*)"\xb7\x04" "read") QDEF(MP_QSTR_readinto, (const byte*)"\x4b\x08" "readinto") QDEF(MP_QSTR_readline, (const byte*)"\xf9\x08" "readline") QDEF(MP_QSTR_real, (const byte*)"\xbf\x04" "real") QDEF(MP_QSTR_rect, (const byte*)"\xe5\x04" "rect") QDEF(MP_QSTR_remove, (const byte*)"\x63\x06" "remove") QDEF(MP_QSTR_repl_info, (const byte*)"\xbf\x09" "repl_info") QDEF(MP_QSTR_replace, (const byte*)"\x49\x07" "replace") QDEF(MP_QSTR_repr, (const byte*)"\xd0\x04" "repr") QDEF(MP_QSTR_reset, (const byte*)"\x10\x05" "reset") QDEF(MP_QSTR_reset_cause, (const byte*)"\xce\x0b" "reset_cause") QDEF(MP_QSTR_reverse, (const byte*)"\x25\x07" "reverse") QDEF(MP_QSTR_reversed, (const byte*)"\xa1\x08" "reversed") QDEF(MP_QSTR_rfind, (const byte*)"\xd2\x05" "rfind") QDEF(MP_QSTR_rindex, (const byte*)"\xe9\x06" "rindex") QDEF(MP_QSTR_round, (const byte*)"\xe7\x05" "round") QDEF(MP_QSTR_rpartition, (const byte*)"\x15\x0a" "rpartition") QDEF(MP_QSTR_rsplit, (const byte*)"\xa5\x06" "rsplit") QDEF(MP_QSTR_rstrip, (const byte*)"\x3b\x06" "rstrip") QDEF(MP_QSTR_rtthread, (const byte*)"\x6d\x08" "rtthread") QDEF(MP_QSTR_search, (const byte*)"\xab\x06" "search") QDEF(MP_QSTR_seed, (const byte*)"\x92\x04" "seed") QDEF(MP_QSTR_seek, (const byte*)"\x9d\x04" "seek") QDEF(MP_QSTR_send, (const byte*)"\xb9\x04" "send") QDEF(MP_QSTR_sep, (const byte*)"\x23\x03" "sep") QDEF(MP_QSTR_set, (const byte*)"\x27\x03" "set") QDEF(MP_QSTR_setattr, (const byte*)"\xd4\x07" "setattr") QDEF(MP_QSTR_setdefault, (const byte*)"\x6c\x0a" "setdefault") QDEF(MP_QSTR_setter, (const byte*)"\x04\x06" "setter") QDEF(MP_QSTR_sha256, (const byte*)"\x2e\x06" "sha256") QDEF(MP_QSTR_sin, (const byte*)"\xb1\x03" "sin") QDEF(MP_QSTR_single, (const byte*)"\x3f\x06" "single") QDEF(MP_QSTR_sinh, (const byte*)"\xb9\x04" "sinh") QDEF(MP_QSTR_sizeof, (const byte*)"\x49\x06" "sizeof") QDEF(MP_QSTR_sleep, (const byte*)"\xea\x05" "sleep") QDEF(MP_QSTR_sleep_ms, (const byte*)"\x0b\x08" "sleep_ms") QDEF(MP_QSTR_sleep_us, (const byte*)"\x13\x08" "sleep_us") QDEF(MP_QSTR_slice, (const byte*)"\xb5\x05" "slice") QDEF(MP_QSTR_soft_reset, (const byte*)"\xe1\x0a" "soft_reset") QDEF(MP_QSTR_sort, (const byte*)"\xbf\x04" "sort") QDEF(MP_QSTR_sorted, (const byte*)"\x5e\x06" "sorted") QDEF(MP_QSTR_split, (const byte*)"\xb7\x05" "split") QDEF(MP_QSTR_splitlines, (const byte*)"\x6a\x0a" "splitlines") QDEF(MP_QSTR_sqrt, (const byte*)"\x21\x04" "sqrt") QDEF(MP_QSTR_stack_use, (const byte*)"\x97\x09" "stack_use") QDEF(MP_QSTR_stacks_analyze, (const byte*)"\x63\x0e" "stacks_analyze") QDEF(MP_QSTR_standby, (const byte*)"\xd2\x07" "standby") QDEF(MP_QSTR_start, (const byte*)"\x85\x05" "start") QDEF(MP_QSTR_startswith, (const byte*)"\x74\x0a" "startswith") QDEF(MP_QSTR_staticmethod, (const byte*)"\x62\x0c" "staticmethod") QDEF(MP_QSTR_step, (const byte*)"\x57\x04" "step") QDEF(MP_QSTR_stop, (const byte*)"\x9d\x04" "stop") QDEF(MP_QSTR_str, (const byte*)"\x50\x03" "str") QDEF(MP_QSTR_strip, (const byte*)"\x29\x05" "strip") QDEF(MP_QSTR_struct, (const byte*)"\x12\x06" "struct") QDEF(MP_QSTR_sum, (const byte*)"\x2e\x03" "sum") QDEF(MP_QSTR_super, (const byte*)"\xc4\x05" "super") QDEF(MP_QSTR_symmetric_difference, (const byte*)"\xce\x14" "symmetric_difference") QDEF(MP_QSTR_symmetric_difference_update, (const byte*)"\x60\x1b" "symmetric_difference_update") QDEF(MP_QSTR_sync, (const byte*)"\xa2\x04" "sync") QDEF(MP_QSTR_tan, (const byte*)"\xfe\x03" "tan") QDEF(MP_QSTR_tanh, (const byte*)"\xd6\x04" "tanh") QDEF(MP_QSTR_throw, (const byte*)"\xb3\x05" "throw") QDEF(MP_QSTR_ticks_add, (const byte*)"\x9d\x09" "ticks_add") QDEF(MP_QSTR_ticks_cpu, (const byte*)"\x1a\x09" "ticks_cpu") QDEF(MP_QSTR_ticks_diff, (const byte*)"\xb1\x0a" "ticks_diff") QDEF(MP_QSTR_ticks_ms, (const byte*)"\x42\x08" "ticks_ms") QDEF(MP_QSTR_ticks_us, (const byte*)"\x5a\x08" "ticks_us") QDEF(MP_QSTR_time, (const byte*)"\xf0\x04" "time") QDEF(MP_QSTR_to_bytes, (const byte*)"\xd8\x08" "to_bytes") QDEF(MP_QSTR_trunc, (const byte*)"\x5b\x05" "trunc") QDEF(MP_QSTR_tuple, (const byte*)"\xfd\x05" "tuple") QDEF(MP_QSTR_type, (const byte*)"\x9d\x04" "type") QDEF(MP_QSTR_ubinascii, (const byte*)"\xc4\x09" "ubinascii") QDEF(MP_QSTR_ucollections, (const byte*)"\x15\x0c" "ucollections") QDEF(MP_QSTR_uctypes, (const byte*)"\xf8\x07" "uctypes") QDEF(MP_QSTR_udelay, (const byte*)"\x25\x06" "udelay") QDEF(MP_QSTR_uhashlib, (const byte*)"\x65\x08" "uhashlib") QDEF(MP_QSTR_uheapq, (const byte*)"\x1d\x06" "uheapq") QDEF(MP_QSTR_uio, (const byte*)"\xb6\x03" "uio") QDEF(MP_QSTR_ujson, (const byte*)"\xe8\x05" "ujson") QDEF(MP_QSTR_umachine, (const byte*)"\x95\x08" "umachine") QDEF(MP_QSTR_unhexlify, (const byte*)"\xb1\x09" "unhexlify") QDEF(MP_QSTR_uniform, (const byte*)"\x01\x07" "uniform") QDEF(MP_QSTR_union, (const byte*)"\xf6\x05" "union") QDEF(MP_QSTR_unique_id, (const byte*)"\x04\x09" "unique_id") QDEF(MP_QSTR_update, (const byte*)"\xb4\x06" "update") QDEF(MP_QSTR_upper, (const byte*)"\x27\x05" "upper") QDEF(MP_QSTR_urandom, (const byte*)"\xab\x07" "urandom") QDEF(MP_QSTR_ure, (const byte*)"\x87\x03" "ure") QDEF(MP_QSTR_utime, (const byte*)"\xe5\x05" "utime") QDEF(MP_QSTR_utimeq, (const byte*)"\xf4\x06" "utimeq") QDEF(MP_QSTR_uzlib, (const byte*)"\x6d\x05" "uzlib") QDEF(MP_QSTR_value, (const byte*)"\x4e\x05" "value") QDEF(MP_QSTR_values, (const byte*)"\x7d\x06" "values") QDEF(MP_QSTR_wfi, (const byte*)"\x9d\x03" "wfi") QDEF(MP_QSTR_write, (const byte*)"\x98\x05" "write") QDEF(MP_QSTR_zip, (const byte*)"\xe6\x03" "zip") QDEF(MP_QSTR_ustruct, (const byte*)"\x47\x07" "ustruct") QDEF(MP_QSTR_unpack, (const byte*)"\x07\x06" "unpack") QDEF(MP_QSTR_unpack_from, (const byte*)"\x0e\x0b" "unpack_from") QDEF(MP_QSTR_exit, (const byte*)"\x85\x04" "exit") QDEF(MP_QSTR_byteorder, (const byte*)"\x61\x09" "byteorder") QDEF(MP_QSTR_version_info, (const byte*)"\x6e\x0c" "version_info") QDEF(MP_QSTR_sys, (const byte*)"\xbc\x03" "sys") QDEF(MP_QSTR_usys, (const byte*)"\xc9\x04" "usys") QDEF(MP_QSTR_fileno, (const byte*)"\x82\x06" "fileno") QDEF(MP_QSTR_makefile, (const byte*)"\xc1\x08" "makefile") QDEF(MP_QSTR_usocket, (const byte*)"\x75\x07" "usocket") QDEF(MP_QSTR_getaddrinfo, (const byte*)"\x6e\x0b" "getaddrinfo") QDEF(MP_QSTR_inet_pton, (const byte*)"\xc9\x09" "inet_pton") QDEF(MP_QSTR_inet_ntop, (const byte*)"\x89\x09" "inet_ntop") QDEF(MP_QSTR_sockaddr, (const byte*)"\x62\x08" "sockaddr") QDEF(MP_QSTR_MSG_DONTROUTE, (const byte*)"\xeb\x0d" "MSG_DONTROUTE") QDEF(MP_QSTR_MSG_DONTWAIT, (const byte*)"\x99\x0c" "MSG_DONTWAIT") QDEF(MP_QSTR_SO_BROADCAST, (const byte*)"\xf9\x0c" "SO_BROADCAST") QDEF(MP_QSTR_AF_UNIX, (const byte*)"\xf7\x07" "AF_UNIX") QDEF(MP_QSTR_SO_ERROR, (const byte*)"\x3e\x08" "SO_ERROR") QDEF(MP_QSTR_uos, (const byte*)"\xec\x03" "uos") QDEF(MP_QSTR_stat, (const byte*)"\xd7\x04" "stat") QDEF(MP_QSTR_statvfs, (const byte*)"\x14\x07" "statvfs") QDEF(MP_QSTR_unlink, (const byte*)"\xfe\x06" "unlink") QDEF(MP_QSTR_chdir, (const byte*)"\xb1\x05" "chdir") QDEF(MP_QSTR_getcwd, (const byte*)"\x03\x06" "getcwd") QDEF(MP_QSTR_dupterm, (const byte*)"\xaa\x07" "dupterm") QDEF(MP_QSTR_umount, (const byte*)"\xdd\x06" "umount") QDEF(MP_QSTR_uname, (const byte*)"\xb7\x05" "uname") QDEF(MP_QSTR_sysname, (const byte*)"\x9b\x07" "sysname") QDEF(MP_QSTR_unregister, (const byte*)"\x17\x0a" "unregister") QDEF(MP_QSTR_uselect, (const byte*)"\x58\x07" "uselect") QDEF(MP_QSTR_writeblocks, (const byte*)"\x02\x0b" "writeblocks") QDEF(MP_QSTR_writebyte, (const byte*)"\xd2\x09" "writebyte") QDEF(MP_QSTR_tell, (const byte*)"\x14\x04" "tell") QDEF(MP_QSTR_listdir, (const byte*)"\x98\x07" "listdir") QDEF(MP_QSTR_ilistdir, (const byte*)"\x71\x08" "ilistdir") QDEF(MP_QSTR_mkdir, (const byte*)"\x9c\x05" "mkdir") QDEF(MP_QSTR_VfsFat, (const byte*)"\x15\x06" "VfsFat") QDEF(MP_QSTR_rename, (const byte*)"\x35\x06" "rename") QDEF(MP_QSTR_rmdir, (const byte*)"\x45\x05" "rmdir") QDEF(MP_QSTR_mkfs, (const byte*)"\x76\x04" "mkfs") QDEF(MP_QSTR_socket, (const byte*)"\x60\x06" "socket") QDEF(MP_QSTR_AF_INET6, (const byte*)"\x7d\x08" "AF_INET6") QDEF(MP_QSTR_SOCK_STREAM, (const byte*)"\x32\x0b" "SOCK_STREAM") QDEF(MP_QSTR_SOCK_DGRAM, (const byte*)"\xb3\x0a" "SOCK_DGRAM") QDEF(MP_QSTR_SOCK_RAW, (const byte*)"\xca\x08" "SOCK_RAW") QDEF(MP_QSTR_AF_INET, (const byte*)"\xeb\x07" "AF_INET") QDEF(MP_QSTR_connect, (const byte*)"\xdb\x07" "connect") QDEF(MP_QSTR_bind, (const byte*)"\x84\x04" "bind") QDEF(MP_QSTR_listen, (const byte*)"\xcc\x06" "listen") QDEF(MP_QSTR_accept, (const byte*)"\x85\x06" "accept") QDEF(MP_QSTR_recv, (const byte*)"\xe7\x04" "recv") QDEF(MP_QSTR_sendto, (const byte*)"\x22\x06" "sendto") QDEF(MP_QSTR_recvfrom, (const byte*)"\x91\x08" "recvfrom") QDEF(MP_QSTR_setsockopt, (const byte*)"\x38\x0a" "setsockopt") QDEF(MP_QSTR_settimeout, (const byte*)"\xdc\x0a" "settimeout") QDEF(MP_QSTR_setblocking, (const byte*)"\x6e\x0b" "setblocking") QDEF(MP_QSTR_calcsize, (const byte*)"\x4d\x08" "calcsize") QDEF(MP_QSTR_pack, (const byte*)"\xbc\x04" "pack") QDEF(MP_QSTR_pack_into, (const byte*)"\x1f\x09" "pack_into") QDEF(MP_QSTR_os, (const byte*)"\x79\x02" "os") QDEF(MP_QSTR_zlib, (const byte*)"\xf8\x04" "zlib") QDEF(MP_QSTR_hashlib, (const byte*)"\x10\x07" "hashlib") QDEF(MP_QSTR_binascii, (const byte*)"\x91\x08" "binascii") QDEF(MP_QSTR_collections, (const byte*)"\xe0\x0b" "collections") QDEF(MP_QSTR_re, (const byte*)"\xd2\x02" "re") QDEF(MP_QSTR_json, (const byte*)"\xfd\x04" "json") QDEF(MP_QSTR_heapq, (const byte*)"\x68\x05" "heapq") QDEF(MP_QSTR_path, (const byte*)"\x88\x04" "path") QDEF(MP_QSTR_argv, (const byte*)"\xc7\x04" "argv") QDEF(MP_QSTR_version, (const byte*)"\xbf\x07" "version") QDEF(MP_QSTR_implementation, (const byte*)"\x17\x0e" "implementation") QDEF(MP_QSTR_print_exception, (const byte*)"\x1c\x0f" "print_exception") QDEF(MP_QSTR_uerrno, (const byte*)"\xb4\x06" "uerrno") QDEF(MP_QSTR_errno, (const byte*)"\xc1\x05" "errno") QDEF(MP_QSTR_errorcode, (const byte*)"\x10\x09" "errorcode") QDEF(MP_QSTR_EPERM, (const byte*)"\xea\x05" "EPERM") QDEF(MP_QSTR_ENOBUFS, (const byte*)"\xe3\x07" "ENOBUFS") QDEF(MP_QSTR_ENODEV, (const byte*)"\xb6\x06" "ENODEV") QDEF(MP_QSTR_ENOENT, (const byte*)"\x5e\x06" "ENOENT") QDEF(MP_QSTR_ENOMEM, (const byte*)"\xa4\x06" "ENOMEM") QDEF(MP_QSTR_ENOTCONN, (const byte*)"\x79\x08" "ENOTCONN") QDEF(MP_QSTR_EOPNOTSUPP, (const byte*)"\xac\x0a" "EOPNOTSUPP") QDEF(MP_QSTR_ETIMEDOUT, (const byte*)"\xff\x09" "ETIMEDOUT") QDEF(MP_QSTR_EIO, (const byte*)"\x86\x03" "EIO") QDEF(MP_QSTR_EBADF, (const byte*)"\x61\x05" "EBADF") QDEF(MP_QSTR_EAGAIN, (const byte*)"\x20\x06" "EAGAIN") QDEF(MP_QSTR_EACCES, (const byte*)"\x37\x06" "EACCES") QDEF(MP_QSTR_EEXIST, (const byte*)"\x53\x06" "EEXIST") QDEF(MP_QSTR_EISDIR, (const byte*)"\xa5\x06" "EISDIR") QDEF(MP_QSTR_EINVAL, (const byte*)"\x5c\x06" "EINVAL") QDEF(MP_QSTR_EADDRINUSE, (const byte*)"\x17\x0a" "EADDRINUSE") QDEF(MP_QSTR_ECONNABORTED, (const byte*)"\x27\x0c" "ECONNABORTED") QDEF(MP_QSTR_ECONNREFUSED, (const byte*)"\x3a\x0c" "ECONNREFUSED") QDEF(MP_QSTR_ECONNRESET, (const byte*)"\x19\x0a" "ECONNRESET") QDEF(MP_QSTR_EHOSTUNREACH, (const byte*)"\x86\x0c" "EHOSTUNREACH") QDEF(MP_QSTR_EALREADY, (const byte*)"\x46\x08" "EALREADY") QDEF(MP_QSTR_EINPROGRESS, (const byte*)"\x9a\x0b" "EINPROGRESS") QDEF(MP_QSTR_ussl, (const byte*)"\x1c\x04" "ussl") QDEF(MP_QSTR_ssl, (const byte*)"\xe9\x03" "ssl") QDEF(MP_QSTR_wrap_socket, (const byte*)"\xcb\x0b" "wrap_socket") QDEF(MP_QSTR_getpeercert, (const byte*)"\xb1\x0b" "getpeercert") QDEF(MP_QSTR_cert, (const byte*)"\x25\x04" "cert") QDEF(MP_QSTR_server_side, (const byte*)"\x64\x0b" "server_side") QDEF(MP_QSTR_server_hostname, (const byte*)"\x58\x0f" "server_hostname") QDEF(MP_QSTR_readlines, (const byte*)"\x6a\x09" "readlines") QDEF(MP_QSTR_decode, (const byte*)"\xa9\x06" "decode") QDEF(MP_QSTR_delattr, (const byte*)"\xdb\x07" "delattr") QDEF(MP_QSTR_FileIO, (const byte*)"\xc5\x06" "FileIO") QDEF(MP_QSTR_TextIOWrapper, (const byte*)"\xad\x0d" "TextIOWrapper") QDEF(MP_QSTR_io, (const byte*)"\x23\x02" "io") QDEF(MP_QSTR_stdin, (const byte*)"\x21\x05" "stdin") QDEF(MP_QSTR_stdout, (const byte*)"\x08\x06" "stdout") QDEF(MP_QSTR_stderr, (const byte*)"\xa3\x06" "stderr") QDEF(MP_QSTR_platform, (const byte*)"\x3a\x08" "platform") QDEF(MP_QSTR_deque, (const byte*)"\x05\x05" "deque") QDEF(MP_QSTR_select, (const byte*)"\x8d\x06" "select") QDEF(MP_QSTR_poll, (const byte*)"\x9a\x04" "poll") QDEF(MP_QSTR_POLLERR, (const byte*)"\xdf\x07" "POLLERR") QDEF(MP_QSTR_POLLHUP, (const byte*)"\x77\x07" "POLLHUP") QDEF(MP_QSTR_POLLIN, (const byte*)"\x7d\x06" "POLLIN") QDEF(MP_QSTR_POLLOUT, (const byte*)"\x74\x07" "POLLOUT") QDEF(MP_QSTR_ipoll, (const byte*)"\x53\x05" "ipoll") QDEF(MP_QSTR_register, (const byte*)"\xac\x08" "register") QDEF(MP_QSTR_modify, (const byte*)"\xf5\x06" "modify") QDEF(MP_QSTR_SOL_SOCKET, (const byte*)"\x0f\x0a" "SOL_SOCKET") QDEF(MP_QSTR_SO_REUSEADDR, (const byte*)"\x21\x0c" "SO_REUSEADDR") QDEF(MP_QSTR_pend_throw, (const byte*)"\xf3\x0a" "pend_throw") QDEF(MP_QSTR__space_, (const byte*)"\xe1\x01" " ") QDEF(MP_QSTR_encode, (const byte*)"\x43\x06" "encode") QDEF(MP_QSTR_nodename, (const byte*)"\x62\x08" "nodename") QDEF(MP_QSTR_file, (const byte*)"\xc3\x04" "file") QDEF(MP_QSTR_r, (const byte*)"\xd7\x01" "r") QDEF(MP_QSTR_buffering, (const byte*)"\x25\x09" "buffering") QDEF(MP_QSTR_encoding, (const byte*)"\x06\x08" "encoding") QDEF(MP_QSTR___dict__, (const byte*)"\x7f\x08" "__dict__") QDEF(MP_QSTR_release, (const byte*)"\xec\x07" "release") QDEF(MP_QSTR_iterable, (const byte*)"\x25\x08" "iterable") QDEF(MP_QSTR_quit, (const byte*)"\x5c\x04" "quit") QDEF(MP_QSTR_addr, (const byte*)"\xb6\x04" "addr") QDEF(MP_QSTR_addrsize, (const byte*)"\x93\x08" "addrsize") QDEF(MP_QSTR_I2C, (const byte*)"\x5d\x03" "I2C") QDEF(MP_QSTR_memaddr, (const byte*)"\x93\x07" "memaddr") QDEF(MP_QSTR_readfrom_into, (const byte*)"\x82\x0d" "readfrom_into") QDEF(MP_QSTR_readfrom_mem, (const byte*)"\x3b\x0c" "readfrom_mem") QDEF(MP_QSTR_readfrom_mem_into, (const byte*)"\x38\x11" "readfrom_mem_into") QDEF(MP_QSTR_readfrom, (const byte*)"\x41\x08" "readfrom") QDEF(MP_QSTR_scan, (const byte*)"\x1a\x04" "scan") QDEF(MP_QSTR_sck, (const byte*)"\xfe\x03" "sck") QDEF(MP_QSTR_scl, (const byte*)"\xf9\x03" "scl") QDEF(MP_QSTR_sda, (const byte*)"\x53\x03" "sda") QDEF(MP_QSTR_arg, (const byte*)"\x91\x03" "arg") QDEF(MP_QSTR_timeout, (const byte*)"\x3e\x07" "timeout") QDEF(MP_QSTR_writeto_mem, (const byte*)"\x79\x0b" "writeto_mem") QDEF(MP_QSTR_writeto, (const byte*)"\x03\x07" "writeto") QDEF(MP_QSTR_deinit, (const byte*)"\x9e\x06" "deinit") QDEF(MP_QSTR_firstbit, (const byte*)"\x20\x08" "firstbit") QDEF(MP_QSTR_mosi, (const byte*)"\x1d\x04" "mosi") QDEF(MP_QSTR_polarity, (const byte*)"\x41\x08" "polarity") QDEF(MP_QSTR_SPI, (const byte*)"\xef\x03" "SPI") QDEF(MP_QSTR_baudrate, (const byte*)"\xf5\x08" "baudrate") QDEF(MP_QSTR_bits, (const byte*)"\x49\x04" "bits") QDEF(MP_QSTR_miso, (const byte*)"\x9d\x04" "miso") QDEF(MP_QSTR_MSB, (const byte*)"\x59\x03" "MSB") QDEF(MP_QSTR_SoftSPI, (const byte*)"\x21\x07" "SoftSPI") QDEF(MP_QSTR_write_readinto, (const byte*)"\x89\x0e" "write_readinto") QDEF(MP_QSTR_LSB, (const byte*)"\xd8\x03" "LSB") QDEF(MP_QSTR__thread, (const byte*)"\xd4\x07" "_thread") QDEF(MP_QSTR_acquire, (const byte*)"\x1d\x07" "acquire") QDEF(MP_QSTR_locked, (const byte*)"\x0f\x06" "locked") QDEF(MP_QSTR_lock, (const byte*)"\xae\x04" "lock") QDEF(MP_QSTR_LockType, (const byte*)"\x36\x08" "LockType") QDEF(MP_QSTR_get_ident, (const byte*)"\xfe\x09" "get_ident") QDEF(MP_QSTR_stack_size, (const byte*)"\x31\x0a" "stack_size") QDEF(MP_QSTR_start_new_thread, (const byte*)"\xd7\x10" "start_new_thread") QDEF(MP_QSTR_allocate_lock, (const byte*)"\xec\x0d" "allocate_lock") QDEF(MP_QSTR_UART, (const byte*)"\xb7\x04" "UART") QDEF(MP_QSTR_writechar, (const byte*)"\x40\x09" "writechar") QDEF(MP_QSTR_readchar, (const byte*)"\xef\x08" "readchar") QDEF(MP_QSTR_sendbreak, (const byte*)"\xc6\x09" "sendbreak") QDEF(MP_QSTR_RTS, (const byte*)"\xb0\x03" "RTS") QDEF(MP_QSTR_CTS, (const byte*)"\x61\x03" "CTS") QDEF(MP_QSTR_parity, (const byte*)"\x42\x06" "parity") QDEF(MP_QSTR_flow, (const byte*)"\x37\x04" "flow") QDEF(MP_QSTR_timeout_char, (const byte*)"\x79\x0c" "timeout_char") QDEF(MP_QSTR_read_buf_len, (const byte*)"\xa1\x0c" "read_buf_len") QDEF(MP_QSTR_mktime, (const byte*)"\x96\x06" "mktime") QDEF(MP_QSTR_localtime, (const byte*)"\x7d\x09" "localtime") QDEF(MP_QSTR_IPPROTO_IP, (const byte*)"\x0c\x0a" "IPPROTO_IP") QDEF(MP_QSTR_IPPROTO_ICMP, (const byte*)"\xa2\x0c" "IPPROTO_ICMP") QDEF(MP_QSTR_IPPROTO_IPV4, (const byte*)"\x2e\x0c" "IPPROTO_IPV4") QDEF(MP_QSTR_IPPROTO_TCP, (const byte*)"\xb2\x0b" "IPPROTO_TCP") QDEF(MP_QSTR_IPPROTO_UDP, (const byte*)"\x54\x0b" "IPPROTO_UDP") QDEF(MP_QSTR_IPPROTO_IPV6, (const byte*)"\x2c\x0c" "IPPROTO_IPV6") QDEF(MP_QSTR_IPPROTO_RAW, (const byte*)"\xf1\x0b" "IPPROTO_RAW") QDEF(MP_QSTR_websocket, (const byte*)"\x90\x09" "websocket") QDEF(MP_QSTR__webrepl, (const byte*)"\x21\x08" "_webrepl") QDEF(MP_QSTR_ioctl, (const byte*)"\x78\x05" "ioctl") QDEF(MP_QSTR_password, (const byte*)"\x9a\x08" "password") QDEF(MP_QSTR_rb, (const byte*)"\xd5\x02" "rb") QDEF(MP_QSTR_wb, (const byte*)"\x70\x02" "wb") QDEF(MP_QSTR_IP_ADD_MEMBERSHIP, (const byte*)"\x6f\x11" "IP_ADD_MEMBERSHIP") QDEF(MP_QSTR_sendall, (const byte*)"\x38\x07" "sendall") QDEF(MP_QSTR_sha1, (const byte*)"\x8e\x04" "sha1") QDEF(MP_QSTR_readbit, (const byte*)"\x08\x07" "readbit") QDEF(MP_QSTR_readbyte, (const byte*)"\x7d\x08" "readbyte") QDEF(MP_QSTR_writebit, (const byte*)"\xc7\x08" "writebit") QDEF(MP_QSTR_flags, (const byte*)"\xfa\x05" "flags") QDEF(MP_QSTR_trigger, (const byte*)"\x9d\x07" "trigger") QDEF(MP_QSTR_handler, (const byte*)"\xdd\x07" "handler") QDEF(MP_QSTR_crc8, (const byte*)"\xcf\x04" "crc8") QDEF(MP_QSTR_hard, (const byte*)"\xda\x04" "hard") QDEF(MP_QSTR_irq, (const byte*)"\x8f\x03" "irq") QDEF(MP_QSTR_onewire, (const byte*)"\x28\x07" "onewire") QDEF(MP_QSTR___dir__, (const byte*)"\x7a\x07" "__dir__") QDEF(MP_QSTR___int__, (const byte*)"\x16\x07" "__int__") QDEF(MP_QSTR_schedule, (const byte*)"\xe0\x08" "schedule") QDEF(MP_QSTR_SHORT, (const byte*)"\xf7\x05" "SHORT") QDEF(MP_QSTR_USHORT, (const byte*)"\xa2\x06" "USHORT") QDEF(MP_QSTR_INT, (const byte*)"\x36\x03" "INT") QDEF(MP_QSTR_UINT, (const byte*)"\x23\x04" "UINT") QDEF(MP_QSTR_LONG, (const byte*)"\x0f\x04" "LONG") QDEF(MP_QSTR_ULONG, (const byte*)"\x7a\x05" "ULONG") QDEF(MP_QSTR_LONGLONG, (const byte*)"\x85\x08" "LONGLONG") QDEF(MP_QSTR_ULONGLONG, (const byte*)"\x70\x09" "ULONGLONG") QDEF(MP_QSTR_ffi, (const byte*)"\x8c\x03" "ffi") QDEF(MP_QSTR_callback, (const byte*)"\x4c\x08" "callback") QDEF(MP_QSTR_func, (const byte*)"\x1b\x04" "func") QDEF(MP_QSTR_as_bytearray, (const byte*)"\x1b\x0c" "as_bytearray") QDEF(MP_QSTR_var, (const byte*)"\xe0\x03" "var") QDEF(MP_QSTR_ffimod, (const byte*)"\xca\x06" "ffimod") QDEF(MP_QSTR_ffifunc, (const byte*)"\x92\x07" "ffifunc") QDEF(MP_QSTR_fficallback, (const byte*)"\xc5\x0b" "fficallback") QDEF(MP_QSTR_ffivar, (const byte*)"\x49\x06" "ffivar") QDEF(MP_QSTR_ADC, (const byte*)"\x63\x03" "ADC") QDEF(MP_QSTR_duty, (const byte*)"\x19\x04" "duty") QDEF(MP_QSTR_PWM, (const byte*)"\x4f\x03" "PWM") QDEF(MP_QSTR_network, (const byte*)"\x5b\x07" "network") QDEF(MP_QSTR_isconnected, (const byte*)"\x80\x0b" "isconnected") QDEF(MP_QSTR_WLAN, (const byte*)"\x11\x04" "WLAN") QDEF(MP_QSTR_disconnect, (const byte*)"\xa5\x0a" "disconnect") QDEF(MP_QSTR_active, (const byte*)"\x69\x06" "active") QDEF(MP_QSTR_STA_IF, (const byte*)"\xb3\x06" "STA_IF") QDEF(MP_QSTR_AP_IF, (const byte*)"\x04\x05" "AP_IF") QDEF(MP_QSTR_bssid, (const byte*)"\x4a\x05" "bssid") QDEF(MP_QSTR_status, (const byte*)"\x71\x06" "status") QDEF(MP_QSTR_rssi, (const byte*)"\x7e\x04" "rssi") QDEF(MP_QSTR_config, (const byte*)"\x4f\x06" "config") QDEF(MP_QSTR_ifconfig, (const byte*)"\xe0\x08" "ifconfig") QDEF(MP_QSTR_mac, (const byte*)"\xaa\x03" "mac") QDEF(MP_QSTR_essid, (const byte*)"\x4d\x05" "essid") QDEF(MP_QSTR_hidden, (const byte*)"\xef\x06" "hidden") QDEF(MP_QSTR_authmode, (const byte*)"\xce\x08" "authmode") QDEF(MP_QSTR_channel, (const byte*)"\x26\x07" "channel") QDEF(MP_QSTR_dhcp_hostname, (const byte*)"\xa2\x0d" "dhcp_hostname") QDEF(MP_QSTR_STAT_GOT_IP, (const byte*)"\xb2\x0b" "STAT_GOT_IP") QDEF(MP_QSTR_STAT_CONNECT_FAIL, (const byte*)"\x0b\x11" "STAT_CONNECT_FAIL") QDEF(MP_QSTR_STAT_NO_AP_FOUND, (const byte*)"\xee\x10" "STAT_NO_AP_FOUND") QDEF(MP_QSTR_STAT_WRONG_PASSWORD, (const byte*)"\x0b\x13" "STAT_WRONG_PASSWORD") QDEF(MP_QSTR_STAT_CONNECTING, (const byte*)"\xf6\x0f" "STAT_CONNECTING") QDEF(MP_QSTR_STAT_IDLE, (const byte*)"\x0c\x09" "STAT_IDLE") QDEF(MP_QSTR_now, (const byte*)"\xb3\x03" "now") QDEF(MP_QSTR_RTC, (const byte*)"\xa0\x03" "RTC") QDEF(MP_QSTR_command, (const byte*)"\x02\x07" "command") QDEF(MP_QSTR_contrast, (const byte*)"\x07\x08" "contrast") QDEF(MP_QSTR_light, (const byte*)"\xfb\x05" "light") QDEF(MP_QSTR_fill, (const byte*)"\xca\x04" "fill") QDEF(MP_QSTR_pixel, (const byte*)"\x4d\x05" "pixel") QDEF(MP_QSTR_show, (const byte*)"\x86\x04" "show") QDEF(MP_QSTR_LCD, (const byte*)"\xce\x03" "LCD") QDEF(MP_QSTR_text, (const byte*)"\x98\x04" "text") QDEF(MP_QSTR_WHITE, (const byte*)"\xa2\x05" "WHITE") QDEF(MP_QSTR_BLACK, (const byte*)"\x82\x05" "BLACK") QDEF(MP_QSTR_BLUE, (const byte*)"\x3b\x04" "BLUE") QDEF(MP_QSTR_BRED, (const byte*)"\x34\x04" "BRED") QDEF(MP_QSTR_GRED, (const byte*)"\x91\x04" "GRED") QDEF(MP_QSTR_GBLUE, (const byte*)"\x5c\x05" "GBLUE") QDEF(MP_QSTR_RED, (const byte*)"\x96\x03" "RED") QDEF(MP_QSTR_MAGENTA, (const byte*)"\xf0\x07" "MAGENTA") QDEF(MP_QSTR_GREEN, (const byte*)"\xde\x05" "GREEN") QDEF(MP_QSTR_CYAN, (const byte*)"\x10\x04" "CYAN") QDEF(MP_QSTR_YELLOW, (const byte*)"\x41\x06" "YELLOW") QDEF(MP_QSTR_BROWN, (const byte*)"\xc3\x05" "BROWN") QDEF(MP_QSTR_BRRED, (const byte*)"\x06\x05" "BRRED") QDEF(MP_QSTR_GRAY, (const byte*)"\x08\x04" "GRAY") QDEF(MP_QSTR_GRAY175, (const byte*)"\x1b\x07" "GRAY175") QDEF(MP_QSTR_GRAY151, (const byte*)"\xdd\x07" "GRAY151") QDEF(MP_QSTR_GRAY187, (const byte*)"\xb6\x07" "GRAY187") QDEF(MP_QSTR_GRAY240, (const byte*)"\x3e\x07" "GRAY240") QDEF(MP_QSTR_line, (const byte*)"\xcb\x04" "line") QDEF(MP_QSTR_rectangle, (const byte*)"\xa4\x09" "rectangle") QDEF(MP_QSTR_circle, (const byte*)"\xb7\x06" "circle") QDEF(MP_QSTR_WDT, (const byte*)"\x62\x03" "WDT") QDEF(MP_QSTR_feed, (const byte*)"\xa7\x04" "feed") QDEF(MP_QSTR_Timer, (const byte*)"\xa2\x05" "Timer") QDEF(MP_QSTR_ONE_SHOT, (const byte*)"\x5e\x08" "ONE_SHOT") QDEF(MP_QSTR_PERIODIC, (const byte*)"\x0a\x08" "PERIODIC") QDEF(MP_QSTR_period, (const byte*)"\xa0\x06" "period") QDEF(MP_QSTR_set_color, (const byte*)"\x25\x09" "set_color") QDEF(MP_QSTR_file_crc32, (const byte*)"\x6f\x0a" "file_crc32") QDEF(MP_QSTR_list_device, (const byte*)"\x20\x0b" "list_device") QDEF(MP_QSTR_show_image, (const byte*)"\xde\x0a" "show_image") QDEF(MP_QSTR_IRQ, (const byte*)"\xaf\x03" "IRQ") QDEF(MP_QSTR_IRQ_HIGH_LEVEL, (const byte*)"\x57\x0e" "IRQ_HIGH_LEVEL") QDEF(MP_QSTR_IRQ_LOW_LEVEL, (const byte*)"\x8d\x0d" "IRQ_LOW_LEVEL") QDEF(MP_QSTR_show_bmp, (const byte*)"\xe6\x08" "show_bmp") QDEF(MP_QSTR_userfunc, (const byte*)"\x4a\x08" "userfunc") QDEF(MP_QSTR_uarray, (const byte*)"\x89\x06" "uarray") QDEF(MP_QSTR_mpy, (const byte*)"\xc1\x03" "mpy") QDEF(MP_QSTR___matmul__, (const byte*)"\x49\x0a" "__matmul__") QDEF(MP_QSTR___bases__, (const byte*)"\x03\x09" "__bases__") QDEF(MP_QSTR_writevto, (const byte*)"\x75\x08" "writevto") QDEF(MP_QSTR_do_handshake, (const byte*)"\x86\x0c" "do_handshake") QDEF(MP_QSTR___ne__, (const byte*)"\x0e\x06" "__ne__") QDEF(MP_QSTR_i, (const byte*)"\xcc\x01" "i") #if MICROPY_USER_EXTMODS #include "qstrdefs.user.extmods.h" #endif ================================================ FILE: port/modules/machine/machine_adc.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include "py/nlr.h" #include "py/runtime.h" #include "modmachine.h" #include "mphalport.h" #ifdef MICROPYTHON_USING_MACHINE_ADC #include #include extern const mp_obj_type_t machine_adc_type; typedef struct _machine_adc_obj_t { mp_obj_base_t base; struct rt_adc_device *adc_device; uint8_t channel; uint8_t is_init; } machine_adc_obj_t; STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, msg)); } } STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // create ADC object from the given pin machine_adc_obj_t *self = m_new_obj(machine_adc_obj_t); struct rt_adc_device *adc_device = RT_NULL; char adc_dev_name[RT_NAME_MAX] = {0}; rt_err_t result = RT_EOK; // init machine adc object information self->channel = 0; self->is_init = RT_FALSE; self->base.type = &machine_adc_type; mp_arg_check_num(n_args, n_kw, 1, 2, true); // check input ADC device name or ID if (mp_obj_is_small_int(args[0])) { rt_snprintf(adc_dev_name, sizeof(adc_dev_name), "adc%d", mp_obj_get_int(args[0])); } else if (mp_obj_is_qstr(args[0])) { rt_strncpy(adc_dev_name, mp_obj_str_get_str(args[0]), RT_NAME_MAX); } else { error_check(0, "Input ADC device name or ID error."); } adc_device = (struct rt_adc_device *) rt_device_find(adc_dev_name); if (adc_device == RT_NULL || adc_device->parent.type != RT_Device_Class_Miscellaneous) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "ADC(%s) don't exist", adc_dev_name)); } self->adc_device = adc_device; if (n_args == 2) { self->channel = mp_obj_get_int(args[1]); result = rt_adc_enable(self->adc_device, self->channel); error_check(result == RT_EOK, "ADC enable error"); self->is_init = RT_TRUE; } return MP_OBJ_FROM_PTR(self); } STATIC mp_obj_t machine_adc_init(size_t n_args, const mp_obj_t *args) { machine_adc_obj_t *self = MP_OBJ_TO_PTR(args[0]); rt_err_t result = RT_EOK; result = rt_adc_enable(self->adc_device, mp_obj_get_int(args[1])); error_check(result == RT_EOK, "ADC enable error"); self->channel = mp_obj_get_int(args[1]); self->is_init = RT_TRUE; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_adc_init_obj, 2, 2, machine_adc_init); STATIC mp_obj_t machine_adc_deinit(mp_obj_t self_in) { machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); rt_err_t result = RT_EOK; if (self->is_init == RT_TRUE) { result = rt_adc_disable(self->adc_device, self->channel); error_check(result == RT_EOK, "ADC disable error"); self->is_init = RT_FALSE; } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_deinit_obj, machine_adc_deinit); STATIC mp_obj_t machine_adc_read(mp_obj_t self_in) { machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); int tval = 0; error_check(self->is_init == RT_TRUE, "ADC device uninitialized"); tval = rt_adc_read(self->adc_device, self->channel); return MP_OBJ_NEW_SMALL_INT(tval); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_obj, machine_adc_read); STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_adc_init_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_adc_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_adc_read_obj) }, }; STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table); const mp_obj_type_t machine_adc_type = { { &mp_type_type }, .name = MP_QSTR_ADC, .make_new = machine_adc_make_new, .locals_dict = (mp_obj_dict_t *) &machine_adc_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_adc ================================================ FILE: port/modules/machine/machine_adc.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef MICROPY_INCLUDED_MACHINE_ADC_H #define MICROPY_INCLUDED_MACHINE_ADC_H #include "py/obj.h" #include extern const mp_obj_type_t machine_adc_type; #endif // MICROPY_INCLUDED_MACHINE_ADC_H ================================================ FILE: port/modules/machine/machine_hw_i2c.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 SummerGift * * 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/mphal.h" #include "py/mperrno.h" #include "extmod/machine_i2c.h" #ifdef MICROPYTHON_USING_MACHINE_I2C STATIC const mp_obj_type_t machine_hard_i2c_type; STATIC const mp_arg_t machine_i2c_mem_allowed_args[] = { { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_arg, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, }; typedef struct _machine_hard_i2c_obj_t { mp_obj_base_t base; struct rt_i2c_bus_device *i2c_bus; } machine_hard_i2c_obj_t; #ifndef RT_USING_I2C #error "Please define the RT_USING_I2C on 'rtconfig.h'" #endif STATIC void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_hard_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print,"I2C(%s, timeout=%u)", self->i2c_bus->parent.parent.name, self->i2c_bus->timeout); return; } STATIC int write_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, const uint8_t *buf, size_t len) { machine_hard_i2c_obj_t *self = (machine_hard_i2c_obj_t*)MP_OBJ_TO_PTR(self_in); // Create buffer with memory address size_t memaddr_len = 0; uint8_t memaddr_buf[4]; for (int16_t i = addrsize - 8; i >= 0; i -= 8) { memaddr_buf[memaddr_len++] = memaddr >> i; } struct rt_i2c_msg msg[2]; msg[0].buf = memaddr_buf; msg[0].len = (addrsize + 7)/8; msg[0].flags = RT_I2C_WR; msg[0].addr = addr; msg[1].buf = (rt_uint8_t*)buf; msg[1].len = len; msg[1].flags = RT_I2C_WR; msg[1].addr = addr; if (rt_i2c_transfer(self->i2c_bus, msg, 2) != 2) return -MP_EIO; return len; } STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, uint8_t *buf, size_t len) { machine_hard_i2c_obj_t *self = (machine_hard_i2c_obj_t*)MP_OBJ_TO_PTR(self_in); uint8_t memaddr_buf[4]; size_t memaddr_len = 0; for (int16_t i = addrsize - 8; i >= 0; i -= 8) { memaddr_buf[memaddr_len++] = memaddr >> i; } struct rt_i2c_msg msg[2]; msg[0].buf = memaddr_buf; msg[0].len = (addrsize + 7)/8; msg[0].flags = RT_I2C_WR; msg[0].addr = addr; msg[1].buf = buf; msg[1].len = len; msg[1].flags = RT_I2C_RD; msg[1].addr = addr; if (rt_i2c_transfer(self->i2c_bus, msg, 2) != 2) return -MP_EIO; return len; } STATIC int mp_machine_i2c_readfrom(mp_obj_base_t *self_in, uint16_t addr, uint8_t *dest, size_t len, bool stop) { machine_hard_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); return rt_i2c_master_recv(self->i2c_bus, addr, 0, dest, len); } STATIC int mp_machine_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uint8_t *src, size_t len, bool stop) { uint8_t buf[1] = {0}; machine_hard_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); if (len == 0){ len = 1; if (src == NULL){ src = buf; } return !rt_i2c_master_send(self->i2c_bus, addr, 0, src, len); } else if (src == NULL){ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "buf must not NULL")); } return rt_i2c_master_send(self->i2c_bus, addr, 0, src, len); } STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) { mp_obj_base_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t list = mp_obj_new_list(0, NULL); // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved for (int addr = 0x08; addr < 0x78; ++addr) { int ret = mp_machine_i2c_writeto(self, addr, NULL, 0, true); if (ret == 0) { mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr)); } } return list; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_scan_obj, machine_i2c_scan); STATIC mp_obj_t machine_i2c_readfrom(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); vstr_t vstr; vstr_init_len(&vstr, mp_obj_get_int(args[2])); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); int ret = mp_machine_i2c_readfrom(self, addr, (uint8_t*)vstr.buf, vstr.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_obj, 3, 4, machine_i2c_readfrom); STATIC mp_obj_t machine_i2c_readfrom_into(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); int ret = mp_machine_i2c_readfrom(self, addr, bufinfo.buf, bufinfo.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_into_obj, 3, 4, machine_i2c_readfrom_into); STATIC mp_obj_t machine_i2c_writeto(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); int ret = mp_machine_i2c_writeto(self, addr, bufinfo.buf, bufinfo.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } // return number of acks received return MP_OBJ_NEW_SMALL_INT(ret); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writeto_obj, 3, 4, machine_i2c_writeto); STATIC mp_obj_t machine_i2c_writevto(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); mp_int_t addr = mp_obj_get_int(args[1]); // Get the list of data buffer(s) to write size_t nitems; const mp_obj_t *items; mp_obj_get_array(args[2], &nitems, (mp_obj_t**)&items); // Get the stop argument bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); // Extract all buffer data, skipping zero-length buffers size_t alloc = nitems == 0 ? 1 : nitems; size_t nbufs = 0; struct rt_i2c_msg *bufs = mp_local_alloc(alloc * sizeof(struct rt_i2c_msg)); for (; nitems--; ++items) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(*items, &bufinfo, MP_BUFFER_READ); if (bufinfo.len > 0) { bufs[nbufs].addr = addr; bufs[nbufs].flags = RT_I2C_WR; bufs[nbufs].len = bufinfo.len; bufs[nbufs++].buf = bufinfo.buf; } } // Make sure there is at least one buffer, empty if needed if (nbufs == 0) { bufs[0].len = 0; bufs[0].buf = NULL; nbufs = 1; } // Do the I2C transfer machine_hard_i2c_obj_t *i2c_p = (machine_hard_i2c_obj_t*)self; int ret = rt_i2c_transfer(i2c_p->i2c_bus, bufs, nbufs); mp_local_free(bufs); if (ret < 0) { mp_raise_OSError(-ret); } // Return number of acks received return MP_OBJ_NEW_SMALL_INT(ret); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writevto_obj, 3, 4, machine_i2c_writevto); STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_n, ARG_addrsize }; mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); // create the buffer to store data into vstr_t vstr; vstr_init_len(&vstr, mp_obj_get_int(args[ARG_n].u_obj)); // do the transfer int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, (uint8_t*)vstr.buf, vstr.len); if (ret < 0) { mp_raise_OSError(-ret); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom_mem); STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize }; mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); // get the buffer to store data into mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE); // do the transfer int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len); if (ret < 0) { mp_raise_OSError(-ret); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_into_obj, 1, machine_i2c_readfrom_mem_into); STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize }; mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); // get the buffer to write the data from mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ); // do the transfer int ret = write_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len); if (ret < 0) { mp_raise_OSError(-ret); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_writeto_mem_obj, 1, machine_i2c_writeto_mem); STATIC const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&machine_i2c_scan_obj) }, // standard bus operations { MP_ROM_QSTR(MP_QSTR_readfrom), MP_ROM_PTR(&machine_i2c_readfrom_obj) }, { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&machine_i2c_readfrom_into_obj) }, { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&machine_i2c_writeto_obj) }, { MP_ROM_QSTR(MP_QSTR_writevto), MP_ROM_PTR(&machine_i2c_writevto_obj) }, // memory operations { MP_ROM_QSTR(MP_QSTR_readfrom_mem), MP_ROM_PTR(&machine_i2c_readfrom_mem_obj) }, { MP_ROM_QSTR(MP_QSTR_readfrom_mem_into), MP_ROM_PTR(&machine_i2c_readfrom_mem_into_obj) }, { MP_ROM_QSTR(MP_QSTR_writeto_mem), MP_ROM_PTR(&machine_i2c_writeto_mem_obj) }, }; /******************************************************************************/ /* MicroPython bindings for machine API */ mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { char iic_device[RT_NAME_MAX]; snprintf(iic_device, sizeof(iic_device), "i2c%d", mp_obj_get_int(all_args[0])); struct rt_i2c_bus_device *i2c_bus = rt_i2c_bus_device_find(iic_device); if (i2c_bus == RT_NULL) { mp_printf(&mp_plat_print, "can't find %s device\r\n", iic_device); nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2C(%s) doesn't exist", iic_device)); } // create new hard I2C object machine_hard_i2c_obj_t *self = m_new_obj(machine_hard_i2c_obj_t); self->base.type = &machine_hard_i2c_type; self->i2c_bus = i2c_bus; return (mp_obj_t) self; } MP_DEFINE_CONST_DICT(mp_machine_hard_i2c_locals_dict, machine_i2c_locals_dict_table); STATIC const mp_machine_i2c_p_t machine_hard_i2c_p = { .start = NULL, .stop = NULL, .read = NULL, .write = NULL, .transfer = NULL, .transfer_single = NULL, }; STATIC const mp_obj_type_t machine_hard_i2c_type = { { &mp_type_type }, .name = MP_QSTR_I2C, .print = machine_hard_i2c_print, .make_new = machine_hard_i2c_make_new, .protocol = &machine_hard_i2c_p, .locals_dict = (mp_obj_dict_t*)&mp_machine_hard_i2c_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_I2C ================================================ FILE: port/modules/machine/machine_hw_spi.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 SummerGift * * 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/mphal.h" #include "extmod/machine_spi.h" #ifdef MICROPYTHON_USING_MACHINE_SPI #ifndef RT_USING_SPI #error "Please define the RT_USING_SPI on 'rtconfig.h'" #endif STATIC const mp_obj_type_t machine_hard_spi_type; typedef struct _machine_hard_spi_obj_t { mp_obj_base_t base; struct rt_spi_device *spi_device; } machine_hard_spi_obj_t; STATIC void machine_hard_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t*)self_in; mp_printf(print,"SPI(device port : %s)",self->spi_device->parent.parent.name); } mp_obj_t machine_hard_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { char spi_dev_name[RT_NAME_MAX]; snprintf(spi_dev_name, sizeof(spi_dev_name), "spi%d", mp_obj_get_int(all_args[0])); struct rt_spi_device *rt_spi_device = (struct rt_spi_device *) rt_device_find(spi_dev_name); if (rt_spi_device == RT_NULL || rt_spi_device->parent.type != RT_Device_Class_SPIDevice) { mp_printf(&mp_plat_print, "ERROR: SPI device %s not found!\n", spi_dev_name); nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "SPI(%s) doesn't exist", spi_dev_name)); } // create new hard SPI object machine_hard_spi_obj_t *self = m_new_obj(machine_hard_spi_obj_t); self->base.type = &machine_hard_spi_type; self->spi_device = rt_spi_device; return (mp_obj_t) self; } //SPI.init( baudrate=100000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB/LSB ) STATIC void machine_hard_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t*)self_in; rt_uint8_t mode = 0; int baudrate = mp_obj_get_int(pos_args[0]); int polarity = mp_obj_get_int(pos_args[1]); int phase = mp_obj_get_int(pos_args[2]); int bits = mp_obj_get_int(pos_args[3]); int firstbit = mp_obj_get_int(pos_args[4]); if(!polarity && !phase) { mode = RT_SPI_MODE_0; } if(!polarity && phase) { mode = RT_SPI_MODE_1; } if(polarity && !phase) { mode = RT_SPI_MODE_2; } if(polarity && phase) { mode = RT_SPI_MODE_3; } if(firstbit) { mode |= RT_SPI_MSB; } else { mode |= RT_SPI_LSB; } /* config spi */ { struct rt_spi_configuration cfg; cfg.data_width = bits; cfg.mode = mode; cfg.max_hz = baudrate; rt_spi_configure(self->spi_device, &cfg); } } STATIC void machine_hard_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t*)self_in; if (src && dest) { rt_spi_send_then_recv(self->spi_device, src, len, dest, len); } else if (src) { rt_spi_send(self->spi_device, src, len); } else { rt_spi_recv(self->spi_device, dest, len); } } STATIC const mp_machine_spi_p_t machine_hard_spi_p = { .init = machine_hard_spi_init, .deinit = NULL, .transfer = machine_hard_spi_transfer, }; STATIC const mp_obj_type_t machine_hard_spi_type = { { &mp_type_type }, .name = MP_QSTR_SPI, .print = machine_hard_spi_print, .make_new = machine_hard_spi_make_new, .protocol = &machine_hard_spi_p, .locals_dict = (mp_obj_t)&mp_machine_spi_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_SPI ================================================ FILE: port/modules/machine/machine_lcd.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 SummerGift * * 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/mphal.h" #include "py/runtime.h" #include "py/mperrno.h" #if MICROPY_PY_MACHINE_LCD #include #include "machine_lcd.h" #include #define MAX_CO (2400 - 1) typedef struct _machine_lcd_obj_t { mp_obj_base_t base; } machine_lcd_obj_t; STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, msg)); } } /// \classmethod \constructor(skin_position) /// /// Construct an LCD object. STATIC mp_obj_t machine_lcd_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); // create lcd object machine_lcd_obj_t *lcd = m_new_obj(machine_lcd_obj_t); lcd->base.type = &machine_lcd_type; return MP_OBJ_FROM_PTR(lcd); } /// \method light(value) /// /// Turn the backlight on/off. True or 1 turns it on, False or 0 turns it off. STATIC mp_obj_t machine_lcd_light(mp_obj_t self_in, mp_obj_t value) { if (mp_obj_is_true(value)) { lcd_display_on(); // set pin high to turn backlight on } else { lcd_display_off();// set pin low to turn backlight off } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_lcd_light_obj, machine_lcd_light); /// \method fill(colour) /// /// Fill the screen with the given colour. /// STATIC mp_obj_t machine_lcd_fill(mp_obj_t self_in, mp_obj_t col_in) { int col = mp_obj_get_int(col_in); lcd_clear(col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_lcd_fill_obj, machine_lcd_fill); /// \method pixel(x, y, colour) /// /// Set the pixel at `(x, y)` to the given colour. /// STATIC mp_obj_t machine_lcd_pixel(size_t n_args, const mp_obj_t *args) { int x = mp_obj_get_int(args[1]); int y = mp_obj_get_int(args[2]); error_check((x >= 0 && x <= MAX_CO) && (y >= 0 && y <= MAX_CO) , "The min/max X/Y coordinates is 0/239"); int col = mp_obj_get_int(args[3]); lcd_draw_point_color(x, y, col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_pixel_obj, 4, 4, machine_lcd_pixel); /// \method text(str, x, y, size) /// /// Draw the given text to the position `(x, y)` using the given size (16 24 32). /// STATIC mp_obj_t machine_lcd_text(size_t n_args, const mp_obj_t *args) { size_t len; const char *data = mp_obj_str_get_data(args[1], &len); int x = mp_obj_get_int(args[2]); int y = mp_obj_get_int(args[3]); int size = mp_obj_get_int(args[4]); error_check((x >= 0 && x <= MAX_CO) && (y >= 0 && y <= MAX_CO) , "The min/max X/Y coordinates is 0/239"); error_check(size == 16 || size == 24 || size == 32, "lcd only support font size 16 24 32"); lcd_show_string(x, y, size, data); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_text_obj, 5, 5, machine_lcd_text); /// \method line(x1, y1, x2, y2) /// /// display a line on the lcd, from (x1, y1) to (x2, y2). /// STATIC mp_obj_t machine_lcd_line(size_t n_args, const mp_obj_t *args) { int x1 = mp_obj_get_int(args[1]); int y1 = mp_obj_get_int(args[2]); int x2 = mp_obj_get_int(args[3]); int y2 = mp_obj_get_int(args[4]); error_check((x1 >= 0 && x1 <= MAX_CO) && (y1 >= 0 && y1 <= MAX_CO) , "The min/max X/Y coordinates is 0/239"); error_check((x2 >= 0 && x2 <= MAX_CO) && (y2 >= 0 && y2 <= MAX_CO) , "The min/max X/Y coordinates is 0/239"); lcd_draw_line(x1, y1, x2, y2); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_line_obj, 5, 5, machine_lcd_line); /// \method rectangle(x1, y1, x2, y2) /// /// display a rectangle on the lcd, from (x1, y1) to (x2, y2). /// STATIC mp_obj_t machine_lcd_rectangle(size_t n_args, const mp_obj_t *args) { int x1 = mp_obj_get_int(args[1]); int y1 = mp_obj_get_int(args[2]); int x2 = mp_obj_get_int(args[3]); int y2 = mp_obj_get_int(args[4]); error_check((x1 >= 0 && x1 <= MAX_CO) && (y1 >= 0 && y1 <= MAX_CO) , "The min/max X/Y coordinates is 0/239"); error_check((x2 >= 0 && x2 <= MAX_CO) && (y2 >= 0 && y2 <= MAX_CO) , "The min/max X/Y coordinates is 0/239"); lcd_draw_rectangle(x1, y1, x2, y2); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_rectangle_obj, 5, 5, machine_lcd_rectangle); /// \method circle(x1, y1, r) /// /// display a circle on the lcd, center(x1, y1) R = r. /// STATIC mp_obj_t machine_lcd_circle(size_t n_args, const mp_obj_t *args) { int x1 = mp_obj_get_int(args[1]); int y1 = mp_obj_get_int(args[2]); int r = mp_obj_get_int(args[3]); error_check((x1 >= 0 && x1 <= MAX_CO) && (y1 >= 0 && y1 <= MAX_CO) , "The min/max X/Y coordinates is 0/239"); lcd_draw_circle(x1, y1, r); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_circle_obj, 4, 4, machine_lcd_circle); /// \method set_color(back, fore) /// /// Set background color and foreground color. /// STATIC mp_obj_t machine_lcd_set_color(size_t n_args, const mp_obj_t *args) { rt_uint16_t back = mp_obj_get_int(args[1]); rt_uint16_t fore = mp_obj_get_int(args[2]); lcd_set_color(back, fore); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_set_color_obj, 3, 3, machine_lcd_set_color); /// \method show_image array /// /// display the image on the lcd.. /// @param x x position /// @param y y position /// @param length length of image /// @param wide wide of image /// @param p image_array STATIC mp_obj_t machine_lcd_show_image(size_t n_args, const mp_obj_t *args) { rt_uint16_t x = mp_obj_get_int(args[1]); rt_uint16_t y = mp_obj_get_int(args[2]); rt_uint16_t length = mp_obj_get_int(args[3]); rt_uint16_t wide = mp_obj_get_int(args[4]); mp_buffer_info_t bufinfo; if (mp_get_buffer(args[5], &bufinfo, MP_BUFFER_READ)) { lcd_show_image( x, y, length, wide, (const rt_uint8_t *)bufinfo.buf); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_show_image_obj, 6, 6, machine_lcd_show_image); STATIC rt_uint16_t rgb888to565(rt_uint32_t RGB) { int R, G, B; R = (RGB >> 19) & 0x1F; G = (RGB >> 10) & 0x3F; B = (RGB >> 3) & 0x1F; return (R << 11) | (G << 5) | B; } /// \method show_image array /// /// display the image on the lcd. /// @param x x position /// @param y y position /// @param file bmp file pathname STATIC mp_obj_t machine_lcd_show_bmp(size_t n_args, const mp_obj_t *args) { #define BMP_INFO_SIZE 54 rt_uint16_t x = mp_obj_get_int(args[1]); rt_uint16_t y = mp_obj_get_int(args[2]); const char *pathname = mp_obj_str_get_str(args[3]); int fd, len; fd = open(pathname, O_RDONLY, 0); if (fd < 0) { mp_raise_OSError(MP_EINVAL); } void *bmp_info = rt_malloc(BMP_INFO_SIZE); if (bmp_info == RT_NULL) { mp_raise_OSError(MP_ENOMEM); } len = read(fd, bmp_info, BMP_INFO_SIZE); if (len < 0) { close(fd); mp_raise_OSError(MP_EINVAL); } rt_uint32_t width = *(rt_uint32_t *)(bmp_info + 18); rt_uint32_t heigth = *(rt_uint32_t *)(bmp_info + 22); rt_uint16_t bit_count = *(rt_uint16_t *)(bmp_info + 28); if (bit_count != 32) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "bit count : %d, only support 32-bit bmp picture", bit_count)); } void *image_buf = rt_malloc(2 * width); if (image_buf == RT_NULL) { mp_raise_OSError(MP_ENOMEM); } void *row_buf = rt_malloc(4 * width); if (row_buf == RT_NULL) { mp_raise_OSError(MP_ENOMEM); } int image_index, row_index; rt_uint16_t rgb565_temp; for(int i = 0; i < heigth; i++) { image_index = 0; row_index = 0; len = read(fd, row_buf, 4 * width); if (len < 0) { close(fd); mp_raise_OSError(MP_EINVAL); } while(row_index < (4 * width)) { rgb565_temp = rgb888to565(*(rt_uint32_t *)(row_buf + row_index)); *(rt_uint8_t *)(image_buf + image_index) = (rgb565_temp >> 8); *(rt_uint8_t *)(image_buf + image_index + 1) = rgb565_temp & 0xff; row_index += 4; image_index += 2; } lcd_show_image( x, y--, width, 1, (const rt_uint8_t *)image_buf); } close(fd); rt_free(bmp_info); rt_free(image_buf); rt_free(row_buf); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lcd_show_bmp_obj, 4, 4, machine_lcd_show_bmp); STATIC const mp_rom_map_elem_t machine_lcd_locals_dict_table[] = { // instance methods { MP_ROM_QSTR(MP_QSTR_light), MP_ROM_PTR(&machine_lcd_light_obj) }, { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&machine_lcd_fill_obj) }, { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&machine_lcd_pixel_obj) }, { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&machine_lcd_text_obj) }, { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&machine_lcd_line_obj) }, { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&machine_lcd_rectangle_obj) }, { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&machine_lcd_circle_obj) }, { MP_ROM_QSTR(MP_QSTR_set_color), MP_ROM_PTR(&machine_lcd_set_color_obj) }, { MP_ROM_QSTR(MP_QSTR_show_image), MP_ROM_PTR(&machine_lcd_show_image_obj) }, { MP_ROM_QSTR(MP_QSTR_show_bmp), MP_ROM_PTR(&machine_lcd_show_bmp_obj) }, // color { MP_ROM_QSTR(MP_QSTR_WHITE), MP_ROM_INT(WHITE) }, { MP_ROM_QSTR(MP_QSTR_BLACK), MP_ROM_INT(BLACK) }, { MP_ROM_QSTR(MP_QSTR_BLUE), MP_ROM_INT(BLUE) }, { MP_ROM_QSTR(MP_QSTR_BRED), MP_ROM_INT(BRED) }, { MP_ROM_QSTR(MP_QSTR_GRED), MP_ROM_INT(GRED) }, { MP_ROM_QSTR(MP_QSTR_GBLUE), MP_ROM_INT(GBLUE) }, { MP_ROM_QSTR(MP_QSTR_RED), MP_ROM_INT(RED) }, { MP_ROM_QSTR(MP_QSTR_MAGENTA), MP_ROM_INT(MAGENTA) }, { MP_ROM_QSTR(MP_QSTR_GREEN), MP_ROM_INT(GREEN) }, { MP_ROM_QSTR(MP_QSTR_CYAN), MP_ROM_INT(CYAN) }, { MP_ROM_QSTR(MP_QSTR_YELLOW), MP_ROM_INT(YELLOW) }, { MP_ROM_QSTR(MP_QSTR_BROWN), MP_ROM_INT(BROWN) }, { MP_ROM_QSTR(MP_QSTR_BRRED), MP_ROM_INT(BRRED) }, { MP_ROM_QSTR(MP_QSTR_GRAY), MP_ROM_INT(GRAY) }, { MP_ROM_QSTR(MP_QSTR_GRAY175), MP_ROM_INT(GRAY175) }, { MP_ROM_QSTR(MP_QSTR_GRAY151), MP_ROM_INT(GRAY151) }, { MP_ROM_QSTR(MP_QSTR_GRAY187), MP_ROM_INT(GRAY187) }, { MP_ROM_QSTR(MP_QSTR_GRAY240), MP_ROM_INT(GRAY240) }, }; STATIC MP_DEFINE_CONST_DICT(machine_lcd_locals_dict, machine_lcd_locals_dict_table); const mp_obj_type_t machine_lcd_type = { { &mp_type_type }, .name = MP_QSTR_LCD, .make_new = machine_lcd_make_new, .locals_dict = (mp_obj_dict_t*)&machine_lcd_locals_dict, }; #endif // MICROPY_PY_MACHINE_LCD ================================================ FILE: port/modules/machine/machine_lcd.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 SummerGift * * 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_MACHINE_LCD_H #define MICROPY_INCLUDED_MACHINE_LCD_H extern const mp_obj_type_t machine_lcd_type; #endif // MICROPY_INCLUDED_MACHINE_LCD_H ================================================ FILE: port/modules/machine/machine_pin.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include "py/runtime.h" #include "py/gc.h" #include "py/mphal.h" #include "py/mperrno.h" #include "py/stream.h" #include "modmachine.h" #if MICROPY_PY_PIN #define GPIO_MODE_IN ((uint32_t)0x00000000) /*!< Input Floating Mode */ #define GPIO_MODE_OUT_PP ((uint32_t)0x00000001) /*!< Output Push Pull Mode */ #define GPIO_MODE_OUT_OD ((uint32_t)0x00000011) /*!< Output Open Drain Mode */ #define GPIO_MODE_AF_PP ((uint32_t)0x00000002) /*!< Alternate Function Push Pull Mode */ #define GPIO_MODE_AF_OD ((uint32_t)0x00000012) /*!< Alternate Function Open Drain Mode */ #define GPIO_MODE_ANALOG ((uint32_t)0x00000003) /*!< Analog Mode */ #define GPIO_NOPULL ((uint32_t)0x00000000) /*!< No Pull-up or Pull-down activation */ #define GPIO_PULLUP ((uint32_t)0x00000001) /*!< Pull-up activation */ #define GPIO_PULLDOWN ((uint32_t)0x00000002) /*!< Pull-down activation */ #define GPIO_MODE_IT_RISING ((uint32_t)0x10110000) /*!< External Interrupt Mode with Rising edge trigger detection */ #define GPIO_MODE_IT_FALLING ((uint32_t)0x10210000) /*!< External Interrupt Mode with Falling edge trigger detection */ #define GPIO_MODE_IT_RISING_FALLING ((uint32_t)0x10310000) /*!< External Interrupt Mode with Rising/Falling edge trigger detection */ const mp_obj_base_t machine_pin_obj_template = {&machine_pin_type}; void mp_pin_od_write(void *machine_pin, int stat) { if (stat == PIN_LOW) { rt_pin_mode(((machine_pin_obj_t *)machine_pin)->pin, PIN_MODE_OUTPUT); rt_pin_write(((machine_pin_obj_t *)machine_pin)->pin, stat); } else { rt_pin_mode(((machine_pin_obj_t *)machine_pin)->pin, PIN_MODE_INPUT_PULLUP); } } void mp_hal_pin_open_set(void *machine_pin, int mode) { rt_pin_mode(((machine_pin_obj_t *)machine_pin)->pin, mode); } char* mp_hal_pin_get_name(void *machine_pin) { return ((machine_pin_obj_t *)machine_pin)->name; } STATIC mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pin_obj_t *self = self_in; mp_printf(print, "", self->pin); } // constructor(drv_name, pin, ...) mp_obj_t mp_pin_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, MP_OBJ_FUN_ARGS_MAX, true); // get the wanted port if (!MP_OBJ_IS_TYPE(args[0], &mp_type_tuple)) { mp_raise_ValueError("Pin id must be tuple of (\"GPIO_x\", pin#)"); } mp_obj_t *items; mp_obj_get_array_fixed_n(args[0], 2, &items); const char *pin_name = mp_obj_str_get_str(items[0]); int wanted_pin = mp_obj_get_int(items[1]); machine_pin_obj_t *pin = m_new_obj(machine_pin_obj_t); if (!pin) { mp_raise_OSError(MP_ENOMEM); } strncpy(pin->name, pin_name, sizeof(pin->name)); pin->base = machine_pin_obj_template; pin->pin = wanted_pin; if (n_args > 1 || n_kw > 0) { // pin mode given, so configure this GPIO mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); machine_pin_obj_init_helper(pin, n_args - 1, args + 1, &kw_args); } return (mp_obj_t)pin; } // pin.init(mode, pull=None, *, value) STATIC mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_mode, ARG_pull, ARG_value }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_pull, MP_ARG_OBJ, {.u_obj = mp_const_none}}, { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // get io mode uint mode = args[ARG_mode].u_int; // get pull mode uint pull = GPIO_NOPULL; if (args[ARG_pull].u_obj != mp_const_none) { pull = mp_obj_get_int(args[ARG_pull].u_obj); } switch(mode) { case GPIO_MODE_IN: { if (pull == GPIO_PULLUP) { mode = PIN_MODE_INPUT_PULLUP; } else if (pull == GPIO_PULLDOWN) { mode = PIN_MODE_INPUT_PULLDOWN; } else { mode = PIN_MODE_INPUT; } break; } case GPIO_MODE_OUT_PP : { mode = PIN_MODE_OUTPUT; break; } case GPIO_MODE_OUT_OD : { mode = PIN_MODE_OUTPUT_OD; break; } case GPIO_MODE_AF_PP : case GPIO_MODE_AF_OD : case GPIO_MODE_ANALOG : //TODO mp_raise_NotImplementedError("not implemented pin mode"); } rt_pin_mode(self->pin, mode); // get initial value if (args[ARG_value].u_obj != MP_OBJ_NULL) { rt_pin_write(self->pin, mp_obj_is_true(args[ARG_value].u_obj)); } return mp_const_none; } // fast method for getting/setting pin value STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, 1, false); machine_pin_obj_t *self = self_in; if (n_args == 0) { return mp_obj_new_bool(rt_pin_read(self->pin)); } else { rt_pin_write(self->pin, mp_obj_is_true(args[0])); return mp_const_none; } } // pin.init(mode, pull) STATIC mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); } MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); // pin.value([value]) STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { return machine_pin_call(args[0], n_args - 1, 0, args + 1); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value); // pin.name() STATIC mp_obj_t machine_pin_name(size_t n_args, const mp_obj_t *args) { machine_pin_obj_t *self = (machine_pin_obj_t *)args[0]; return mp_obj_new_str(self->name, strlen(self->name)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_name_obj, 1, 2, machine_pin_name); // pin.pin() STATIC mp_obj_t machine_pin_pin(size_t n_args, const mp_obj_t *args) { return MP_OBJ_NEW_SMALL_INT(((machine_pin_obj_t *)args[0])->pin); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_pin_obj, 1, 2, machine_pin_pin); STATIC mp_uint_t machine_pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { (void)errcode; machine_pin_obj_t *self = self_in; switch (request) { case MP_PIN_READ: { uint32_t pin_val = rt_pin_read(self->pin); return pin_val; } case MP_PIN_WRITE: { rt_pin_write(self->pin, arg); return 0; } } *errcode = MP_EINVAL; return MP_STREAM_ERROR; } STATIC void machine_pin_isr_handler(void *arg) { machine_pin_obj_t *self = arg; mp_sched_schedule(self->pin_isr_cb, MP_OBJ_FROM_PTR(self)); } // pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING) STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_handler, ARG_trigger, ARG_wake }; static const mp_arg_t allowed_args[] = { { MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_trigger, MP_ARG_INT, {.u_int = PIN_IRQ_MODE_RISING} }, }; machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); 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); if (n_args > 1 || kw_args->used != 0) { // configure irq self->pin_isr_cb = args[ARG_handler].u_obj; uint32_t trigger = args[ARG_trigger].u_int; rt_pin_mode(self->pin, PIN_MODE_INPUT_PULLUP); rt_pin_attach_irq(self->pin, trigger, machine_pin_isr_handler, (void*)self); rt_pin_irq_enable(self->pin, PIN_IRQ_ENABLE); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq); STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { // instance methods { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) }, { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&machine_pin_name_obj) }, { MP_ROM_QSTR(MP_QSTR_pin), MP_ROM_PTR(&machine_pin_pin_obj) }, { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, // class constants { MP_ROM_QSTR(MP_QSTR_ALT_OD), MP_ROM_INT(GPIO_MODE_AF_OD) }, { MP_ROM_QSTR(MP_QSTR_ALT_PP), MP_ROM_INT(GPIO_MODE_AF_PP) }, { MP_ROM_QSTR(MP_QSTR_ANALOG), MP_ROM_INT(GPIO_MODE_ANALOG) }, { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_IN) }, { MP_ROM_QSTR(MP_QSTR_OUT_PP), MP_ROM_INT(GPIO_MODE_OUT_PP) }, { MP_ROM_QSTR(MP_QSTR_OUT_OD), MP_ROM_INT(GPIO_MODE_OUT_OD) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULLDOWN) }, { MP_ROM_QSTR(MP_QSTR_PULL_NONE), MP_ROM_INT(GPIO_NOPULL) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULLUP) }, { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(PIN_IRQ_MODE_RISING) }, { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(PIN_IRQ_MODE_FALLING) }, { MP_ROM_QSTR(MP_QSTR_IRQ_RISING_FALLING), MP_ROM_INT(PIN_IRQ_MODE_RISING_FALLING) }, { MP_ROM_QSTR(MP_QSTR_IRQ_LOW_LEVEL), MP_ROM_INT(PIN_IRQ_MODE_LOW_LEVEL) }, { MP_ROM_QSTR(MP_QSTR_IRQ_HIGH_LEVEL), MP_ROM_INT(PIN_IRQ_MODE_HIGH_LEVEL) }, }; STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); STATIC const mp_pin_p_t machine_pin_pin_p = { .ioctl = machine_pin_ioctl, }; const mp_obj_type_t machine_pin_type = { { &mp_type_type }, .name = MP_QSTR_Pin, .print = machine_pin_print, .make_new = mp_pin_make_new, .call = machine_pin_call, .protocol = &machine_pin_pin_p, .locals_dict = (mp_obj_t)&machine_pin_locals_dict, }; #endif ================================================ FILE: port/modules/machine/machine_pwm.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include "py/nlr.h" #include "py/runtime.h" #include "modmachine.h" #include "mphalport.h" #ifdef MICROPYTHON_USING_MACHINE_PWM #include #include #define MP_PWM_PULSE_MAX 255 #define MP_PWM_PERIOD_GET(freq) (1000000000 / (freq)) #define MP_PWM_PULSE_GET(period, duty) ((period) / MP_PWM_PULSE_MAX * (duty)) extern const mp_obj_type_t machine_pwm_type; typedef struct _machine_pwm_obj_t { mp_obj_base_t base; struct rt_device_pwm *pwm_device; char dev_name[RT_NAME_MAX]; uint8_t is_init; int8_t id; uint8_t channel; uint8_t duty; uint32_t freq; } machine_pwm_obj_t; STATIC void machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pwm_obj_t *self = self_in; mp_printf(print, "PWM(%p; ", self); if (self->id >= 0) { mp_printf(print, "pwm_id=%d, ", self->id); } else { mp_printf(print, "pwm_name=%s, ", self->dev_name); } mp_printf(print, "channel=%d, ", self->channel); mp_printf(print, "freq=%d, ", self->freq); mp_printf(print, "duty=%d)", self->duty); } STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, msg)); } } STATIC void machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { rt_err_t result = RT_EOK; uint32_t period = 0, pulse = 0; char pwm_dev_name[RT_NAME_MAX]; struct rt_device_pwm *pwm_device = RT_NULL; enum { ARG_channel, ARG_freq, ARG_duty }; static const mp_arg_t allowed_args[] = { { MP_QSTR_channel, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_freq, MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_duty, MP_ARG_INT, {.u_int = 0} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); int tval = args[ARG_channel].u_int; if ((tval < 0) || (tval > 4)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Bad channel %d", tval)); } self->channel = tval; tval = args[ARG_freq].u_int; if ((tval < 1) || (tval > 156250)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Bad frequency %d", tval)); } self->freq = tval; tval = args[ARG_duty].u_int; if ((tval < 0) || (tval > 255)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Bad duty %d", tval)); } self->duty = tval; if (self->id >= 0) { rt_snprintf(pwm_dev_name, sizeof(pwm_dev_name), "pwm%d", self->id); } else { rt_strncpy(pwm_dev_name, self->dev_name, RT_NAME_MAX); } pwm_device = (struct rt_device_pwm *) rt_device_find(pwm_dev_name); if (pwm_device == RT_NULL || pwm_device->parent.type != RT_Device_Class_Miscellaneous) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "PWM(%s) don't exist", pwm_dev_name)); } self->pwm_device = pwm_device; // get period number by frequency period = MP_PWM_PERIOD_GET(self->freq); // get pulse number by duty pulse = MP_PWM_PULSE_GET(period, self->duty); result = rt_pwm_set(pwm_device, self->channel, period, pulse); error_check(result == RT_EOK, "PWM set information error"); result = rt_pwm_enable(pwm_device, self->channel); error_check(result == RT_EOK, "PWM enable error"); self->is_init = RT_TRUE; } STATIC mp_obj_t machine_pwm_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, MP_OBJ_FUN_ARGS_MAX, true); // create PWM object from the given pin machine_pwm_obj_t *self = m_new_obj(machine_pwm_obj_t); self->base.type = &machine_pwm_type; self->is_init = RT_FALSE; // check input PWM device name or ID if (mp_obj_is_small_int(args[0])) { self->id = mp_obj_get_int(args[0]); } else if (mp_obj_is_qstr(args[0])) { self->id = -1; rt_strncpy(self->dev_name, mp_obj_str_get_str(args[0]), RT_NAME_MAX); } else { error_check(0, "Input PWM device name or ID error."); } self->channel = 0; self->freq = 1; self->duty = 0; mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); return MP_OBJ_FROM_PTR(self); } STATIC mp_obj_t machine_pwm_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { machine_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(machine_pwm_init_obj, 1, machine_pwm_init); STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); rt_err_t result = RT_EOK; if (self->is_init == RT_TRUE) { result = rt_pwm_disable(self->pwm_device, self->channel); error_check(result == RT_EOK, "PWM disable error"); self->is_init = RT_FALSE; } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit); STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *args) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t period = 0, pulse = 0; rt_err_t result = RT_EOK; error_check(self->is_init == RT_TRUE, "PWM device uninitialized"); if (n_args == 1) { // get return MP_OBJ_NEW_SMALL_INT(self->freq); } // set int tval = mp_obj_get_int(args[1]); if ((tval < 1) || (tval > 156250)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Bad frequency %d", tval)); } // get period number by frequency period = MP_PWM_PERIOD_GET(tval); // get pulse number by duty pulse = MP_PWM_PULSE_GET(period, self->duty); result = rt_pwm_set(self->pwm_device, self->channel, period, pulse); error_check(result == RT_EOK, "PWM set information error"); self->freq = tval; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freq_obj, 1, 2, machine_pwm_freq); STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t period = 0, pulse = 0; rt_err_t result = RT_EOK; error_check(self->is_init == RT_TRUE, "PWM device uninitialized"); if (n_args == 1) { // get return MP_OBJ_NEW_SMALL_INT(self->duty); } // set int tval = mp_obj_get_int(args[1]); if ((tval < 0) || (tval > 255)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Bad duty %d", tval)); } // get period number by frequency period = MP_PWM_PERIOD_GET(self->freq); // get pulse number by duty pulse = MP_PWM_PULSE_GET(period, tval); result = rt_pwm_set(self->pwm_device, self->channel, period, pulse); error_check(result == RT_EOK, "PWM set information error"); self->duty = tval; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_obj, 1, 2, machine_pwm_duty); STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pwm_init_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_pwm_freq_obj) }, { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&machine_pwm_duty_obj) }, }; STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table); const mp_obj_type_t machine_pwm_type = { { &mp_type_type }, .name = MP_QSTR_PWM, .print = machine_pwm_print, .make_new = machine_pwm_make_new, .locals_dict = (mp_obj_dict_t *) &machine_pwm_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_PWM ================================================ FILE: port/modules/machine/machine_pwm.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef MICROPY_INCLUDED_MACHINE_PWM_H #define MICROPY_INCLUDED_MACHINE_PWM_H #include "py/obj.h" #include extern const mp_obj_type_t machine_pwm_type; #endif // MICROPY_INCLUDED_MACHINE_PWM_H ================================================ FILE: port/modules/machine/machine_rtc.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include "py/nlr.h" #include "py/obj.h" #include "py/runtime.h" #include "py/mphal.h" #include "lib/timeutils/timeutils.h" #include "modmachine.h" #ifdef MICROPYTHON_USING_MACHINE_RTC #include #include #include #define MP_YEAR_BASE 1900 const mp_obj_type_t machine_rtc_type; // singleton RTC object STATIC const mp_obj_base_t machine_rtc_obj = {&machine_rtc_type}; STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, msg)); } } STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { #define MP_RTC_DEV_NAME "rtc" rt_device_t rtc_deivce = RT_NULL; // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); // check RTC device rtc_deivce = rt_device_find(MP_RTC_DEV_NAME); if (rtc_deivce == RT_NULL || rtc_deivce->type != RT_Device_Class_RTC) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "RTC(%s) don't exist", MP_RTC_DEV_NAME)); } // return constant object return (mp_obj_t)&machine_rtc_obj; } STATIC mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *args) { if (n_args == 1) { struct tm *tblock; time_t t; // Get time t = time(RT_NULL); tblock = localtime(&t); mp_uint_t seconds = timeutils_mktime(tblock->tm_year + MP_YEAR_BASE, tblock->tm_mon + 1, tblock->tm_mday, tblock->tm_hour, tblock->tm_min, tblock->tm_sec); timeutils_struct_time_t tm; timeutils_seconds_since_2000_to_struct_time(seconds, &tm); mp_obj_t tuple[8] = { mp_obj_new_int(tm.tm_year), mp_obj_new_int(tm.tm_mon), mp_obj_new_int(tm.tm_mday), mp_obj_new_int(tm.tm_wday), mp_obj_new_int(tm.tm_hour), mp_obj_new_int(tm.tm_min), mp_obj_new_int(tm.tm_sec), mp_obj_new_int(0) }; return mp_obj_new_tuple(8, tuple); } else { // Set time rt_err_t result; mp_obj_t *items; mp_obj_get_array_fixed_n(args[1], 8, &items); result = set_date(mp_obj_get_int(items[0]), mp_obj_get_int(items[1]), mp_obj_get_int(items[2])); error_check(result == RT_EOK, "Set date error"); result = set_time(mp_obj_get_int(items[4]), mp_obj_get_int(items[5]), mp_obj_get_int(items[6])); error_check(result == RT_EOK, "Set time error"); return mp_const_none; } } STATIC mp_obj_t machine_rtc_now(mp_uint_t n_args, const mp_obj_t *args) { return machine_rtc_datetime_helper(1, args); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_now_obj, 0, 1, machine_rtc_now); STATIC mp_obj_t machine_rtc_init(mp_uint_t n_args, const mp_obj_t *args) { return machine_rtc_datetime_helper(n_args, args); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_init_obj, 1, 2, machine_rtc_init); STATIC mp_obj_t machine_rtc_deinit(mp_uint_t n_args, const mp_obj_t *args) { rt_err_t result; struct tm tblock; tblock.tm_year = 2015 - MP_YEAR_BASE; tblock.tm_mon = 0; tblock.tm_mday = 1; tblock.tm_hour = 0; tblock.tm_min = 0; tblock.tm_sec = 0; result = set_date(tblock.tm_year + MP_YEAR_BASE, tblock.tm_mon + 1, tblock.tm_mday); error_check(result == RT_EOK, "Set date error"); result = set_time(tblock.tm_hour, tblock.tm_min, tblock.tm_sec); error_check(result == RT_EOK, "Set time error"); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_deinit_obj, 0, 1, machine_rtc_deinit); STATIC const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_rtc_init_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_rtc_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_now), MP_ROM_PTR(&machine_rtc_now_obj) }, }; STATIC MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table); const mp_obj_type_t machine_rtc_type = { { &mp_type_type }, .name = MP_QSTR_RTC, .make_new = machine_rtc_make_new, .locals_dict = (mp_obj_t) &machine_rtc_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_RTC ================================================ FILE: port/modules/machine/machine_rtc.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef MICROPY_INCLUDED_MACHINE_RTC_H #define MICROPY_INCLUDED_MACHINE_RTC_H #include "py/obj.h" #include extern const mp_obj_type_t machine_rtc_type; #endif // MICROPY_INCLUDED_MACHINE_RTC_H ================================================ FILE: port/modules/machine/machine_timer.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include "py/obj.h" #include "py/runtime.h" #include "modmachine.h" #include "mphalport.h" #ifdef MICROPYTHON_USING_MACHINE_TIMER #include #include #include "machine_timer.h" #define MAX_TIMER 17 typedef struct _machine_timer_obj_t { mp_obj_base_t base; rt_device_t timer_device; char dev_name[RT_NAME_MAX]; mp_obj_t timeout_cb; int8_t timerid; uint32_t timeout; rt_bool_t is_repeat; rt_bool_t is_init; } machine_timer_obj_t; const mp_obj_type_t machine_timer_type; STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, msg)); } } STATIC void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_timer_obj_t *self = self_in; mp_printf(print, "Timer(%p; ", self); if (self->timerid >= 0) { mp_printf(print, "timer_id=%d, ", self->timerid); } else { mp_printf(print, "timer_name=%s, ", self->dev_name); } mp_printf(print, "period=%d, ", self->timeout); mp_printf(print, "auto_reload=%d)", self->is_repeat); } STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { machine_timer_obj_t *self = m_new_obj(machine_timer_obj_t); char timer_dev_name[RT_NAME_MAX] = {0}; // check arguments mp_arg_check_num(n_args, n_kw, 1, 1, true); // check input timer device name or ID if (mp_obj_is_small_int(args[0])) { int device_id = mp_obj_get_int(args[0]); self->timerid = device_id; self->timer_device->device_id = device_id; rt_snprintf(timer_dev_name, sizeof(timer_dev_name), "timer%d", mp_obj_get_int(args[0])); } else if (mp_obj_is_qstr(args[0])) { static int device_id = 0; self->timerid = -1; self->timer_device->device_id = device_id++; rt_strncpy(self->dev_name, mp_obj_str_get_str(args[0]), RT_NAME_MAX); rt_strncpy(timer_dev_name, self->dev_name, RT_NAME_MAX); } else { error_check(0, "Input ADC device name or ID error."); } // find timer device self->timer_device = rt_device_find(timer_dev_name); if (self->timer_device == RT_NULL || self->timer_device->type != RT_Device_Class_Timer) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer(%s) don't exist", timer_dev_name)); } // initialize timer device self->base.type = &machine_timer_type; self->timeout = 0; self->timeout_cb = RT_NULL; self->is_repeat = RT_TRUE; self->is_init = RT_FALSE; // return constant object return MP_OBJ_FROM_PTR(self); } static machine_timer_obj_t *timer_self[MAX_TIMER] = {RT_NULL}; STATIC mp_obj_t machine_timer_deinit(mp_obj_t self_in) { machine_timer_obj_t *self = self_in; rt_err_t result = RT_EOK; if (self->is_init == RT_TRUE) { result = rt_device_close(self->timer_device); error_check(result == RT_EOK, "Timer device close error"); self->is_init = RT_FALSE; timer_self[self->timer_device->device_id] = RT_NULL; } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); STATIC rt_err_t timer_event_handler(rt_device_t dev, rt_size_t size) { machine_timer_obj_t *self = timer_self[dev->device_id]; mp_sched_schedule(self->timeout_cb, MP_OBJ_FROM_PTR(self)); return RT_EOK; } STATIC mp_obj_t machine_timer_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { machine_timer_obj_t *self = (machine_timer_obj_t *)args[0]; rt_bool_t result = RT_EOK; int mode = 0; enum { ARG_mode, ARG_period, ARG_callback, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_period, MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_callback, MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; mp_arg_val_t dargs[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, dargs); if (2 == n_args) { self->timeout = dargs[0].u_int; } else if (3 == n_args) { self->is_repeat = dargs[ARG_mode].u_int; self->timeout = dargs[ARG_period].u_int; } else if (4 == n_args) { self->is_repeat = dargs[ARG_mode].u_int; self->timeout = dargs[ARG_period].u_int; self->timeout_cb = dargs[ARG_callback].u_obj; } else { mp_raise_ValueError("invalid format"); } error_check(self->timeout > 0, "Set timeout value error"); if (self->is_init == RT_FALSE) { // open timer device result = rt_device_open(self->timer_device, RT_DEVICE_OFLAG_RDWR); error_check(result == RT_EOK, "Timer device open error"); } if (self->timeout_cb != RT_NULL) { // set callback timer if (timer_self[self->timer_device->device_id] && timer_self[self->timer_device->device_id] != self) { error_check(result == RT_EOK, "Timer device callback function already exists"); } else { timer_self[self->timer_device->device_id] = self; } result = rt_device_set_rx_indicate(self->timer_device, timer_event_handler); error_check(result == RT_EOK, "Timer set timout callback error"); } // set timer mode mode = self->is_repeat ? HWTIMER_MODE_PERIOD : HWTIMER_MODE_ONESHOT; result = rt_device_control(self->timer_device, HWTIMER_CTRL_MODE_SET, &mode); error_check(result == RT_EOK, "Timer set mode error"); if (self->timeout) { rt_hwtimerval_t timeout_s; rt_size_t len; timeout_s.sec = self->timeout / 1000; // second timeout_s.usec = self->timeout % 1000; // microsecond len = rt_device_write(self->timer_device, 0, &timeout_s, sizeof(timeout_s)); error_check(len == sizeof(timeout_s), "Timer set timout error"); } self->is_init = RT_TRUE; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); STATIC mp_obj_t machine_timer_callback(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { machine_timer_obj_t *self = (machine_timer_obj_t *)args[0]; rt_bool_t result = RT_EOK; static const mp_arg_t allowed_args[] = { { MP_QSTR_callback, MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; mp_arg_val_t dargs[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, dargs); self->timeout_cb = dargs[0].u_obj; if(n_args == 1) { self->timeout_cb = RT_NULL; self->timer_device->rx_indicate = RT_NULL;//Log-off callback function } else if(n_args == 2) { if(self->timeout_cb != mp_const_none) { timer_self[self->timer_device->device_id] = self; result = rt_device_set_rx_indicate(self->timer_device, timer_event_handler); //set callback timer error_check(result == RT_EOK, "Timer set timout callback error"); } else { self->timeout_cb = RT_NULL; self->timer_device->rx_indicate = RT_NULL;//Log-off callback function } } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_callback_obj, 0,machine_timer_callback); STATIC const mp_rom_map_elem_t machine_timer_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_timer_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_timer_init_obj) }, { MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&machine_timer_callback_obj) }, { MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(RT_FALSE) }, { MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(RT_TRUE) }, }; STATIC MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table); const mp_obj_type_t machine_timer_type = { { &mp_type_type }, .name = MP_QSTR_Timer, .print = machine_timer_print, .make_new = machine_timer_make_new, .locals_dict = (mp_obj_t) &machine_timer_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_TIMER ================================================ FILE: port/modules/machine/machine_timer.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef MICROPY_INCLUDED_MACHINE_TIMER_H #define MICROPY_INCLUDED_MACHINE_TIMER_H #include "py/obj.h" #include extern const mp_obj_type_t machine_timer_type; #endif // MICROPY_INCLUDED_MACHINE_TIMER_H ================================================ FILE: port/modules/machine/machine_uart.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 SummerGift * * 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/mphal.h" #include "py/mperrno.h" #include "py/stream.h" #include #include "machine_uart.h" #include "rtdevice.h" #ifdef MICROPYTHON_USING_MACHINE_UART #ifndef RT_USING_SERIAL #error "Please define the RT_USING_SERIAL on 'rtconfig.h'" #endif typedef struct _machine_uart_obj_t { mp_obj_base_t base; struct rt_serial_device *uart_device; }machine_uart_obj_t; STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = (machine_uart_obj_t*) self_in; mp_printf(print, "uart( device port : %s,baud_rate = %d, data_bits = %d, parity = %d, stop_bits = %d )", self->uart_device->parent.parent.name, self->uart_device->config.baud_rate, self->uart_device->config.data_bits, self->uart_device->config.parity, self->uart_device->config.stop_bits); } STATIC mp_obj_t machine_uart_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, MP_OBJ_FUN_ARGS_MAX, true); char uart_dev_name[RT_NAME_MAX]; snprintf(uart_dev_name, sizeof(uart_dev_name), "uart%d", mp_obj_get_int(args[0])); struct rt_serial_device *rt_serial_device = (struct rt_serial_device *) rt_device_find(uart_dev_name); if (rt_serial_device == RT_NULL || rt_serial_device->parent.type != RT_Device_Class_Char) { mp_printf(&mp_plat_print, "ERROR: UART device %s not found!\n", uart_dev_name); nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%s) doesn't exist", uart_dev_name)); } rt_err_t result; result = rt_device_open((rt_device_t)rt_serial_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX ); if (result != RT_EOK) { mp_printf(&mp_plat_print, "ERROR: UART device %s can't open!\n", uart_dev_name); nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%s) can't open", uart_dev_name)); } // create new uart object machine_uart_obj_t *self = m_new_obj(machine_uart_obj_t); self->base.type = &machine_uart_type; self->uart_device = rt_serial_device; return (mp_obj_t) self; } /// \method init(baudrate, bits=8, parity=None, stop=1, *, timeout=1000, timeout_char=0, flow=0, read_buf_len=64) /// /// Initialise the UART bus with the given parameters: /// /// - `baudrate` is the clock rate. /// - `bits` is the number of bits per byte, 7, 8 or 9. /// - `parity` is the parity, `None`, 0 (even) or 1 (odd). /// - `stop` is the number of stop bits, 1 or 2. /// - `timeout` is the timeout in milliseconds to wait for the first character. /// - `timeout_char` is the timeout in milliseconds to wait between characters. /// - `flow` is RTS | CTS where RTS == 256, CTS == 512 /// - `read_buf_len` is the character length of the read buffer (0 to disable). /// STATIC mp_obj_t machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 9600} }, { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} }, { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, // rt-thread does not support { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_read_buf_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, }; // parse args struct { mp_arg_val_t baudrate, bits, parity, stop, flow, timeout, timeout_char, read_buf_len; } args; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args); // set the UART configuration values struct rt_serial_device *uart_p = self->uart_device; struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; // baudrate config.baud_rate = args.baudrate.u_int; // parity mp_int_t bits = args.bits.u_int; if (args.parity.u_obj == mp_const_none) { config.parity = PARITY_NONE; } else { mp_int_t parity = mp_obj_get_int(args.parity.u_obj); config.parity = (parity & 1) ? PARITY_ODD : PARITY_EVEN; //bits += 1; // STs convention has bits including parity, not all mcu } // number of bits if (bits == 8) { config.data_bits = DATA_BITS_8; } else if (bits == 9) { config.data_bits = DATA_BITS_9; } else if (bits == 7) { config.data_bits = DATA_BITS_7; } else { mp_raise_ValueError("unsupported combination of bits and parity"); } // stop bits switch (args.stop.u_int) { case 1: config.stop_bits = STOP_BITS_1; break; default: config.stop_bits = STOP_BITS_2; break; } //buffer size #if defined(RT_USING_SERIAL_V1) config.bufsz = args.read_buf_len.u_int; #elif defined(RT_USING_SERIAL_V2) config.rx_bufsz = args.read_buf_len.u_int; config.tx_bufsz = args.read_buf_len.u_int; #endif rt_device_control((struct rt_device *) uart_p, RT_DEVICE_CTRL_CONFIG, &config); return mp_const_none; } STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { return machine_uart_init_helper(args[0], n_args - 1, args + 1, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init); STATIC mp_obj_t machine_uart_deinit(mp_obj_t self_in) { return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_deinit_obj, machine_uart_deinit); #define RETRY_TIMES 500 STATIC mp_obj_t machine_uart_writechar(mp_obj_t self_in, mp_obj_t char_in) { machine_uart_obj_t *self = self_in; uint16_t data = mp_obj_get_int(char_in); rt_size_t len = 0; rt_uint32_t timeout = 0; do { len = rt_device_write((struct rt_device *)(self->uart_device), 0, &data, 1); timeout++; } while (len != 1 && timeout < RETRY_TIMES); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_uart_writechar_obj, machine_uart_writechar); #define UART_RX_EVENT (1 << 0) static struct rt_event event; STATIC mp_obj_t machine_uart_readchar(mp_obj_t self_in) { machine_uart_obj_t *self = self_in; rt_uint32_t e; rt_uint8_t ch; while (rt_device_read((struct rt_device *)(self->uart_device), 0, &ch, 1) != 1) { rt_event_recv(&event, UART_RX_EVENT, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &e); } return MP_OBJ_NEW_SMALL_INT(ch); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_readchar_obj, machine_uart_readchar); STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { // instance methods { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_uart_init_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_uart_deinit_obj) }, // { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, /// \method read([nbytes]) { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, /// \method readline() { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)}, /// \method readinto(buf[, nbytes]) { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, /// \method write(buf) { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_writechar), MP_ROM_PTR(&machine_uart_writechar_obj) }, { MP_ROM_QSTR(MP_QSTR_readchar), MP_ROM_PTR(&machine_uart_readchar_obj) }, // { MP_ROM_QSTR(MP_QSTR_sendbreak), MP_ROM_PTR(&pyb_uart_sendbreak_obj) }, // class constants // { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, // { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, }; STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = self_in; byte *buf = buf_in; //TODO dfs sync read //MP_RTT_NOT_IMPL_PRINT; return rt_device_read((struct rt_device *)(self->uart_device), -1, buf, size); } STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = self_in; const byte *buf = buf_in; //TODO dfs sync write //MP_RTT_NOT_IMPL_PRINT; return rt_device_write((struct rt_device *)(self->uart_device), -1, buf, size); } STATIC mp_uint_t machine_uart_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) { return NULL; } STATIC const mp_stream_p_t uart_stream_p = { .read = machine_uart_read, .write = machine_uart_write, .ioctl = machine_uart_ioctl, .is_text = false, }; const mp_obj_type_t machine_uart_type = { { &mp_type_type }, .name = MP_QSTR_UART, .print = machine_uart_print, .make_new = machine_uart_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &uart_stream_p, .locals_dict = (mp_obj_dict_t*)&machine_uart_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_UART ================================================ FILE: port/modules/machine/machine_uart.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 SummerGift * * 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_MACHINE_UART_H #define MICROPY_INCLUDED_MACHINE_UART_H #include "py/obj.h" #include extern const mp_obj_type_t machine_uart_type; #endif // _MACHINE_UART_H ================================================ FILE: port/modules/machine/machine_wdt.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "py/nlr.h" #include "py/obj.h" #include "py/runtime.h" #include "modmachine.h" #include "mphalport.h" #ifdef MICROPYTHON_USING_MACHINE_WDT #include #include #include "machine_wdt.h" typedef struct _machine_wdt_obj_t { mp_obj_base_t base; rt_device_t wdt_device; }machine_wdt_obj_t; const mp_obj_type_t machine_wdt_type; STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, msg)); } } STATIC mp_obj_t machine_wdt_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { #define MP_WDT_DEV_NAME "wdt" machine_wdt_obj_t *self = m_new_obj(machine_wdt_obj_t); char wdt_dev_name[RT_NAME_MAX] = {0}; rt_err_t result = RT_EOK; mp_int_t timeout = 5; // check arguments mp_arg_check_num(n_args, n_kw, 0, 2, false); if (n_args == 2) { // check input WDT device name or ID if (mp_obj_is_small_int(args[0])) { rt_snprintf(wdt_dev_name, sizeof(wdt_dev_name), "wdt%d", mp_obj_get_int(args[0])); } else if (mp_obj_is_qstr(args[0])) { rt_strncpy(wdt_dev_name, mp_obj_str_get_str(args[0]), RT_NAME_MAX); } else { error_check(0, "Input WDT device name or ID error."); } timeout = mp_obj_get_int(args[1]); error_check(timeout >= 1, "input timeout value error"); } else if (n_args == 1) { if (mp_obj_is_small_int(args[0])) { timeout = mp_obj_get_int(args[0]); error_check(timeout >= 1, "input timeout value error"); } else if (mp_obj_is_qstr(args[0])) { rt_strncpy(wdt_dev_name, mp_obj_str_get_str(args[0]), RT_NAME_MAX); } else { error_check(0, "Input WDT device name or ID error."); } } else { rt_strncpy(wdt_dev_name, MP_WDT_DEV_NAME, RT_NAME_MAX); } self->base.type = &machine_wdt_type; // find WDT device self->wdt_device = rt_device_find(wdt_dev_name); if (self->wdt_device == RT_NULL || self->wdt_device->type != RT_Device_Class_Security) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "WDT(%s) don't exist", wdt_dev_name)); } result = rt_device_init(self->wdt_device); error_check(result == RT_EOK, "WDT init error"); // set WDT device timout result = rt_device_control(self->wdt_device, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, (void *)&timeout); error_check(result == RT_EOK, "WDT set timout error"); result = rt_device_control(self->wdt_device, RT_DEVICE_CTRL_WDT_START, RT_NULL); error_check(result == RT_EOK, "WDT start error"); // return constant object return MP_OBJ_FROM_PTR(self); } STATIC mp_obj_t machine_wdt_feed(mp_obj_t self_in) { /* idle task feed */ machine_wdt_obj_t *self = MP_OBJ_TO_PTR(self_in); rt_err_t result = RT_EOK; result = rt_device_control(self->wdt_device, RT_DEVICE_CTRL_WDT_KEEPALIVE, RT_NULL); error_check(result == RT_EOK, "WDT feed failed"); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_wdt_feed_obj, machine_wdt_feed); STATIC const mp_rom_map_elem_t machine_wdt_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_feed), MP_ROM_PTR(&machine_wdt_feed_obj) }, }; STATIC MP_DEFINE_CONST_DICT(machine_wdt_locals_dict, machine_wdt_locals_dict_table); const mp_obj_type_t machine_wdt_type = { { &mp_type_type }, .name = MP_QSTR_WDT, .make_new = machine_wdt_make_new, .locals_dict = (mp_obj_t) &machine_wdt_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_WDT ================================================ FILE: port/modules/machine/machine_wdt.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong (chenyong@rt-thread.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef MICROPY_INCLUDED_MACHINE_WDT_H #define MICROPY_INCLUDED_MACHINE_WDT_H #include "py/obj.h" #include extern const mp_obj_type_t machine_wdt_type; #endif // MICROPY_INCLUDED_MACHINE_WDT_H ================================================ FILE: port/modules/machine/modmachine.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include "py/obj.h" #include "py/runtime.h" #include "py/gc.h" #include "lib/utils/pyexec.h" #include "extmod/machine_mem.h" #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" #include "extmod/machine_i2c.h" #include "extmod/machine_spi.h" #include "modmachine.h" #include "machine_uart.h" #include "machine_adc.h" #include "machine_pwm.h" #include "machine_lcd.h" #include "machine_rtc.h" #include "machine_wdt.h" #include "machine_timer.h" #include #if MICROPY_PY_MACHINE STATIC mp_obj_t machine_info(size_t n_args, const mp_obj_t *args) { #ifdef RT_USING_FINSH extern long list_thread(void); #endif // RT-Thread info { mp_printf(&mp_plat_print, "---------------------------------------------\n"); mp_printf(&mp_plat_print, "RT-Thread\n"); mp_printf(&mp_plat_print, "---------------------------------------------\n"); #ifdef RT_USING_FINSH extern void list_memheap(void); extern void list_mempool(void); #if defined(RT_USING_MEMHEAP_AS_HEAP) list_memheap(); #elif defined(RT_USING_MEMPOOL) list_mempool(); #endif list_thread(); #endif mp_printf(&mp_plat_print, "---------------------------------------------\n"); } // qstr info { mp_uint_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:\n n_pool=" UINT_FMT "\n n_qstr=" UINT_FMT "\n n_str_data_bytes=" UINT_FMT "\n n_total_bytes=" UINT_FMT "\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes); } mp_printf(&mp_plat_print, "---------------------------------------------\n"); // GC info { gc_info_t info; gc_info(&info); mp_printf(&mp_plat_print, "GC:\n"); mp_printf(&mp_plat_print, " " UINT_FMT " total\n", info.total); mp_printf(&mp_plat_print, " " UINT_FMT " : " UINT_FMT "\n", info.used, info.free); mp_printf(&mp_plat_print, " 1=" UINT_FMT " 2=" UINT_FMT " m=" UINT_FMT "\n", info.num_1block, info.num_2block, info.max_block); } // free space on flash { //TODO } if (n_args == 1) { // arg given means dump gc allocation table gc_dump_alloc_table(); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_info_obj, 0, 1, machine_info); STATIC mp_obj_t machine_unique_id(void) { //TODO MP_RTT_NOT_IMPL_PRINT; return 0; } MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); STATIC mp_obj_t machine_reset(void) { //TODO MP_RTT_NOT_IMPL_PRINT; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); STATIC mp_obj_t machine_soft_reset(void) { pyexec_system_exit = PYEXEC_FORCED_EXIT; nlr_raise(mp_obj_new_exception(&mp_type_SystemExit)); } MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); /* * @param clkid - range 0~127 (e.g 0:SYSCLK 1:HCLK 2:PCLK1 etc) * * @return 0 - ok, -1 - no such clock */ MP_WEAK int mp_port_get_freq(int clkid, int *freq) { return -1; } STATIC mp_obj_t machine_freq(void) { int i; mp_obj_list_t *ret_list = m_new(mp_obj_list_t, 1); mp_obj_list_init(ret_list, 0); int freq; for (i = 0; i < 128; i ++) { if (mp_port_get_freq(i, &freq) != 0) break; mp_obj_list_append(ret_list, mp_obj_new_int(freq)); } return MP_OBJ_FROM_PTR(ret_list); } MP_DEFINE_CONST_FUN_OBJ_0(machine_freq_obj, machine_freq); STATIC mp_obj_t pyb_wfi(void) { //TODO __WFI(); MP_RTT_NOT_IMPL_PRINT; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(pyb_wfi_obj, pyb_wfi); static rt_base_t int_lvl; STATIC mp_obj_t pyb_disable_irq(void) { int_lvl = rt_hw_interrupt_disable(); return mp_obj_new_bool(1); } MP_DEFINE_CONST_FUN_OBJ_0(pyb_disable_irq_obj, pyb_disable_irq); STATIC mp_obj_t pyb_enable_irq(size_t n_args, const mp_obj_t *arg) { if (n_args == 0) { rt_hw_interrupt_enable(int_lvl); } else { if (mp_obj_is_true(arg[0])) { rt_hw_interrupt_enable(int_lvl); } else { int_lvl = rt_hw_interrupt_disable(); } } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_enable_irq_obj, 0, 1, pyb_enable_irq); STATIC mp_obj_t machine_sleep (void) { //TODO MP_RTT_NOT_IMPL_PRINT; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(machine_sleep_obj, machine_sleep); STATIC mp_obj_t machine_deepsleep (void) { //TODO MP_RTT_NOT_IMPL_PRINT; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(machine_deepsleep_obj, machine_deepsleep); STATIC mp_obj_t machine_reset_cause(void) { //TODO MP_RTT_NOT_IMPL_PRINT; return MP_OBJ_NEW_SMALL_INT(42); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause); STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_info_obj) }, { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&machine_soft_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&pyb_wfi_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&machine_sleep_obj) }, { MP_ROM_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&machine_deepsleep_obj) }, { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&pyb_disable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&pyb_enable_irq_obj) }, // { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, #if MICROPY_PY_PIN { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, #endif { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, #if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, #endif #if MICROPY_PY_MACHINE_SPI { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, #endif #if MICROPY_PY_MACHINE_UART { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, #endif #if MICROPY_PY_MACHINE_RTC { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, #endif #if MICROPY_PY_MACHINE_LCD { MP_ROM_QSTR(MP_QSTR_LCD), MP_ROM_PTR(&machine_lcd_type ) }, #endif #if MICROPY_PY_MACHINE_PWM { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, #endif #if MICROPY_PY_MACHINE_ADC { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, #endif #if MICROPY_PY_MACHINE_WDT { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) }, #endif #if MICROPY_PY_MACHINE_TIMER { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, #endif }; STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); const mp_obj_module_t mp_module_machine = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&machine_module_globals, }; #endif // MICROPY_PY_MACHINE ================================================ FILE: port/modules/machine/modmachine.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef _MODMACHINE_H #define _MODMACHINE_H #include "py/obj.h" #include extern const mp_obj_type_t machine_pin_type; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_info_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_unique_id_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_reset_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_bootloader_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_freq_obj); MP_DECLARE_CONST_FUN_OBJ_0(pyb_wfi_obj); MP_DECLARE_CONST_FUN_OBJ_0(pyb_disable_irq_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_enable_irq_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_sleep_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_deepsleep_obj); typedef struct _machine_pin_obj_t { mp_obj_base_t base; char name[RT_NAME_MAX]; uint32_t pin; mp_obj_t pin_isr_cb; } machine_pin_obj_t; #endif // _MODMACHINE_H ================================================ FILE: port/modules/modffi.c ================================================ /* * 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 * Copyright (c) 2019 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include "py/runtime.h" #include "py/binary.h" #include "py/mperrno.h" #ifdef MICROPYTHON_USING_FFI #if !defined(__GNUC__) #error "The ffi module only supports GCC toolchain at present" #endif #include #include typedef enum { FFI_TYPE_UNKNOWN, FFI_TYPE_SCHAR, FFI_TYPE_UCHAR, FFI_TYPE_SSHORT, FFI_TYPE_USHORT, FFI_TYPE_SINT, FFI_TYPE_UINT, FFI_TYPE_SLONG, FFI_TYPE_ULONG, FFI_TYPE_SINT64, FFI_TYPE_UINT64, FFI_TYPE_FLOAT, FFI_TYPE_DOUBLE, FFI_TYPE_POINTER, FFI_TYPE_VOID, } ffi_type_t; typedef struct _mp_obj_opaque_t { mp_obj_base_t base; void *val; } mp_obj_opaque_t; typedef struct _mp_obj_ffimod_t { mp_obj_base_t base; void *handle; } mp_obj_ffimod_t; typedef struct _mp_obj_ffivar_t { mp_obj_base_t base; void *var; char type; } mp_obj_ffivar_t; typedef struct _mp_obj_ffifunc_t { mp_obj_base_t base; void *func; char rettype; uint32_t argc; const char *argtypes; ffi_type_t *params; } mp_obj_ffifunc_t; typedef struct _mp_obj_fficallback_t { mp_obj_base_t base; void *func; char rettype; ffi_type_t *params; } mp_obj_fficallback_t; typedef unsigned long ffi_arg; STATIC const mp_obj_type_t ffimod_type; STATIC const mp_obj_type_t ffifunc_type; STATIC const mp_obj_type_t fficallback_type; STATIC const mp_obj_type_t ffivar_type; STATIC ffi_type_t char2ffi_type(char c) { switch (c) { case 'b': return FFI_TYPE_SCHAR; case 'B': return FFI_TYPE_UCHAR; case 'h': return FFI_TYPE_SSHORT; case 'H': return FFI_TYPE_USHORT; case 'i': return FFI_TYPE_SINT; case 'I': return FFI_TYPE_UINT; case 'l': return FFI_TYPE_SLONG; case 'L': return FFI_TYPE_ULONG; case 'q': return FFI_TYPE_SINT64; case 'Q': return FFI_TYPE_UINT64; #if MICROPY_PY_BUILTINS_FLOAT case 'f': return FFI_TYPE_FLOAT; case 'd': return FFI_TYPE_DOUBLE; #endif case 'O': // mp_obj_t case 'C': // (*)() case 'P': // const void* case 'p': // void* case 's': return FFI_TYPE_POINTER; case 'v': return FFI_TYPE_VOID; default: return FFI_TYPE_UNKNOWN; } } STATIC ffi_type_t get_ffi_type(mp_obj_t o_in) { if (MP_OBJ_IS_STR(o_in)) { const char *s = mp_obj_str_get_str(o_in); ffi_type_t t = char2ffi_type(*s); if (t != FFI_TYPE_UNKNOWN) { return t; } } // TODO: Support actual libffi type objects mp_raise_TypeError("Unknown type"); } STATIC mp_obj_t return_ffi_value(void *val, char type) { switch (type) { case 's': { const char *s = (const char *)(intptr_t)val; if (!s) { return mp_const_none; } return mp_obj_new_str(s, strlen(s)); } case 'v': return mp_const_none; #if MICROPY_PY_BUILTINS_FLOAT case 'f': { union { void *ffi; float flt; } val_union = { .ffi = val }; return mp_obj_new_float(val_union.flt); } case 'd': { double *p = (double*)&val; mp_raise_NotImplementedError("The double return type NOT supported"); return mp_obj_new_float(*p); } #endif case 'O': return (mp_obj_t)(intptr_t)val; default: return mp_obj_new_int((mp_int_t)val); } } // FFI module STATIC void ffimod_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", self->handle); } STATIC mp_obj_t ffimod_close(mp_obj_t self_in) { mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); dlclose(self->handle); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(ffimod_close_obj, ffimod_close); STATIC mp_obj_t make_func(mp_obj_t rettype_in, void *func, mp_obj_t argtypes_in) { const char *rettype = mp_obj_str_get_str(rettype_in); const char *argtypes = mp_obj_str_get_str(argtypes_in); mp_int_t nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(argtypes_in)); mp_obj_ffifunc_t *o = m_new_obj_var(mp_obj_ffifunc_t, ffi_type_t, nparams); o->base.type = &ffifunc_type; o->func = func; o->rettype = *rettype; o->argtypes = argtypes; o->argc = nparams; o->params = (uint8_t *)o + sizeof(mp_obj_ffifunc_t); mp_obj_iter_buf_t iter_buf; mp_obj_t iterable = mp_getiter(argtypes_in, &iter_buf); mp_obj_t item; int i = 0; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { o->params[i++] = get_ffi_type(item); } /* when param is void change the argc to 0 */ if (o->argc == 1 && o->params[0] == FFI_TYPE_VOID) { o->argc = 0; } return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t ffimod_func(size_t n_args, const mp_obj_t *args) { (void)n_args; // always 4 mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(args[0]); const char *symname = mp_obj_str_get_str(args[2]); void *sym = dlsym(self->handle, symname); if (sym == NULL) { mp_raise_ValueError("input symbol NOT found"); } return make_func(args[1], sym, args[3]); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ffimod_func_obj, 4, 4, ffimod_func); STATIC mp_obj_t mod_ffi_func(mp_obj_t rettype, mp_obj_t addr_in, mp_obj_t argtypes) { void *addr; /* find the built-in function address when address is string type */ if (mp_obj_is_str(addr_in)) { addr = (void *) dlmodule_symbol_find(mp_obj_str_get_str(addr_in)); if (addr == NULL) { mp_raise_ValueError("input symbol NOT found"); } } else { addr = (void*) MP_OBJ_TO_PTR(mp_obj_int_get_truncated(addr_in)); } return make_func(rettype, addr, argtypes); } MP_DEFINE_CONST_FUN_OBJ_3(mod_ffi_func_obj, mod_ffi_func); STATIC void call_py_func(void *ret, int argc, void** args, void *func) { mp_obj_t *pyargs = m_new(mp_obj_t, argc); for (int i = 0; i < argc; i++) { pyargs[i] = mp_obj_new_int(*(mp_int_t*)args[i]); } mp_obj_t res = mp_call_function_n_kw(MP_OBJ_FROM_PTR(func), argc, 0, pyargs); m_free(pyargs); if (res != mp_const_none) { *(ffi_arg*)ret = mp_obj_int_get_truncated(res); } } STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t paramtypes_in) { const char *rettype = mp_obj_str_get_str(rettype_in); mp_int_t nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(paramtypes_in)); mp_obj_fficallback_t *o = m_new_obj_var(mp_obj_fficallback_t, ffi_type_t, nparams); //TODO add callback impl mp_raise_NotImplementedError("The callback NOT supported"); return MP_OBJ_FROM_PTR(o); } MP_DEFINE_CONST_FUN_OBJ_3(mod_ffi_callback_obj, mod_ffi_callback); STATIC mp_obj_t ffimod_var(mp_obj_t self_in, mp_obj_t vartype_in, mp_obj_t symname_in) { mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); const char *rettype = mp_obj_str_get_str(vartype_in); const char *symname = mp_obj_str_get_str(symname_in); void *sym = dlsym(self->handle, symname); if (sym == NULL) { mp_raise_OSError(MP_ENOENT); } mp_obj_ffivar_t *o = m_new_obj(mp_obj_ffivar_t); o->base.type = &ffivar_type; o->var = sym; o->type = *rettype; return MP_OBJ_FROM_PTR(o); } MP_DEFINE_CONST_FUN_OBJ_3(ffimod_var_obj, ffimod_var); STATIC mp_obj_t ffimod_addr(mp_obj_t self_in, mp_obj_t symname_in) { mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); const char *symname = mp_obj_str_get_str(symname_in); void *sym = dlsym(self->handle, symname); if (sym == NULL) { mp_raise_OSError(MP_ENOENT); } return mp_obj_new_int((uintptr_t)sym); } MP_DEFINE_CONST_FUN_OBJ_2(ffimod_addr_obj, ffimod_addr); STATIC mp_obj_t ffimod_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { (void)n_args; (void)n_kw; const char *fname = NULL; if (args[0] != mp_const_none) { fname = mp_obj_str_get_str(args[0]); } void *mod = dlopen(fname, RTLD_NOW | RTLD_LOCAL); if (mod == NULL) { mp_raise_OSError(errno); } mp_obj_ffimod_t *o = m_new_obj(mp_obj_ffimod_t); o->base.type = type; o->handle = mod; return MP_OBJ_FROM_PTR(o); } STATIC const mp_rom_map_elem_t ffimod_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_func), MP_ROM_PTR(&ffimod_func_obj) }, { MP_ROM_QSTR(MP_QSTR_var), MP_ROM_PTR(&ffimod_var_obj) }, { MP_ROM_QSTR(MP_QSTR_addr), MP_ROM_PTR(&ffimod_addr_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&ffimod_close_obj) }, }; STATIC MP_DEFINE_CONST_DICT(ffimod_locals_dict, ffimod_locals_dict_table); STATIC const mp_obj_type_t ffimod_type = { { &mp_type_type }, .name = MP_QSTR_ffimod, .print = ffimod_print, .make_new = ffimod_make_new, .locals_dict = (mp_obj_dict_t*)&ffimod_locals_dict, }; // FFI function STATIC void ffifunc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_ffifunc_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", self->func); } STATIC void ffi_call(void *func, ffi_arg *retval, uint32_t argc, ffi_arg *argv) { typedef ffi_arg(*f6_t)(ffi_arg, ffi_arg, ffi_arg, ffi_arg, ffi_arg, ffi_arg); ffi_arg dummy = 0; ffi_arg args[6]; uint32_t i; for (i = 0; i < sizeof(args) / sizeof(args[0]); i ++) { if (i < argc) { args[i] = argv[i]; } else { args[i] = (ffi_arg)&dummy; } } *retval = ((f6_t)(func))(args[0], args[1], args[2], args[3], args[4], args[5]); } STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { (void)n_kw; mp_obj_ffifunc_t *self = MP_OBJ_TO_PTR(self_in); assert(n_kw == 0); if (self->argc > n_args) { mp_raise_ValueError("input function parameter number mismatch"); } ffi_arg *values = m_new(ffi_arg, n_args); const char *argtype = self->argtypes; for (uint i = 0; i < n_args; i++, argtype++) { mp_obj_t a = args[i]; if (*argtype == 'O') { values[i] = (ffi_arg)(intptr_t)a; #if MICROPY_PY_BUILTINS_FLOAT } else if (*argtype == 'f') { float *p = (float*)&values[i]; *p = mp_obj_get_float(a); } else if (*argtype == 'd') { double *p = (double*)&values[i]; *p = mp_obj_get_float(a); //TODO add double and long long supported mp_raise_NotImplementedError("The double parameter NOT supported"); #endif } else if (a == mp_const_none) { values[i] = 0; } else if (mp_obj_is_int(a)) { values[i] = mp_obj_int_get_truncated(a); } else if (mp_obj_is_str(a)) { const char *s = mp_obj_str_get_str(a); values[i] = (ffi_arg)(intptr_t)s; } else if (((mp_obj_base_t*)MP_OBJ_TO_PTR(a))->type->buffer_p.get_buffer != NULL) { mp_obj_base_t *o = (mp_obj_base_t*)MP_OBJ_TO_PTR(a); mp_buffer_info_t bufinfo; int ret = o->type->buffer_p.get_buffer(MP_OBJ_FROM_PTR(o), &bufinfo, MP_BUFFER_READ); // TODO: MP_BUFFER_READ? if (ret != 0) { goto __error; } values[i] = (ffi_arg)(intptr_t)bufinfo.buf; } else if (mp_obj_is_type(a, &fficallback_type)) { mp_obj_fficallback_t *p = MP_OBJ_TO_PTR(a); values[i] = (ffi_arg)(intptr_t)p->func; } else { goto __error; } } // If ffi_arg is not big enough to hold a double, then we must pass along a // pointer to a memory location of the correct size. // TODO check if this needs to be done for other types which don't fit into // ffi_arg. #if MICROPY_PY_BUILTINS_FLOAT if (sizeof(ffi_arg) == 4 && self->rettype == 'd') { double retval; //TODO add double supported mp_raise_NotImplementedError("The double return type NOT supported"); // ffi_call(self->func, &retval, n_args, values); return mp_obj_new_float(retval); } else #endif { ffi_arg retval; ffi_call(self->func, &retval, n_args, values); m_free(values); return return_ffi_value((void *)retval, self->rettype); } __error: mp_raise_TypeError("Don't know how to pass object to native function"); m_free(values); } STATIC const mp_obj_type_t ffifunc_type = { { &mp_type_type }, .name = MP_QSTR_ffifunc, .print = ffifunc_print, .call = ffifunc_call, }; // FFI callback for Python function STATIC void fficallback_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_fficallback_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", self->func); } STATIC const mp_obj_type_t fficallback_type = { { &mp_type_type }, .name = MP_QSTR_fficallback, .print = fficallback_print, }; // FFI variable STATIC void ffivar_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_ffivar_t *self = MP_OBJ_TO_PTR(self_in); // Variable value printed as cast to int mp_printf(print, "", self->var, *(int*)self->var); } STATIC mp_obj_t ffivar_get(mp_obj_t self_in) { mp_obj_ffivar_t *self = MP_OBJ_TO_PTR(self_in); return mp_binary_get_val_array(self->type, self->var, 0); } MP_DEFINE_CONST_FUN_OBJ_1(ffivar_get_obj, ffivar_get); STATIC mp_obj_t ffivar_set(mp_obj_t self_in, mp_obj_t val_in) { mp_obj_ffivar_t *self = MP_OBJ_TO_PTR(self_in); mp_binary_set_val_array(self->type, self->var, 0, val_in); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(ffivar_set_obj, ffivar_set); STATIC const mp_rom_map_elem_t ffivar_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&ffivar_get_obj) }, { MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&ffivar_set_obj) }, }; STATIC MP_DEFINE_CONST_DICT(ffivar_locals_dict, ffivar_locals_dict_table); STATIC const mp_obj_type_t ffivar_type = { { &mp_type_type }, .name = MP_QSTR_ffivar, .print = ffivar_print, .locals_dict = (mp_obj_dict_t*)&ffivar_locals_dict, }; STATIC mp_obj_t mod_ffi_open(size_t n_args, const mp_obj_t *args) { return ffimod_make_new(&ffimod_type, n_args, 0, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_ffi_open_obj, 1, 2, mod_ffi_open); STATIC mp_obj_t mod_ffi_as_bytearray(mp_obj_t ptr, mp_obj_t size) { return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void*)(uintptr_t)mp_obj_int_get_truncated(ptr)); } MP_DEFINE_CONST_FUN_OBJ_2(mod_ffi_as_bytearray_obj, mod_ffi_as_bytearray); STATIC const mp_rom_map_elem_t mp_module_ffi_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ffi) }, { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_ffi_open_obj) }, { MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&mod_ffi_callback_obj) }, { MP_ROM_QSTR(MP_QSTR_func), MP_ROM_PTR(&mod_ffi_func_obj) }, { MP_ROM_QSTR(MP_QSTR_as_bytearray), MP_ROM_PTR(&mod_ffi_as_bytearray_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_ffi_globals, mp_module_ffi_globals_table); const mp_obj_module_t mp_module_ffi = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_ffi_globals, }; #endif /* MICROPY_PY_FFI */ ================================================ FILE: port/modules/modfile.c ================================================ /* * 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" #if MICROPY_PY_IO #include "py/runtime.h" #include "py/stream.h" #include "py/builtin.h" #include "py/mphal.h" #include #include #include #include #include #include #ifdef _WIN32 #define fsync _commit #endif typedef struct _mp_obj_fdfile_t { mp_obj_base_t base; int fd; } mp_obj_fdfile_t; #ifdef MICROPY_CPYTHON_COMPAT STATIC void check_fd_is_open(const mp_obj_fdfile_t *o) { if (o->fd < 0) { mp_raise_ValueError("I/O operation on closed file"); } } #else #define check_fd_is_open(o) #endif extern const mp_obj_type_t mp_type_fileio; extern const mp_obj_type_t mp_type_textio; STATIC void fdfile_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_fdfile_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "", mp_obj_get_type_str(self_in), self->fd); } STATIC mp_uint_t fdfile_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_fdfile_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); mp_int_t r = read(o->fd, buf, size); if (r == -1) { *errcode = errno; return MP_STREAM_ERROR; } return r; } STATIC mp_uint_t fdfile_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_fdfile_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); #if MICROPY_PY_OS_DUPTERM if (o->fd <= STDERR_FILENO) { mp_hal_stdout_tx_strn(buf, size); return size; } #endif mp_int_t r = write(o->fd, buf, size); while (r == -1 && errno == MP_EINTR) { if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { mp_obj_t obj = MP_STATE_VM(mp_pending_exception); MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; nlr_raise(obj); } r = write(o->fd, buf, size); } if (r == -1) { *errcode = errno; return MP_STREAM_ERROR; } return r; } STATIC mp_uint_t fdfile_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_fdfile_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); switch (request) { case MP_STREAM_SEEK: { struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)arg; off_t off = lseek(o->fd, s->offset, s->whence); if (off == (off_t)-1) { *errcode = errno; return MP_STREAM_ERROR; } s->offset = off; return 0; } case MP_STREAM_FLUSH: if (fsync(o->fd) < 0) { *errcode = errno; return MP_STREAM_ERROR; } return 0; default: *errcode = EINVAL; return MP_STREAM_ERROR; } } STATIC mp_obj_t fdfile_close(mp_obj_t self_in) { mp_obj_fdfile_t *self = MP_OBJ_TO_PTR(self_in); close(self->fd); #ifdef MICROPY_CPYTHON_COMPAT self->fd = -1; #endif return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fdfile_close_obj, fdfile_close); STATIC mp_obj_t fdfile___exit__(size_t n_args, const mp_obj_t *args) { (void)n_args; return fdfile_close(args[0]); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fdfile___exit___obj, 4, 4, fdfile___exit__); STATIC mp_obj_t fdfile_fileno(mp_obj_t self_in) { mp_obj_fdfile_t *self = MP_OBJ_TO_PTR(self_in); check_fd_is_open(self); return MP_OBJ_NEW_SMALL_INT(self->fd); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fdfile_fileno_obj, fdfile_fileno); // Note: encoding is ignored for now; it's also not a valid kwarg for CPython's FileIO, // but by adding it here we can use one single mp_arg_t array for open() and FileIO's constructor STATIC const mp_arg_t file_open_args[] = { { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} }, { MP_QSTR_buffering, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, }; #define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args) STATIC mp_obj_t fdfile_open(const mp_obj_type_t *type, mp_arg_val_t *args) { mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t); const char *mode_s = mp_obj_str_get_str(args[1].u_obj); int mode_rw = 0, mode_x = 0; while (*mode_s) { switch (*mode_s++) { case 'r': mode_rw = O_RDONLY; break; case 'w': mode_rw = O_WRONLY; mode_x = O_CREAT | O_TRUNC; break; case 'a': mode_rw = O_WRONLY; mode_x = O_CREAT | O_APPEND; break; case '+': mode_rw = O_RDWR; break; #if MICROPY_PY_IO_FILEIO // If we don't have io.FileIO, then files are in text mode implicitly case 'b': type = &mp_type_fileio; break; case 't': type = &mp_type_textio; break; #endif } } o->base.type = type; mp_obj_t fid = args[0].u_obj; if (MP_OBJ_IS_SMALL_INT(fid)) { o->fd = MP_OBJ_SMALL_INT_VALUE(fid); return MP_OBJ_FROM_PTR(o); } const char *fname = mp_obj_str_get_str(fid); int fd = open(fname, mode_x | mode_rw, 0644); if (fd == -1) { mp_raise_OSError(errno); } o->fd = fd; return MP_OBJ_FROM_PTR(o); } STATIC mp_obj_t fdfile_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS]; mp_arg_parse_all_kw_array(n_args, n_kw, args, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals); return fdfile_open(type, arg_vals); } STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&fdfile_fileno_obj) }, { 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_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_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(&fdfile_close_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&fdfile___exit___obj) }, }; STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table); #if MICROPY_PY_IO_FILEIO STATIC const mp_stream_p_t fileio_stream_p = { .read = fdfile_read, .write = fdfile_write, .ioctl = fdfile_ioctl, }; const mp_obj_type_t mp_type_fileio = { { &mp_type_type }, .name = MP_QSTR_FileIO, .print = fdfile_print, .make_new = fdfile_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &fileio_stream_p, .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, }; #endif STATIC const mp_stream_p_t textio_stream_p = { .read = fdfile_read, .write = fdfile_write, .ioctl = fdfile_ioctl, .is_text = true, }; const mp_obj_type_t mp_type_textio = { { &mp_type_type }, .name = MP_QSTR_TextIOWrapper, .print = fdfile_print, .make_new = fdfile_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &textio_stream_p, .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, }; // Factory function for I/O stream classes mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { // TODO: analyze buffering args and instantiate appropriate type mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS]; mp_arg_parse_all(n_args, args, kwargs, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals); return fdfile_open(&mp_type_textio, arg_vals); } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); #endif // MICROPY_PY_IO ================================================ FILE: port/modules/modnetwork.c ================================================ /* * 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 "py/objlist.h" #include "py/runtime.h" #include "py/mphal.h" #include "lib/netutils/netutils.h" #include "modnetwork.h" #if MICROPY_PY_NETWORK STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) }, #if defined(MICROPY_PY_WLAN) { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&get_wlan_obj) }, { MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(STATION_IF)}, { MP_ROM_QSTR(MP_QSTR_AP_IF), MP_ROM_INT(SOFTAP_IF)}, #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_network_globals, mp_module_network_globals_table); const mp_obj_module_t mp_module_network = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_network_globals, }; #endif // MICROPY_PY_NETWORK ================================================ FILE: port/modules/modnetwork.h ================================================ /* * 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_MODNETWORK_H #define MICROPY_INCLUDED_MODNETWORK_H #define STATION_IF 0 #define SOFTAP_IF 1 #define MOD_NETWORK_AF_INET (2) #define MOD_NETWORK_AF_INET6 (10) #define MOD_NETWORK_SOCK_STREAM (1) #define MOD_NETWORK_SOCK_DGRAM (2) #define MOD_NETWORK_SOCK_RAW (3) #define MODNETWORK_INCLUDE_CONSTANTS (1) MP_DECLARE_CONST_FUN_OBJ_KW(get_wlan_obj); #endif // MICROPY_INCLUDED_MODNETWORK_H ================================================ FILE: port/modules/modnetwork_wlan.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 SummerGift * * 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/objlist.h" #include "py/runtime.h" #include "py/mphal.h" #include "lib/netutils/netutils.h" #if MICROPY_PY_WLAN #include #include #include #include #include #include #include #include "modnetwork.h" extern struct netdev *netdev_default; typedef struct _wlan_if_obj_t { mp_obj_base_t base; int if_id; } wlan_if_obj_t; enum { STATION_IDLE = 0, STATION_CONNECTING, STATION_WRONG_PASSWORD, STATION_NO_AP_FOUND, STATION_CONNECT_FAIL, STATION_GOT_IP, }; const mp_obj_type_t wlan_if_type; STATIC struct rt_wlan_info _ap_info; STATIC char _ap_password[RT_WLAN_PASSWORD_MAX_LENGTH]; STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, msg)); } } STATIC const wlan_if_obj_t wlan_objs[] = { {{&wlan_if_type}, STATION_IF}, {{&wlan_if_type}, SOFTAP_IF}, }; STATIC void require_if(mp_obj_t wlan_if, int if_no) { wlan_if_obj_t *self = MP_OBJ_TO_PTR(wlan_if); if (self->if_id != if_no) { error_check(false, if_no == STATION_IF ? "STA required" : "AP required"); } } STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) { int idx = 0; if (n_args > 0) { idx = mp_obj_get_int(args[0]); if (idx < 0 || idx >= sizeof(wlan_objs)) { mp_raise_ValueError(NULL); } } return MP_OBJ_FROM_PTR(&wlan_objs[idx]); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_wlan_obj, 0, 1, get_wlan); STATIC mp_obj_t wlan_active(size_t n_args, const mp_obj_t *args) { wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args > 1) { if (self->if_id == STATION_IF) { if (mp_obj_get_int(args[1]) == RT_TRUE) { error_check(netdev_set_up(netdev_default) == RT_EOK, "Cannot active wlan device"); } else { error_check(netdev_set_down(netdev_default) == RT_EOK, "Cannot disable wlan device"); } } else { if (mp_obj_get_int(args[1]) == RT_TRUE) { error_check(rt_wlan_start_ap((char *)&_ap_info.ssid.val, _ap_password) == RT_EOK, "Cannot start AP"); } else { error_check(rt_wlan_ap_stop() == RT_EOK, "Cannot stop AP"); } } return mp_const_none; } if (self->if_id == STATION_IF) { return mp_obj_new_bool(rt_wlan_get_mode("wlan0") == RT_WLAN_STATION); } else { return mp_obj_new_bool(rt_wlan_get_mode("wlan1") == RT_WLAN_AP); } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wlan_active_obj, 1, 2, wlan_active); STATIC mp_obj_t wlan_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_ssid, ARG_password, ARG_bssid }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_bssid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; // 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); require_if(pos_args[0], STATION_IF); const char *ssid = RT_NULL; const char *key = RT_NULL; size_t len; const char *p; // set parameters based on given args if (args[ARG_ssid].u_obj != mp_const_none) { p = mp_obj_str_get_data(args[ARG_ssid].u_obj, &len); ssid = p; } if (args[ARG_password].u_obj != mp_const_none) { p = mp_obj_str_get_data(args[ARG_password].u_obj, &len); key = p; } error_check(rt_wlan_connect(ssid, key) == RT_EOK, "Cannot connect to AP"); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wlan_connect_obj, 1, wlan_connect); STATIC mp_obj_t wlan_disconnect(mp_obj_t self_in) { require_if(self_in, STATION_IF); error_check(rt_wlan_disconnect() == RT_EOK, "Cannot disconnect from AP"); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_disconnect_obj, wlan_disconnect); STATIC mp_obj_t wlan_status(size_t n_args, const mp_obj_t *args) { wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { // Get link status if (self->if_id == STATION_IF) { if(rt_wlan_is_ready() == RT_EOK) { return MP_OBJ_NEW_SMALL_INT(STATION_GOT_IP); } else { return MP_OBJ_NEW_SMALL_INT(STATION_IDLE); } } return MP_OBJ_NEW_SMALL_INT(-1); } else { // Get specific status parameter switch (mp_obj_str_get_qstr(args[1])) { case MP_QSTR_rssi: if (self->if_id == STATION_IF) { return MP_OBJ_NEW_SMALL_INT(rt_wlan_get_rssi()); } } mp_raise_ValueError("unknown status param"); } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wlan_status_obj, 1, 2, wlan_status); #if (RTTHREAD_VERSION >= RT_VERSION_CHECK(4, 1, 0)) && defined(RT_USING_WIFI) #include #include #include static struct rt_semaphore scan_done; struct rt_wlan_scan_result *scan_result_cache = RT_NULL; static void wlan_scan_report_hander(int event,struct rt_wlan_buff *buff,void *parameter) { struct rt_wlan_info *info = RT_NULL; int index = 0; int ret = RT_EOK; RT_ASSERT(event == RT_WLAN_EVT_SCAN_REPORT); RT_ASSERT(buff != RT_NULL); RT_ASSERT(parameter != RT_NULL); info = (struct rt_wlan_info *)buff->data; index = *((int *)(parameter)); if (scan_result_cache == RT_NULL) { RT_ASSERT(index == 0); scan_result_cache = rt_malloc(sizeof(struct rt_wlan_scan_result) + (rt_ubase_t)sizeof(struct rt_wlan_info)); scan_result_cache->num = 0; scan_result_cache->info = (struct rt_wlan_info *)(rt_ubase_t)((rt_ubase_t)&scan_result_cache->info + sizeof(struct rt_wlan_info *)); } else { scan_result_cache = rt_realloc(scan_result_cache, sizeof(struct rt_wlan_scan_result) + (uint32_t)sizeof(struct rt_wlan_info) * (index + 1)); scan_result_cache->info = (struct rt_wlan_info *)(rt_ubase_t)((rt_ubase_t)&scan_result_cache->info + sizeof(struct rt_wlan_info *)); } if (scan_result_cache == RT_NULL) { LOG_E("malloc failed!"); } rt_memcpy(&(scan_result_cache->info)[index], info, sizeof(struct rt_wlan_info)); scan_result_cache->num = index + 1; ++ *((int *)(parameter)); } static void wlan_scan_done_hander(int event,struct rt_wlan_buff *buff,void *parameter) { RT_ASSERT(event == RT_WLAN_EVT_SCAN_DONE); rt_sem_release(&scan_done); } struct rt_wlan_scan_result *rt_wlan_scan_sync(void) { static int _init = 0; static int i = 0; LOG_D("start to scan ap ..."); if (!_init) { _init = 1; rt_sem_init(&scan_done, "scan_done", 0 , RT_IPC_FLAG_FIFO); } rt_wlan_register_event_handler(RT_WLAN_EVT_SCAN_REPORT, wlan_scan_report_hander,&i); rt_wlan_register_event_handler(RT_WLAN_EVT_SCAN_DONE, wlan_scan_done_hander,RT_NULL); if(rt_wlan_scan() == RT_EOK) { LOG_D("the scan is started... "); } else { LOG_E("scan failed"); } rt_sem_take(&scan_done, RT_WAITING_FOREVER); i = 0; return scan_result_cache; } void rt_wlan_scan_result_clean(void) { if(scan_result_cache) { rt_free(scan_result_cache); scan_result_cache = 0; } } #endif STATIC mp_obj_t *wlan_scan_list = NULL; void wlan_station_scan(void) { if (wlan_scan_list == NULL) { // called unexpectedly return; } struct rt_wlan_scan_result *scan_result = RT_NULL; /* scan ap info */ scan_result = rt_wlan_scan_sync(); if (scan_result) { int index, num; char *security; num = scan_result->num; for (index = 0; index < num; index ++) { switch (scan_result->info[index].security) { case SECURITY_OPEN: security = "OPEN"; break; case SECURITY_WEP_PSK: security = "WEP_PSK"; break; case SECURITY_WEP_SHARED: security = "WEP_SHARED"; break; case SECURITY_WPA_TKIP_PSK: security = "WPA_TKIP_PSK"; break; case SECURITY_WPA_AES_PSK: security = "WPA_AES_PSK"; break; case SECURITY_WPA2_AES_PSK: security = "WPA2_AES_PSK"; break; case SECURITY_WPA2_TKIP_PSK: security = "WPA2_TKIP_PSK"; break; case SECURITY_WPA2_MIXED_PSK: security = "WPA2_MIXED_PSK"; break; case SECURITY_WPS_OPEN: security = "WPS_OPEN"; break; case SECURITY_WPS_SECURE: security = "WPS_SECURE"; break; default: security = "UNKNOWN"; break; } mp_obj_tuple_t *t = mp_obj_new_tuple(6, NULL); t->items[0] = mp_obj_new_bytes(&scan_result->info[index].ssid.val[0], strlen((char *)(&scan_result->info[index].ssid.val[0]))); t->items[1] = mp_obj_new_bytes(&scan_result->info[index].bssid[0], strlen((char *)(&scan_result->info[index].bssid[0]))); t->items[2] = MP_OBJ_NEW_SMALL_INT(scan_result->info[index].channel); t->items[3] = MP_OBJ_NEW_SMALL_INT(scan_result->info[index].rssi); t->items[4] = mp_obj_new_bytes((const byte *)security, strlen(security)); t->items[5] = MP_OBJ_NEW_SMALL_INT(scan_result->info[index].hidden); mp_obj_list_append(*wlan_scan_list, MP_OBJ_FROM_PTR(t)); } rt_wlan_scan_result_clean(); } else { mp_printf(&mp_plat_print, ("wifi scan result is null\n")); *wlan_scan_list = MP_OBJ_NULL; } } STATIC mp_obj_t wlan_scan(mp_obj_t self_in) { require_if(self_in, STATION_IF); mp_obj_t list = mp_obj_new_list(0, NULL); wlan_scan_list = &list; wlan_station_scan(); if (list == MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "scan failed")); } return list; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_scan_obj, wlan_scan); /// \method isconnected() /// Return True if connected to an AP and an IP address has been assigned, /// false otherwise. STATIC mp_obj_t wlan_isconnected(mp_obj_t self_in) { wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->if_id == STATION_IF) { if (rt_wlan_is_connected() == RT_TRUE) { return mp_const_true; } } else { if (rt_wlan_ap_get_sta_num() > 0) { return mp_const_true; } } return mp_const_false; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_isconnected_obj, wlan_isconnected); STATIC mp_obj_t wlan_ifconfig(size_t n_args, const mp_obj_t *args) { wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); struct netdev *netdev = netdev_default; if (netdev == RT_NULL) { mp_printf(&mp_plat_print, ("not find wlan interface device.\n")); return MP_OBJ_NEW_SMALL_INT(-1); } if (n_args == 1) { // get mp_obj_t tuple[4] = { mp_obj_new_str((const char *)inet_ntoa(netdev->ip_addr), strlen((char *)(inet_ntoa(netdev->ip_addr)))), mp_obj_new_str((const char *)inet_ntoa(netdev->netmask), strlen((char *)(inet_ntoa(netdev->netmask)))), mp_obj_new_str((const char *)inet_ntoa(netdev->gw), strlen((char *)(inet_ntoa(netdev->gw)))), mp_obj_new_str((const char *)inet_ntoa(netdev->dns_servers), strlen((char *)(inet_ntoa(netdev->dns_servers)))), }; return mp_obj_new_tuple(4, tuple); } else { // set mp_obj_t *items; uint8_t ip_addr[4]; uint8_t netmask[4]; uint8_t gw[4]; uint8_t dns_server[4]; mp_obj_get_array_fixed_n(args[1], 4, &items); netutils_parse_ipv4_addr(items[0], (uint8_t *)ip_addr, NETUTILS_BIG); netutils_parse_ipv4_addr(items[1], (uint8_t *)netmask, NETUTILS_BIG); netutils_parse_ipv4_addr(items[2], (uint8_t *)gw , NETUTILS_BIG); netutils_parse_ipv4_addr(items[3], (uint8_t *)dns_server, NETUTILS_BIG); // To set a static IP we have to disable DHCP first if (self->if_id == STATION_IF) { if(netdev_dhcp_enabled(netdev, 0) == RT_EOK) { if (netdev_set_ipaddr(netdev, (const ip_addr_t *)ip_addr) != RT_EOK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "netdev_set_ipaddr() failed")); } } else { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "netdev_dhcp_enabled() failed")); } } else { // TODO modify IP netmask gw under AP mode netdev_set_dns_server(netdev, 0, (const ip_addr_t *)dns_server); return mp_const_none; } netdev_set_netmask(netdev, (const ip_addr_t *)netmask); netdev_set_gw(netdev, (const ip_addr_t *)gw); netdev_set_dns_server(netdev, 0, (const ip_addr_t *)dns_server); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wlan_ifconfig_obj, 1, 2, wlan_ifconfig); STATIC mp_obj_t wlan_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { if (n_args != 1 && kwargs->used != 0) { mp_raise_TypeError("either pos or kw args are allowed"); } wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); struct rt_wlan_info cfg = {0}; if (self->if_id == STATION_IF) { error_check(rt_wlan_get_info(&cfg) == RT_EOK, "can't get STA config"); } else { error_check(rt_wlan_ap_get_info(&cfg) == RT_EOK, "can't get AP config"); } int req_if = -1; if (kwargs->used != 0) { for (mp_uint_t i = 0; i < kwargs->alloc; i++) { if (mp_map_slot_is_filled(kwargs, i)) { #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) switch ((uintptr_t)kwargs->table[i].key) { case QS(MP_QSTR_mac): { mp_buffer_info_t bufinfo; mp_get_buffer_raise(kwargs->table[i].value, &bufinfo, MP_BUFFER_READ); if (bufinfo.len != 6) { mp_raise_ValueError("invalid buffer length"); } error_check(rt_wlan_set_mac((rt_uint8_t *)bufinfo.buf) == RT_EOK, "can't set MAC"); break; } case QS(MP_QSTR_essid): { req_if = SOFTAP_IF; size_t len; const char *s = mp_obj_str_get_data(kwargs->table[i].value, &len); len = MIN(len, sizeof(_ap_info.ssid.val)); memcpy(_ap_info.ssid.val, s, len); _ap_info.ssid.len = len; break; } case QS(MP_QSTR_hidden): { req_if = SOFTAP_IF; _ap_info.hidden = mp_obj_is_true(kwargs->table[i].value); break; } // case QS(MP_QSTR_authmode): { // req_if = SOFTAP_IF; // cfg.ap.authmode = mp_obj_get_int(kwargs->table[i].value); // break; // } case QS(MP_QSTR_password): { req_if = SOFTAP_IF; size_t len; const char *s = mp_obj_str_get_data(kwargs->table[i].value, &len); len = MIN(len, sizeof(_ap_password) - 1); memcpy(_ap_password, s, len); _ap_password[len] = 0; break; } case QS(MP_QSTR_channel): { req_if = SOFTAP_IF; _ap_info.channel = mp_obj_get_int(kwargs->table[i].value); break; } // case QS(MP_QSTR_dhcp_hostname): { // req_if = STATION_IF; // if (self->if_id == STATION_IF) { // const char *s = mp_obj_str_get_str(kwargs->table[i].value); // wifi_station_set_hostname((char*)s); // } // break; // } default: goto unknown; } #undef QS } } // We post-check interface requirements to save on code size if (req_if >= 0) { require_if(args[0], req_if); } return mp_const_none; } // Get config if (n_args != 2) { mp_raise_TypeError("can query only one param"); } mp_obj_t val; qstr key = mp_obj_str_get_qstr(args[1]); switch (key) { case MP_QSTR_mac: { uint8_t mac[6]; error_check(rt_wlan_get_mac(mac) == RT_EOK, "can't get mac config"); return mp_obj_new_bytes(mac, sizeof(mac)); } case MP_QSTR_essid: if (self->if_id == STATION_IF) { val = mp_obj_new_str((char*)cfg.ssid.val, strlen((char*)cfg.ssid.val)); } else { val = mp_obj_new_str((char*)_ap_info.ssid.val, _ap_info.ssid.len); } break; case MP_QSTR_hidden: req_if = SOFTAP_IF; val = mp_obj_new_bool(cfg.hidden); break; // case MP_QSTR_authmode: // req_if = SOFTAP_IF; // val = MP_OBJ_NEW_SMALL_INT(cfg.ap.authmode); // break; case MP_QSTR_channel: req_if = SOFTAP_IF; val = MP_OBJ_NEW_SMALL_INT(cfg.channel); break; // case MP_QSTR_dhcp_hostname: { // req_if = STATION_IF; // char* s = wifi_station_get_hostname(); // if (s == NULL) { // val = MP_OBJ_NEW_QSTR(MP_QSTR_); // } else { // val = mp_obj_new_str(s, strlen(s)); // } // break; // } default: goto unknown; } // We post-check interface requirements to save on code size if (req_if >= 0) { require_if(args[0], req_if); } return val; unknown: mp_raise_ValueError("unknown config param"); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wlan_config_obj, 1, wlan_config); STATIC const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&wlan_active_obj) }, { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&wlan_connect_obj) }, { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&wlan_disconnect_obj) }, { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&wlan_status_obj) }, { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&wlan_scan_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&wlan_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&wlan_config_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wlan_ifconfig_obj) }, #if MODNETWORK_INCLUDE_CONSTANTS { MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(STATION_IDLE)}, { MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(STATION_CONNECTING)}, { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(STATION_WRONG_PASSWORD)}, { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(STATION_NO_AP_FOUND)}, { MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(STATION_CONNECT_FAIL)}, { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(STATION_GOT_IP)}, // { MP_ROM_QSTR(MP_QSTR_MODE_11B), MP_ROM_INT(PHY_MODE_11B) }, // { MP_ROM_QSTR(MP_QSTR_MODE_11G), MP_ROM_INT(PHY_MODE_11G) }, // { MP_ROM_QSTR(MP_QSTR_MODE_11N), MP_ROM_INT(PHY_MODE_11N) }, // { MP_ROM_QSTR(MP_QSTR_AUTH_OPEN), MP_ROM_INT(AUTH_OPEN) }, // { MP_ROM_QSTR(MP_QSTR_AUTH_WEP), MP_ROM_INT(AUTH_WEP) }, // { MP_ROM_QSTR(MP_QSTR_AUTH_WPA_PSK), MP_ROM_INT(AUTH_WPA_PSK) }, // { MP_ROM_QSTR(MP_QSTR_AUTH_WPA2_PSK), MP_ROM_INT(AUTH_WPA2_PSK) }, // { MP_ROM_QSTR(MP_QSTR_AUTH_WPA_WPA2_PSK), MP_ROM_INT(AUTH_WPA_WPA2_PSK) }, #endif }; STATIC MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); const mp_obj_type_t wlan_if_type = { { &mp_type_type }, .name = MP_QSTR_WLAN, .locals_dict = (mp_obj_dict_t*)&wlan_if_locals_dict, }; #endif ================================================ FILE: port/modules/modpyb.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include "py/runtime.h" #include "py/gc.h" #include "py/builtin.h" #include "py/mphal.h" #include "lib/utils/pyexec.h" #include "modmachine.h" #include "extmod/vfs.h" #include "extmod/utime_mphal.h" /// \function elapsed_millis(start) /// Returns the number of milliseconds which have elapsed since `start`. /// /// This function takes care of counter wrap, and always returns a positive /// number. This means it can be used to measure periods upto about 12.4 days. /// /// Example: /// start = pyb.millis() /// while pyb.elapsed_millis(start) < 1000: /// # Perform some operation STATIC mp_obj_t pyb_elapsed_millis(mp_obj_t start) { uint32_t startMillis = mp_obj_get_int(start); uint32_t currMillis = mp_hal_ticks_ms(); return MP_OBJ_NEW_SMALL_INT((currMillis - startMillis) & 0x3fffffff); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_millis_obj, pyb_elapsed_millis); /// \function elapsed_micros(start) /// Returns the number of microseconds which have elapsed since `start`. /// /// This function takes care of counter wrap, and always returns a positive /// number. This means it can be used to measure periods upto about 17.8 minutes. /// /// Example: /// start = pyb.micros() /// while pyb.elapsed_micros(start) < 1000: /// # Perform some operation STATIC mp_obj_t pyb_elapsed_micros(mp_obj_t start) { uint32_t startMicros = mp_obj_get_int(start); uint32_t currMicros = mp_hal_ticks_us(); return MP_OBJ_NEW_SMALL_INT((currMicros - startMicros) & 0x3fffffff); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_micros_obj, pyb_elapsed_micros); MP_DECLARE_CONST_FUN_OBJ_KW(pyb_main_obj); // defined in main.c STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pyb) }, { MP_ROM_QSTR(MP_QSTR_hard_reset), MP_ROM_PTR(&machine_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_info_obj) }, { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, #if MICROPY_REPL_INFO { MP_ROM_QSTR(MP_QSTR_repl_info), MP_ROM_PTR(&pyb_set_repl_info_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_wfi), MP_ROM_PTR(&pyb_wfi_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&pyb_disable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&pyb_enable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_sleep_obj) }, { MP_ROM_QSTR(MP_QSTR_standby), MP_ROM_PTR(&machine_deepsleep_obj) }, // { MP_ROM_QSTR(MP_QSTR_main), MP_ROM_PTR(&pyb_main_obj) }, // { MP_ROM_QSTR(MP_QSTR_repl_uart), MP_ROM_PTR(&pyb_repl_uart_obj) }, { MP_ROM_QSTR(MP_QSTR_millis), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_elapsed_millis), MP_ROM_PTR(&pyb_elapsed_millis_obj) }, { MP_ROM_QSTR(MP_QSTR_micros), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, { MP_ROM_QSTR(MP_QSTR_elapsed_micros), MP_ROM_PTR(&pyb_elapsed_micros_obj) }, { MP_ROM_QSTR(MP_QSTR_delay), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_udelay), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, // { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_os_mount_obj) }, // { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&pyb_timer_type) }, //#if MICROPY_HW_ENABLE_RNG // { MP_ROM_QSTR(MP_QSTR_rng), MP_ROM_PTR(&pyb_rng_get_obj) }, //#endif // //#if MICROPY_HW_ENABLE_RTC // { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) }, //#endif // #if MICROPY_PY_PIN { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, #endif // { MP_ROM_QSTR(MP_QSTR_ExtInt), MP_ROM_PTR(&extint_type) }, // //#if MICROPY_HW_ENABLE_SERVO // { MP_ROM_QSTR(MP_QSTR_pwm), MP_ROM_PTR(&pyb_pwm_set_obj) }, // { MP_ROM_QSTR(MP_QSTR_servo), MP_ROM_PTR(&pyb_servo_set_obj) }, // { MP_ROM_QSTR(MP_QSTR_Servo), MP_ROM_PTR(&pyb_servo_type) }, //#endif // //#if MICROPY_HW_HAS_SWITCH // { MP_ROM_QSTR(MP_QSTR_Switch), MP_ROM_PTR(&pyb_switch_type) }, //#endif // //#if MICROPY_HW_HAS_FLASH // { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&pyb_flash_type) }, //#endif // //#if MICROPY_HW_HAS_SDCARD // { MP_ROM_QSTR(MP_QSTR_SD), MP_ROM_PTR(&pyb_sdcard_obj) }, // now obsolete // { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&pyb_sdcard_type) }, //#endif // //#if defined(MICROPY_HW_LED1) // { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pyb_led_type) }, //#endif // { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&pyb_i2c_type) }, // { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&pyb_spi_type) }, // { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, //#if MICROPY_HW_ENABLE_CAN // { MP_ROM_QSTR(MP_QSTR_CAN), MP_ROM_PTR(&pyb_can_type) }, //#endif // // { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pyb_adc_type) }, // { MP_ROM_QSTR(MP_QSTR_ADCAll), MP_ROM_PTR(&pyb_adc_all_type) }, // //#if MICROPY_HW_ENABLE_DAC // { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pyb_dac_type) }, //#endif // //#if MICROPY_HW_HAS_MMA7660 // { MP_ROM_QSTR(MP_QSTR_Accel), MP_ROM_PTR(&pyb_accel_type) }, //#endif // //#if MICROPY_HW_HAS_LCD // { MP_ROM_QSTR(MP_QSTR_LCD), MP_ROM_PTR(&pyb_lcd_type) }, //#endif }; STATIC MP_DEFINE_CONST_DICT(pyb_module_globals, pyb_module_globals_table); const mp_obj_module_t pyb_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&pyb_module_globals, }; ================================================ FILE: port/modules/modrtthread.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "py/mpconfig.h" #if MICROPY_PY_RTTHREAD #include #include #include "py/runtime.h" rt_bool_t rt_is_preempt_thread(void) { if (rt_interrupt_get_nest() || rt_critical_level()) { return RT_FALSE; } else { return RT_TRUE; } } STATIC mp_obj_t mod_is_preempt_thread(void) { return mp_obj_new_bool(rt_is_preempt_thread()); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_is_preempt_thread_obj, mod_is_preempt_thread); STATIC mp_obj_t mod_current_tid(void) { return MP_OBJ_NEW_SMALL_INT(rt_thread_self()); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_current_tid_obj, mod_current_tid); STATIC mp_obj_t mod_stacks_analyze(void) { #ifdef RT_USING_FINSH extern long list_thread(void); list_thread(); #else mp_printf(&mp_plat_print, "Not available when FINSH module disable\n"); #endif return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_stacks_analyze_obj, mod_stacks_analyze); STATIC mp_obj_t mod_list_device(void) { struct rt_device *device; struct rt_list_node *node; struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device); struct rt_list_node *list = &info->object_list; mp_obj_t mp_list = mp_obj_new_list(0, NULL); rt_enter_critical(); for (node = list->next; node != list; node = node->next) { device = (struct rt_device *)(rt_list_entry(node, struct rt_object, list)); mp_obj_tuple_t *t = mp_obj_new_tuple(2, NULL); t->items[0] = mp_obj_new_str(device->parent.name, strlen((char *)device->parent.name)); t->items[1] = MP_OBJ_NEW_SMALL_INT((device->type <= RT_Device_Class_Unknown) ? device->type : RT_Device_Class_Unknown); mp_obj_list_append(mp_list, MP_OBJ_FROM_PTR(t)); } rt_exit_critical(); return mp_list; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_list_device_obj, mod_list_device); STATIC const mp_rom_map_elem_t mp_module_rtthread_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rtthread) }, { MP_ROM_QSTR(MP_QSTR_is_preempt_thread), MP_ROM_PTR(&mod_is_preempt_thread_obj) }, { MP_ROM_QSTR(MP_QSTR_current_tid), MP_ROM_PTR(&mod_current_tid_obj) }, { MP_ROM_QSTR(MP_QSTR_stacks_analyze), MP_ROM_PTR(&mod_stacks_analyze_obj) }, { MP_ROM_QSTR(MP_QSTR_list_device), MP_ROM_PTR(&mod_list_device_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_rtthread_globals, mp_module_rtthread_globals_table); const mp_obj_module_t mp_module_rtthread = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_rtthread_globals, }; #endif // MICROPY_PY_RTTHREAD ================================================ FILE: port/modules/moduos.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 SummerGift * * 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" #if MICROPY_PY_MODUOS #include #include #include "py/runtime.h" #include "py/objtuple.h" #include "py/objstr.h" #include "py/mperrno.h" #include "lib/timeutils/timeutils.h" #include "extmod/misc.h" #include "genhdr/mpversion.h" #if !MICROPY_VFS #if MICROPY_PY_MODUOS_FILE #include "moduos_file.h" #endif #else #include "extmod/vfs.h" #if MICROPY_VFS_FAT #include "extmod/vfs_fat.h" #endif #endif STATIC const qstr os_uname_info_fields[] = { MP_QSTR_sysname, MP_QSTR_nodename, MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine }; STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, "pyboard"); STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, "pyboard"); STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); STATIC MP_DEFINE_ATTRTUPLE( os_uname_info_obj, os_uname_info_fields, 5, (mp_obj_t)&os_uname_info_sysname_obj, (mp_obj_t)&os_uname_info_nodename_obj, (mp_obj_t)&os_uname_info_release_obj, (mp_obj_t)&os_uname_info_version_obj, (mp_obj_t)&os_uname_info_machine_obj ); STATIC mp_obj_t os_uname(void) { return (mp_obj_t)&os_uname_info_obj; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); STATIC mp_obj_t os_sync(void) { return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(mod_os_sync_obj, os_sync); #if MICROPY_HW_ENABLE_RNG /// \function urandom(n) /// Return a bytes object with n random bytes, generated by the hardware /// random number generator. STATIC mp_obj_t os_urandom(mp_obj_t num) { mp_int_t n = mp_obj_get_int(num); vstr_t vstr; vstr_init_len(&vstr, n); for (int i = 0; i < n; i++) { vstr.buf[i] = rng_get(); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); #endif STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_posix_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_posix_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_posix_listdir_obj) }, { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_posix_listdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_posix_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_posix_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_rename),MP_ROM_PTR(&mp_posix_rename_obj)}, { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_posix_rmdir_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_posix_stat_obj) }, { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mp_posix_remove_obj) }, // unlink aliases to remove { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mod_os_sync_obj) }, { MP_ROM_QSTR(MP_QSTR_file_crc32), MP_ROM_PTR(&mp_posix_file_crc32_obj) }, /// \constant sep - separation character used in paths //{ MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, #if MICROPY_HW_ENABLE_RNG { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, #endif // these are MicroPython extensions { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_posix_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_posix_umount_obj) }, { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&mp_posix_mkfs_obj) }, //{ MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, }; STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); const mp_obj_module_t mp_module_uos = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&os_module_globals, }; #endif /* MICROPY_PY_MODUOS */ ================================================ FILE: port/modules/moduos_file.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 SummerGift * * 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" #if MICROPY_PY_MODUOS_FILE #include #include #include #include "py/runtime.h" #include "py/objstr.h" #include "py/mperrno.h" #include "moduos_file.h" mp_obj_t mp_posix_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(mp_posix_mount_obj, 2, mp_posix_mount); mp_obj_t mp_posix_umount(mp_obj_t mnt_in) { return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_posix_umount_obj, mp_posix_umount); mp_obj_t mp_posix_mkfs(size_t n_args, const mp_obj_t *args) { int result = RT_EOK; char *type = "elm"; /* use the default file system type as 'fatfs' */ if (n_args == 1) { result = dfs_mkfs(type, mp_obj_str_get_str(args[0])); }else if (n_args == 2) { type = (char *)mp_obj_str_get_str(args[0]); result = dfs_mkfs(type, mp_obj_str_get_str(args[1])); } if (result != RT_EOK) { mp_raise_ValueError("mkfs failed, please check filesystem type and device name."); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_posix_mkfs_obj, 1, 2, mp_posix_mkfs); mp_obj_t mp_posix_chdir(mp_obj_t path_in) { const char *changepath = mp_obj_str_get_str(path_in); if (chdir(changepath) != 0) { mp_printf(&mp_plat_print, "No such directory: %s\n", changepath); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_posix_chdir_obj, mp_posix_chdir); mp_obj_t mp_posix_getcwd(void) { char buf[MICROPY_ALLOC_PATH_MAX + 1]; getcwd(buf, sizeof(buf)); return mp_obj_new_str(buf, strlen(buf)); } MP_DEFINE_CONST_FUN_OBJ_0(mp_posix_getcwd_obj, mp_posix_getcwd); #include static struct dfs_fd fd; static struct dirent dirent; mp_obj_t mp_posix_listdir(size_t n_args, const mp_obj_t *args) { mp_obj_t dir_list = mp_obj_new_list(0, NULL); struct stat stat; int length; char *fullpath, *path; const char *pathname; if (n_args == 0) { #ifdef DFS_USING_WORKDIR extern char working_directory[]; pathname = working_directory; #else pathname = "/"; #endif } else { pathname = mp_obj_str_get_str(args[0]); } fullpath = NULL; if (pathname == NULL) { #ifdef DFS_USING_WORKDIR extern char working_directory[]; /* open current working directory */ path = rt_strdup(working_directory); #else path = rt_strdup("/"); #endif if (path == NULL) mp_raise_OSError(MP_ENOMEM); /* out of memory */ } else { path = (char *)pathname; } /* list directory */ if (dfs_file_open(&fd, path, O_DIRECTORY) == 0) { do { memset(&dirent, 0, sizeof(struct dirent)); length = dfs_file_getdents(&fd, &dirent, sizeof(struct dirent)); if (length > 0) { memset(&stat, 0, sizeof(struct stat)); /* build full path for each file */ fullpath = dfs_normalize_path(path, dirent.d_name); if (fullpath == NULL) break; if (dfs_file_stat(fullpath, &stat) == 0) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); t->items[0] = mp_obj_new_str(dirent.d_name, strlen(dirent.d_name)); t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number mp_obj_t next = MP_OBJ_FROM_PTR(t); mp_obj_t *items; mp_obj_get_array_fixed_n(next, 3, &items); mp_obj_list_append(dir_list, items[0]); } else { mp_printf(&mp_plat_print, "BAD file: %s\n", dirent.d_name); } rt_free(fullpath); } } while (length > 0); dfs_file_close(&fd); } else { // mp_printf(&mp_plat_print, "No such directory\n"); } if (pathname == NULL) rt_free(path); return dir_list; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_posix_listdir_obj, 0, 1, mp_posix_listdir); mp_obj_t mp_posix_mkdir(mp_obj_t path_in) { const char *createpath = mp_obj_str_get_str(path_in); int res = mkdir(createpath, 0); if (res != 0) { mp_raise_OSError(MP_EEXIST); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_posix_mkdir_obj, mp_posix_mkdir); mp_obj_t mp_posix_remove(uint n_args, const mp_obj_t *arg) { int index; if (n_args == 0) { mp_printf(&mp_plat_print, "Usage: rm FILE...\n"); mp_printf(&mp_plat_print, "Remove (unlink) the FILE(s).\n"); return mp_const_none; } for (index = 0; index < n_args; index++) { //mp_printf(&mp_plat_print, "Remove %s.\n", mp_obj_str_get_str(arg[index])); unlink(mp_obj_str_get_str(arg[index])); } // TODO recursive deletion return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR(mp_posix_remove_obj, 0, mp_posix_remove); mp_obj_t mp_posix_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) { const char *old_path = mp_obj_str_get_str(old_path_in); const char *new_path = mp_obj_str_get_str(new_path_in); int res = rename(old_path, new_path); if (res != 0) { mp_raise_OSError(MP_EPERM); } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(mp_posix_rename_obj, mp_posix_rename); mp_obj_t mp_posix_rmdir(uint n_args, const mp_obj_t *arg) { int index; if (n_args == 0) { mp_printf(&mp_plat_print, "Usage: rm FILE...\n"); mp_printf(&mp_plat_print, "Remove (unlink) the FILE(s).\n"); return mp_const_none; } for (index = 0; index < n_args; index++) { //mp_printf(&mp_plat_print, "Remove %s.\n", mp_obj_str_get_str(arg[index])); rmdir(mp_obj_str_get_str(arg[index])); } // TODO recursive deletion return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR(mp_posix_rmdir_obj, 0, mp_posix_rmdir); mp_obj_t mp_posix_stat(mp_obj_t path_in) { struct stat buf; const char *createpath = mp_obj_str_get_str(path_in); int res = stat(createpath, &buf); if (res != 0) { mp_raise_OSError(MP_EPERM); } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(buf.st_mode); // st_mode t->items[1] = MP_OBJ_NEW_SMALL_INT(buf.st_ino); // st_ino t->items[2] = MP_OBJ_NEW_SMALL_INT(buf.st_dev); // st_dev t->items[3] = MP_OBJ_NEW_SMALL_INT(buf.st_nlink); // st_nlink t->items[4] = MP_OBJ_NEW_SMALL_INT(buf.st_uid); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(buf.st_gid); // st_gid t->items[6] = mp_obj_new_int_from_uint(buf.st_size); // st_size t->items[7] = MP_OBJ_NEW_SMALL_INT(buf.st_atime); // st_atime t->items[8] = MP_OBJ_NEW_SMALL_INT(buf.st_mtime); // st_mtime t->items[9] = MP_OBJ_NEW_SMALL_INT(buf.st_ctime); // st_ctime return MP_OBJ_FROM_PTR(t); } MP_DEFINE_CONST_FUN_OBJ_1(mp_posix_stat_obj, mp_posix_stat); static uint32_t calc_crc32(const char* pathname) { #define CALC_BUFFER_SIZE 512 extern uint32_t mp_calc_crc32(uint32_t crc, const void *buf, size_t len); int fd; uint32_t temp_crc = 0; void *buffer = malloc(CALC_BUFFER_SIZE); if (buffer == RT_NULL) { mp_raise_OSError(MP_ENOMEM); } fd = open(pathname, O_RDONLY, 0); if (fd < 0) { return -MP_EINVAL; } while (1) { int len = read(fd, buffer, CALC_BUFFER_SIZE); if (len < 0) { close(fd); return -MP_EIO; } else if (len == 0) break; temp_crc = mp_calc_crc32(temp_crc, buffer, len); } close(fd); free(buffer); return temp_crc; } mp_obj_t mp_posix_file_crc32(mp_obj_t path_in) { extern void mp_hex_to_str(char *pbDest, char *pbSrc, int nLen); uint32_t value = 0; char str[9]; const char *createpath = mp_obj_str_get_str(path_in); value = calc_crc32((char *)createpath); mp_hex_to_str(str,(char *)&value, 4); return mp_obj_new_str(str, strlen(str)); } MP_DEFINE_CONST_FUN_OBJ_1(mp_posix_file_crc32_obj, mp_posix_file_crc32); mp_import_stat_t mp_posix_import_stat(const char *path) { struct stat stat; if (dfs_file_stat(path, &stat) == 0) { if (S_ISDIR(stat.st_mode)) { return MP_IMPORT_STAT_DIR; } else { return MP_IMPORT_STAT_FILE; } } else { return MP_IMPORT_STAT_NO_EXIST; } } #endif //MICROPY_MODUOS_FILE ================================================ FILE: port/modules/moduos_file.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 SummerGift * * 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_MODUOS_FILE_H #define MICROPY_INCLUDED_PY_MODUOS_FILE_H #include "py/lexer.h" #include "py/obj.h" // MicroPython's port-standardized versions of stat constants #define MP_S_IFDIR (0x4000) #define MP_S_IFREG (0x8000) // constants for block protocol ioctl #define BP_IOCTL_INIT (1) #define BP_IOCTL_DEINIT (2) #define BP_IOCTL_SYNC (3) #define BP_IOCTL_SEC_COUNT (4) #define BP_IOCTL_SEC_SIZE (5) mp_obj_t mp_posix_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); mp_obj_t mp_posix_umount(mp_obj_t mnt_in); mp_obj_t mp_posix_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); mp_obj_t mp_posix_chdir(mp_obj_t path_in); mp_obj_t mp_posix_getcwd(void); mp_obj_t mp_posix_listdir(size_t n_args, const mp_obj_t *args); mp_obj_t mp_posix_mkdir(mp_obj_t path_in); mp_obj_t mp_posix_remove(uint n_args, const mp_obj_t *arg); mp_obj_t mp_posix_rename(mp_obj_t old_path_in, mp_obj_t new_path_in); mp_obj_t mp_posix_rmdir(uint n_args, const mp_obj_t *arg); mp_obj_t mp_posix_stat(mp_obj_t path_in); mp_obj_t mp_posix_statvfs(mp_obj_t path_in); mp_obj_t mp_posix_file_crc32(mp_obj_t path_in); mp_obj_t mp_posix_mkfs(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_KW(mp_posix_mount_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_posix_umount_obj); MP_DECLARE_CONST_FUN_OBJ_KW(mp_posix_open_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_posix_chdir_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_posix_getcwd_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_posix_listdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_posix_mkdir_obj); MP_DECLARE_CONST_FUN_OBJ_VAR(mp_posix_remove_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_posix_rename_obj); MP_DECLARE_CONST_FUN_OBJ_VAR(mp_posix_rmdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_posix_stat_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_posix_statvfs_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_posix_file_crc32_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_posix_mkfs_obj); #endif // MICROPY_INCLUDED_PY_MODUOS_FILE_H ================================================ FILE: port/modules/modusocket.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "py/mpconfig.h" #if MICROPY_PY_USOCKET #include #include #include #include #include #include #include #include #include #include "py/objtuple.h" #include "py/objlist.h" #include "py/runtime.h" #include "py/mperrno.h" #include "py/stream.h" #include "py/objstr.h" #include "py/builtin.h" #include "lib/netutils/netutils.h" #include "modnetwork.h" #define SOCKET_POLL_US (100000) // socket class typedef struct _socket_obj_t { mp_obj_base_t base; int fd; uint8_t domain; uint8_t type; uint8_t proto; bool peer_closed; unsigned int retries; } socket_obj_t; STATIC const mp_obj_type_t socket_type; STATIC void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms); NORETURN static void exception_from_errno(int _errno) { // Here we need to convert from lwip errno values to MicroPython's standard ones if (_errno == EINPROGRESS) { _errno = MP_EINPROGRESS; } mp_raise_OSError(_errno); } static inline void check_for_exceptions(void) { mp_handle_pending(true); } static int _socket_getaddrinfo2(const mp_obj_t host, const mp_obj_t portx, struct addrinfo **resp) { const struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, }; mp_obj_t port = portx; if (MP_OBJ_IS_SMALL_INT(port)) { // This is perverse, because lwip_getaddrinfo promptly converts it back to an int, but // that's the API we have to work with ... port = mp_obj_str_binary_op(MP_BINARY_OP_MODULO, mp_obj_new_str_via_qstr("%s", 2), port); } const char *host_str = mp_obj_str_get_str(host); const char *port_str = mp_obj_str_get_str(port); if (host_str[0] == '\0') { // a host of "" is equivalent to the default/all-local IP address host_str = "0.0.0.0"; } MP_THREAD_GIL_EXIT(); int res = getaddrinfo(host_str, port_str, &hints, resp); MP_THREAD_GIL_ENTER(); return res; } int _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) { mp_uint_t len = 0; mp_obj_t *elem; mp_obj_get_array(addrtuple, &len, &elem); if (len != 2) return -1; return _socket_getaddrinfo2(elem[0], elem[1], resp); } STATIC mp_obj_t socket_bind(const mp_obj_t arg0, const mp_obj_t arg1) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); struct addrinfo *res; _socket_getaddrinfo(arg1, &res); int r = bind(self->fd, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); if (r < 0) exception_from_errno(errno); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); STATIC mp_obj_t socket_listen(const mp_obj_t arg0, const mp_obj_t arg1) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); int backlog = mp_obj_get_int(arg1); int r = listen(self->fd, backlog); if (r < 0) exception_from_errno(errno); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen); STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); struct sockaddr addr; socklen_t addr_len = sizeof(addr); int new_fd = -1; for (int i = 0; i <= self->retries; i++) { MP_THREAD_GIL_EXIT(); new_fd = accept(self->fd, &addr, &addr_len); MP_THREAD_GIL_ENTER(); if (new_fd >= 0) break; // if (errno != EAGAIN) exception_from_errno(errno); check_for_exceptions(); } if (new_fd < 0) mp_raise_OSError(MP_ETIMEDOUT); // create new socket object socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); sock->base.type = self->base.type; sock->fd = new_fd; sock->domain = self->domain; sock->type = self->type; sock->proto = self->proto; sock->peer_closed = false; _socket_settimeout(sock, UINT64_MAX); // make the return value uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&addr)->sin_addr; mp_uint_t port = ntohs(((struct sockaddr_in*)&addr)->sin_port); mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL); client->items[0] = sock; client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); return client; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); STATIC mp_obj_t socket_connect(const mp_obj_t arg0, const mp_obj_t arg1) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); struct addrinfo *res; _socket_getaddrinfo(arg1, &res); MP_THREAD_GIL_EXIT(); int r = connect(self->fd, res->ai_addr, res->ai_addrlen); MP_THREAD_GIL_ENTER(); freeaddrinfo(res); if (r != 0) { exception_from_errno(errno); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { (void)n_args; // always 4 socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); int opt = mp_obj_get_int(args[2]); switch (opt) { // level: SOL_SOCKET case SO_REUSEADDR: { int val = mp_obj_get_int(args[3]); int ret = setsockopt(self->fd, SOL_SOCKET, opt, &val, sizeof(int)); if (ret != 0) { exception_from_errno(errno); } break; } // level: IPPROTO_IP case IP_ADD_MEMBERSHIP: { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); if (bufinfo.len != sizeof(ip4_addr_t) * 2) { mp_raise_ValueError(NULL); } // // POSIX setsockopt has order: group addr, if addr, lwIP has it vice-versa // err_t err = igmp_joingroup((const ip4_addr_t*)bufinfo.buf + 1, bufinfo.buf); // if (err != ERR_OK) { // mp_raise_OSError(-err); // } break; } default: mp_printf(&mp_plat_print, "Warning: setsockopt() option not implemented\n"); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt); STATIC void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms) { // Rather than waiting for the entire timeout specified, we wait sock->retries times // for SOCKET_POLL_US each, checking for a MicroPython interrupt between timeouts. // with SOCKET_POLL_MS == 100ms, sock->retries allows for timeouts up to 13 years. // if timeout_ms == UINT64_MAX, wait forever. sock->retries = (timeout_ms == UINT64_MAX) ? UINT_MAX : timeout_ms * 1000 / SOCKET_POLL_US; struct timeval timeout = { .tv_sec = 0, .tv_usec = timeout_ms ? SOCKET_POLL_US : 0 }; setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&timeout, sizeof(timeout)); setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout)); fcntl(sock->fd, F_SETFL, timeout_ms ? 0 : O_NONBLOCK); } STATIC mp_obj_t socket_settimeout(const mp_obj_t arg0, const mp_obj_t arg1) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); if (arg1 == mp_const_none) _socket_settimeout(self, UINT64_MAX); else _socket_settimeout(self, mp_obj_get_float(arg1) * 1000L); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout); STATIC mp_obj_t socket_setblocking(const mp_obj_t arg0, const mp_obj_t arg1) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); if (mp_obj_is_true(arg1)) _socket_settimeout(self, UINT64_MAX); else _socket_settimeout(self, 0); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); // XXX this can end up waiting a very long time if the content is dribbled in one character // at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not // good behaviour. STATIC mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, struct sockaddr *from, socklen_t *from_len, int *errcode) { socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); // If the peer closed the connection then the lwIP socket API will only return "0" once // from lwip_recvfrom_r and then block on subsequent calls. To emulate POSIX behaviour, // which continues to return "0" for each call on a closed socket, we set a flag when // the peer closed the socket. if (sock->peer_closed) { return 0; } // XXX Would be nicer to use RTC to handle timeouts for (int i = 0; i <= sock->retries; ++i) { MP_THREAD_GIL_EXIT(); int r = recvfrom(sock->fd, buf, size, 0, from, from_len); MP_THREAD_GIL_ENTER(); if (r == 0) { sock->peer_closed = true; } if (r >= 0) { return r; } // if (errno != EWOULDBLOCK) { // *errcode = errno; // return MP_STREAM_ERROR; // } check_for_exceptions(); } *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; return MP_STREAM_ERROR; } mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, struct sockaddr *from, socklen_t *from_len) { size_t len = mp_obj_get_int(len_in); vstr_t vstr; vstr_init_len(&vstr, len); int errcode; mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); if (ret == MP_STREAM_ERROR) { exception_from_errno(errcode); } vstr.len = ret; return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { return _socket_recvfrom(self_in, len_in, NULL, NULL); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { struct sockaddr from; socklen_t fromlen = sizeof(from); mp_obj_t tuple[2]; tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&from)->sin_addr; mp_uint_t port = ntohs(((struct sockaddr_in*)&from)->sin_port); tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); return mp_obj_new_tuple(2, tuple); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); STATIC int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { int sentlen = 0; for (int i = 0; i <= sock->retries && sentlen < datalen; i++) { MP_THREAD_GIL_EXIT(); int r = send(sock->fd, data + sentlen, datalen - sentlen, 0); MP_THREAD_GIL_ENTER(); if (r < 0 && errno != EWOULDBLOCK) exception_from_errno(errno); if (r > 0) sentlen += r; check_for_exceptions(); } if (sentlen == 0) mp_raise_OSError(MP_ETIMEDOUT); return sentlen; } STATIC mp_obj_t socket_send(const mp_obj_t arg0, const mp_obj_t arg1) { socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); mp_uint_t datalen; const char *data = mp_obj_str_get_data(arg1, &datalen); int r = _socket_send(sock, data, datalen); return mp_obj_new_int(r); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send); STATIC mp_obj_t socket_sendall(const mp_obj_t arg0, const mp_obj_t arg1) { // XXX behaviour when nonblocking (see extmod/modlwip.c) // XXX also timeout behaviour. socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg1, &bufinfo, MP_BUFFER_READ); int r = _socket_send(sock, bufinfo.buf, bufinfo.len); if (r < bufinfo.len) mp_raise_OSError(MP_ETIMEDOUT); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_sendall_obj, socket_sendall); STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { socket_obj_t *self = MP_OBJ_TO_PTR(self_in); // get the buffer to send mp_buffer_info_t bufinfo; mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); // create the destination address struct sockaddr_in to; to.sin_len = sizeof(to); to.sin_family = AF_INET; to.sin_port = htons(netutils_parse_inet_addr(addr_in, (uint8_t*)&to.sin_addr, NETUTILS_BIG)); // send the data for (int i=0; i<=self->retries; i++) { MP_THREAD_GIL_EXIT(); int ret = sendto(self->fd, bufinfo.buf, bufinfo.len, 0, (struct sockaddr*)&to, sizeof(to)); MP_THREAD_GIL_ENTER(); if (ret > 0) return mp_obj_new_int_from_uint(ret); if (ret == -1 && errno != EWOULDBLOCK) { exception_from_errno(errno); } check_for_exceptions(); } mp_raise_OSError(MP_ETIMEDOUT); } STATIC MP_DEFINE_CONST_FUN_OBJ_3(socket_sendto_obj, socket_sendto); STATIC mp_obj_t socket_fileno(const mp_obj_t arg0) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); return mp_obj_new_int(self->fd); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_fileno_obj, socket_fileno); STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { (void)n_args; return args[0]; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); STATIC mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { return _socket_read_data(self_in, buf, size, NULL, NULL, errcode); } STATIC mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { socket_obj_t *sock = self_in; for (int i = 0; i <= sock->retries; i++) { MP_THREAD_GIL_EXIT(); int r = send(sock->fd, buf, size, 0); MP_THREAD_GIL_ENTER(); if (r > 0) return r; if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } check_for_exceptions(); } *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; return MP_STREAM_ERROR; } STATIC mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { socket_obj_t * socket = self_in; if (request == MP_STREAM_POLL) { fd_set rfds; FD_ZERO(&rfds); fd_set wfds; FD_ZERO(&wfds); fd_set efds; FD_ZERO(&efds); struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; if (arg & MP_STREAM_POLL_RD) FD_SET(socket->fd, &rfds); if (arg & MP_STREAM_POLL_WR) FD_SET(socket->fd, &wfds); if (arg & MP_STREAM_POLL_HUP) FD_SET(socket->fd, &efds); MP_THREAD_GIL_EXIT(); int r = select((socket->fd)+1, &rfds, &wfds, &efds, &timeout); MP_THREAD_GIL_ENTER(); if (r < 0) { *errcode = MP_EIO; return MP_STREAM_ERROR; } mp_uint_t ret = 0; if (FD_ISSET(socket->fd, &rfds)) ret |= MP_STREAM_POLL_RD; if (FD_ISSET(socket->fd, &wfds)) ret |= MP_STREAM_POLL_WR; if (FD_ISSET(socket->fd, &efds)) ret |= MP_STREAM_POLL_HUP; return ret; } else if (request == MP_STREAM_CLOSE) { if (socket->fd >= 0) { int ret = closesocket(socket->fd); if (ret != 0) { *errcode = errno; return MP_STREAM_ERROR; } socket->fd = -1; } return 0; } *errcode = MP_EINVAL; return MP_STREAM_ERROR; } STATIC const mp_rom_map_elem_t socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, { MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&socket_bind_obj) }, { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&socket_listen_obj) }, { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&socket_accept_obj) }, { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&socket_connect_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&socket_send_obj) }, { MP_ROM_QSTR(MP_QSTR_sendall), MP_ROM_PTR(&socket_sendall_obj) }, { MP_ROM_QSTR(MP_QSTR_sendto), MP_ROM_PTR(&socket_sendto_obj) }, { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&socket_recv_obj) }, { MP_ROM_QSTR(MP_QSTR_recvfrom), MP_ROM_PTR(&socket_recvfrom_obj) }, { MP_ROM_QSTR(MP_QSTR_setsockopt), MP_ROM_PTR(&socket_setsockopt_obj) }, { MP_ROM_QSTR(MP_QSTR_settimeout), MP_ROM_PTR(&socket_settimeout_obj) }, { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, { MP_ROM_QSTR(MP_QSTR_makefile), MP_ROM_PTR(&socket_makefile_obj) }, { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&socket_fileno_obj) }, { 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) }, }; STATIC MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); STATIC const mp_stream_p_t socket_stream_p = { .read = socket_stream_read, .write = socket_stream_write, .ioctl = socket_stream_ioctl }; STATIC const mp_obj_type_t socket_type = { { &mp_type_type }, .name = MP_QSTR_socket, .protocol = &socket_stream_p, .locals_dict = (mp_obj_t)&socket_locals_dict, }; STATIC mp_obj_t get_socket(size_t n_args, const mp_obj_t *args) { socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); sock->base.type = &socket_type; sock->domain = AF_INET; sock->type = SOCK_STREAM; sock->proto = 0; sock->peer_closed = false; if (n_args > 0) { sock->domain = mp_obj_get_int(args[0]); if (n_args > 1) { sock->type = mp_obj_get_int(args[1]); if (n_args > 2) { sock->proto = mp_obj_get_int(args[2]); } } } sock->fd = socket(sock->domain, sock->type, sock->proto); if (sock->fd < 0) { exception_from_errno(errno); } _socket_settimeout(sock, UINT64_MAX); return MP_OBJ_FROM_PTR(sock); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_socket_obj, 0, 3, get_socket); /******************************************************************************/ // usocket module // function usocket.getaddrinfo(host, port) STATIC mp_obj_t mod_usocket_getaddrinfo(uint n_args, const mp_obj_t *arg) { // TODO support additional args beyond the first two size_t hlen; int ret; const char *host = mp_obj_str_get_data(arg[0], &hlen); mp_int_t port = mp_obj_get_int(arg[1]); struct addrinfo hint, *res = NULL; memset(&hint, 0, sizeof(hint)); MP_THREAD_GIL_EXIT(); ret = getaddrinfo(host, NULL, &hint, &res); MP_THREAD_GIL_ENTER(); if (ret != 0) { mp_printf(&mp_plat_print, "getaddrinfo err: %d '%s'\n", ret, host); nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available netif")); } mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL); tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET); tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM); tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_); mp_obj_t tuple_addr[2] = { tuple_addr[0] = netutils_format_ipv4_addr((uint8_t *)((res->ai_addr->sa_data) + 2), NETUTILS_BIG), tuple_addr[1] = mp_obj_new_int(port), }; tuple->items[4] = mp_obj_new_tuple(2, tuple_addr); freeaddrinfo(res); return mp_obj_new_list(1, (mp_obj_t*) &tuple); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_usocket_getaddrinfo_obj, 2, 6, mod_usocket_getaddrinfo); STATIC const mp_rom_map_elem_t mp_module_socket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usocket) }, { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&get_socket_obj) }, { MP_ROM_QSTR(MP_QSTR_getaddrinfo), MP_ROM_PTR(&mod_usocket_getaddrinfo_obj) }, { MP_ROM_QSTR(MP_QSTR_AF_INET), MP_ROM_INT(AF_INET) }, { MP_ROM_QSTR(MP_QSTR_AF_INET6), MP_ROM_INT(AF_INET6) }, { MP_ROM_QSTR(MP_QSTR_SOCK_STREAM), MP_ROM_INT(SOCK_STREAM) }, { MP_ROM_QSTR(MP_QSTR_SOCK_DGRAM), MP_ROM_INT(SOCK_DGRAM) }, { MP_ROM_QSTR(MP_QSTR_SOCK_RAW), MP_ROM_INT(SOCK_RAW) }, { MP_ROM_QSTR(MP_QSTR_IPPROTO_TCP), MP_ROM_INT(IPPROTO_TCP) }, { MP_ROM_QSTR(MP_QSTR_IPPROTO_UDP), MP_ROM_INT(IPPROTO_UDP) }, { MP_ROM_QSTR(MP_QSTR_IPPROTO_IP), MP_ROM_INT(IPPROTO_IP) }, { MP_ROM_QSTR(MP_QSTR_SOL_SOCKET), MP_ROM_INT(SOL_SOCKET) }, { MP_ROM_QSTR(MP_QSTR_SO_REUSEADDR), MP_ROM_INT(SO_REUSEADDR) }, { MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); const mp_obj_module_t mp_module_usocket = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_socket_globals, }; #endif // MICROPY_PY_USOCKET ================================================ FILE: port/modules/modutils.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 SummerGift (SummerGift@qq.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "py/mpconfig.h" static const uint32_t crc32_table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; /** * Calculate the CRC32 value of a memory buffer. * * @param crc accumulated CRC32 value, must be 0 on first call * @param buf buffer to calculate CRC32 value for * @param len bytes in buffer * * @return calculated CRC32 value */ uint32_t mp_calc_crc32(uint32_t crc, const void *buf, size_t len) { const uint8_t *p; p = (const uint8_t *)buf; crc = crc ^ ~0U; while (len--) { crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8); } return crc ^ ~0U; } void mp_hex_to_str(char *pbDest, char *pbSrc, int nLen) { char ddl,ddh; int i; for (i=0; i 57) ddh = ddh + 7; if (ddl > 57) ddl = ddl + 7; pbDest[i*2] = ddh; pbDest[i*2+1] = ddl; } pbDest[nLen*2] = '\0'; } ================================================ FILE: port/modules/modutime.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "py/mpconfig.h" #if MICROPY_PY_UTIME #include #include #include "py/runtime.h" #include "py/smallint.h" #include "py/mphal.h" #include "extmod/utime_mphal.h" #include "lib/timeutils/timeutils.h" #include STATIC mp_obj_t mod_time_time(void) { #if MICROPY_PY_BUILTINS_FLOAT struct timeval tv; gettimeofday(&tv, NULL); mp_float_t val = tv.tv_sec + (mp_float_t)tv.tv_usec / 1000000; return mp_obj_new_float(val); #else return mp_obj_new_int((mp_int_t)time(NULL)); #endif } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_time_time_obj, mod_time_time); STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { time_t t; if (n_args == 0) { t = time(NULL); } else { #if MICROPY_PY_BUILTINS_FLOAT mp_float_t val = mp_obj_get_float(args[0]); t = (time_t)MICROPY_FLOAT_C_FUN(trunc)(val); #else t = mp_obj_get_int(args[0]); #endif } struct tm *tm = localtime(&t); mp_obj_t ret = mp_obj_new_tuple(9, NULL); mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(ret); tuple->items[0] = MP_OBJ_NEW_SMALL_INT(tm->tm_year + 1900); tuple->items[1] = MP_OBJ_NEW_SMALL_INT(tm->tm_mon + 1); tuple->items[2] = MP_OBJ_NEW_SMALL_INT(tm->tm_mday); tuple->items[3] = MP_OBJ_NEW_SMALL_INT(tm->tm_hour); tuple->items[4] = MP_OBJ_NEW_SMALL_INT(tm->tm_min); tuple->items[5] = MP_OBJ_NEW_SMALL_INT(tm->tm_sec); int wday = tm->tm_wday - 1; if (wday < 0) { wday = 6; } tuple->items[6] = MP_OBJ_NEW_SMALL_INT(wday); tuple->items[7] = MP_OBJ_NEW_SMALL_INT(tm->tm_yday + 1); tuple->items[8] = MP_OBJ_NEW_SMALL_INT(tm->tm_isdst); return ret; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_time_localtime_obj, 0, 1, mod_time_localtime); /// \function mktime() /// This is inverse function of localtime. It's argument is a full 8-tuple /// which expresses a time as per localtime. It returns an integer which is /// the number of seconds since Jan 1, 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) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "mktime needs a tuple of length 8 or 9 (%d given)", len)); } 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(time_mktime_obj, time_mktime); STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mod_time_time_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&mod_time_localtime_obj) }, { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&time_mktime_obj) }, }; 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, }; #endif // MICROPY_PY_UTIME ================================================ FILE: port/modules/user/moduserfunc.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 SummerGift * * 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" STATIC mp_obj_t add( mp_obj_t arg_1_obj, mp_obj_t arg_2_obj) { mp_int_t arg_1 = mp_obj_get_int(arg_1_obj); mp_int_t arg_2 = mp_obj_get_int(arg_2_obj); mp_int_t ret_val; /* Your code start! */ ret_val = arg_1 + arg_2; /* Your code end! */ return mp_obj_new_int(ret_val); } MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add); STATIC const mp_rom_map_elem_t mp_module_userfunc_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_userfunc) }, { MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&add_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_userfunc_globals, mp_module_userfunc_globals_table); const mp_obj_module_t mp_module_userfunc = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_userfunc_globals, }; ================================================ FILE: port/mpconfigport.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include // options to control how MicroPython is built // You can disable the built-in MicroPython compiler by setting the following // config option to 0. If you do this then you won't get a REPL prompt, but you // will still be able to execute pre-compiled scripts, compiled with mpy-cross. #define MICROPY_ENABLE_COMPILER (1) #if defined(PKG_MICROPYTHON_HEAP_SIZE) #define MICROPY_HEAP_SIZE PKG_MICROPYTHON_HEAP_SIZE #else #define MICROPY_HEAP_SIZE (8 * 1024) #endif #define MP_ENDIANNESS_LITTLE (1) #define MICROPY_STACK_CHECK (1) #define MICROPY_PY_MICROPYTHON_STACK_USE (1) #define MICROPY_QSTR_BYTES_IN_HASH (1) #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool #define MICROPY_ALLOC_PATH_MAX (256) #define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) #define MICROPY_EMIT_X64 (0) #define MICROPY_EMIT_THUMB (0) #define MICROPY_EMIT_INLINE_THUMB (0) #define MICROPY_COMP_MODULE_CONST (0) #define MICROPY_COMP_CONST (0) #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0) #define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0) #define MICROPY_MEM_STATS (0) #define MICROPY_DEBUG_PRINTERS (0) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_FINALISER (1) #define MICROPY_GC_ALLOC_THRESHOLD (0) #define MICROPY_REPL_EVENT_DRIVEN (0) #define MICROPY_REPL_AUTO_INDENT (1) #define MICROPY_KBD_EXCEPTION (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_HELPER_LEXER_UNIX (0) #define MICROPY_ENABLE_SOURCE_LINE (1) #define MICROPY_ENABLE_DOC_STRING (0) #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) #define MICROPY_PY_ASYNC_AWAIT (0) // control over Python builtins #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_BUILTINS_STR_UNICODE (1) #define MICROPY_PY_BUILTINS_STR_CENTER (1) #define MICROPY_PY_BUILTINS_STR_PARTITION (1) #define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) #define MICROPY_PY_BUILTINS_BYTEARRAY (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW (1) #define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (1) #define MICROPY_PY_BUILTINS_INPUT (1) #define MICROPY_PY_BUILTINS_POW3 (1) #define MICROPY_PY_BUILTINS_ENUMERATE (1) #define MICROPY_PY_BUILTINS_FILTER (1) #define MICROPY_PY_BUILTINS_FROZENSET (1) #define MICROPY_PY_BUILTINS_REVERSED (1) #define MICROPY_PY_BUILTINS_SET (1) #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_TEXT rtthread_help_text #define MICROPY_PY_BUILTINS_HELP_MODULES (1) #define MICROPY_PY_BUILTINS_SLICE (1) #define MICROPY_PY_BUILTINS_PROPERTY (1) #define MICROPY_PY_BUILTINS_MIN_MAX (1) #define MICROPY_PY___FILE__ (1) #define MICROPY_PY_GC (1) #define MICROPY_PY_ARRAY (1) #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_PY_ATTRTUPLE (1) #define MICROPY_PY_COLLECTIONS (1) #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) #define MICROPY_PY_MATH (1) #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_STREAMS_NON_BLOCK (1) #define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_CAN_OVERRIDE_BUILTINS (1) #define MICROPY_USE_INTERNAL_ERRNO (1) #define MICROPY_USE_INTERNAL_PRINTF (0) #define MICROPY_PY_STRUCT (1) #define MICROPY_PY_SYS (1) #define MICROPY_MODULE_FROZEN_MPY (1) #define MICROPY_MODULE_FROZEN_STR (0) #define MICROPY_CPYTHON_COMPAT (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #ifdef MICROPYTHON_USING_FLOAT_IMPL_FLOAT #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #else #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) #endif #define MICROPY_READER_VFS (0) #define MICROPY_PY_PIN (1) #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_VFS (0) #define MICROPY_VFS_FAT (0) #define MICROPY_PY_UTIME (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_UTIMEQ (1) #define MICROPY_PY_RTTHREAD (1) /*****************************************************************************/ /* Hardware Module */ #ifdef MICROPYTHON_USING_MACHINE_I2C #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new #endif #ifdef MICROPYTHON_USING_MACHINE_SPI #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hard_spi_make_new #endif #ifdef MICROPYTHON_USING_MACHINE_UART #define MICROPY_PY_MACHINE_UART (1) #endif #ifdef MICROPYTHON_USING_MACHINE_ADC #define MICROPY_PY_MACHINE_ADC (1) #endif #ifdef MICROPYTHON_USING_MACHINE_PWM #define MICROPY_PY_MACHINE_PWM (1) #endif #ifdef MICROPYTHON_USING_MACHINE_LCD #define MICROPY_PY_MACHINE_LCD (1) #endif #ifdef MICROPYTHON_USING_MACHINE_RTC #define MICROPY_PY_MACHINE_RTC (1) #endif #ifdef MICROPYTHON_USING_MACHINE_WDT #define MICROPY_PY_MACHINE_WDT (1) #endif #ifdef MICROPYTHON_USING_MACHINE_TIMER #define MICROPY_PY_MACHINE_TIMER (1) #endif /*****************************************************************************/ /* System Module */ #ifdef MICROPYTHON_USING_UOS #define MICROPY_PY_IO (1) #define MICROPY_PY_IO_FILEIO (1) #define MICROPY_PY_MODUOS (1) #define MICROPY_PY_MODUOS_FILE (1) #define MICROPY_PY_SYS_STDFILES (1) #define MICROPY_READER_POSIX (1) #define MICROPY_PY_BUILTINS_COMPILE (1) #define MICROPY_PY_BUILTINS_EXECFILE (1) #define MICROPY_PERSISTENT_CODE_LOAD (1) #else #define MICROPY_PY_IO (0) #define MICROPY_PY_MODUOS (0) #endif /* MICROPYTHON_USING_UOS */ #ifdef MICROPYTHON_USING_THREAD #define MICROPY_PY_THREAD (1) #define MICROPY_SCHEDULER_DEPTH (8) #endif /* MICROPYTHON_USING_THREAD */ #ifdef MICROPYTHON_USING_USELECT #define MICROPY_PY_USELECT (1) #endif #ifdef MICROPYTHON_USING_UCTYPES #define MICROPY_PY_UCTYPES (1) #endif #ifdef MICROPYTHON_USING_UERRNO #define MICROPY_PY_UERRNO (1) #endif #ifdef MICROPYTHON_USING_FFI #define MICROPY_PY_FFI (1) #endif /*****************************************************************************/ /* Tools Module */ #ifdef MICROPYTHON_USING_CMATH #define MICROPY_PY_CMATH (1) #endif #ifdef MICROPYTHON_USING_UBINASCII #define MICROPY_PY_UBINASCII (1) #endif #ifdef MICROPYTHON_USING_UHASHLIB #define MICROPY_PY_UHASHLIB (1) #endif #ifdef MICROPYTHON_USING_UHEAPQ #define MICROPY_PY_UHEAPQ (1) #endif #ifdef MICROPYTHON_USING_UJSON #define MICROPY_PY_UJSON (1) #endif #ifdef MICROPYTHON_USING_URE #define MICROPY_PY_URE (1) #endif #ifdef MICROPYTHON_USING_UZLIB #define MICROPY_PY_UZLIB (1) #endif #ifdef MICROPYTHON_USING_URANDOM #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #endif /*****************************************************************************/ /* Network Module */ #ifdef MICROPYTHON_USING_USOCKET #define MICROPY_PY_USOCKET (1) #endif #ifdef MICROPYTHON_USING_USSL #define MICROPY_PY_USSL (1) #define MICROPY_SSL_MBEDTLS (1) #endif #ifdef MICROPYTHON_USING_NETWORK #define MICROPY_PY_NETWORK (1) #endif #ifdef MICROPYTHON_USING_WLAN #define MICROPY_PY_WLAN (1) #endif #if MICROPY_PY_THREAD #define MICROPY_EVENT_POLL_HOOK \ do { \ extern void mp_handle_pending(bool); \ mp_handle_pending(true); \ MP_THREAD_GIL_EXIT(); \ MP_THREAD_GIL_ENTER(); \ } while (0); #else #define MICROPY_EVENT_POLL_HOOK \ do { \ extern void mp_handle_pending(bool); \ mp_handle_pending(true); \ rt_thread_delay(1); \ } while (0); #endif /*****************************************************************************/ /* Modules define in your project You must provide 'moddefs.user.extmods.h' and 'qstrdefs.user.extmods.h' */ #ifdef MICROPYTHON_USING_USEREXTMODS #define MICROPY_USER_EXTMODS (1) #include #else #define MICROPY_USER_EXTMODS (0) #endif #ifndef MICROPY_USER_MODULES #define MICROPY_USER_MODULES #endif #if defined(__CC_ARM) #include #define MICROPY_NO_ALLOCA 1 #if (RTTHREAD_VERSION < RT_VERSION_CHECK(5, 0, 0)) #define MP_WEAK RT_WEAK #else #define MP_WEAK rt_weak #endif #define MP_NOINLINE #define MP_ALWAYSINLINE #define MP_LIKELY(x) x #define MP_UNLIKELY(x) x #undef __arm__ #undef __thumb__ #undef __thumb2__ #elif defined(__ICCARM__) #include #include #define MICROPY_NO_ALLOCA 1 #define NORETURN __noreturn #if (RTTHREAD_VERSION < RT_VERSION_CHECK(5, 0, 0)) #define MP_WEAK RT_WEAK #else #define MP_WEAK rt_weak #endif #define MP_NOINLINE #define MP_ALWAYSINLINE #define MP_LIKELY(x) x #define MP_UNLIKELY(x) x #elif defined(__GNUC__) // We need to provide a declaration/definition of alloca() #include #else #error "not supported compiler" #endif /* defined(__CC_ARM) */ // type definitions for the specific machine #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 1)) // This port is intended to be 32-bit, but unfortunately, int32_t for // different targets may be defined in different ways - either as int // or as long. This requires different printf formatting specifiers // to print such value. So, we avoid int32_t and use int directly. #define UINT_FMT "%u" #define INT_FMT "%d" typedef intptr_t mp_int_t; // must be pointer size typedef uintptr_t mp_uint_t; // must be pointer size typedef long mp_off_t; #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_stream(str, len) #define MICROPY_PY_SYS_PLATFORM "rt-thread" #define MICROPY_HW_BOARD_NAME "Universal python platform" #define MICROPY_HW_MCU_NAME "RT-Thread" #define MICROPY_PY_PATH_FIRST "/libs/mpy/" #define MICROPY_PY_PATH_SECOND "/scripts/" #define MICROPY_MAIN_PY_PATH "/main.py" #define MICROPY_BEGIN_ATOMIC_SECTION() rt_hw_interrupt_disable() #define MICROPY_END_ATOMIC_SECTION(state) rt_hw_interrupt_enable(state) #ifdef __linux__ #define MICROPY_MIN_USE_STDOUT (1) #endif #define MP_STATE_PORT MP_STATE_VM #define MICROPY_PORT_ROOT_POINTERS const char *readline_hist[8]; extern const struct _mp_obj_module_t pyb_module; extern const struct _mp_obj_module_t mp_module_rtthread; extern const struct _mp_obj_module_t mp_module_time; extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_uselect; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_io; extern const struct _mp_obj_fun_builtin_fixed_t machine_soft_reset_obj; extern const struct _mp_obj_module_t mp_module_ffi; extern const struct _mp_obj_module_t mp_module_network; extern const struct _mp_obj_module_t mp_module_userfunc; #if MICROPY_PY_RTTHREAD #define RTTHREAD_PORT_BUILTIN_MODULES { MP_ROM_QSTR(MP_QSTR_rtthread), MP_ROM_PTR(&mp_module_rtthread) }, #else #define RTTHREAD_PORT_BUILTIN_MODULES #endif /* MICROPY_PY_RTTHREAD */ #if MICROPY_PY_MODUOS #define MODUOS_PORT_BUILTINS { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, #define MODUOS_PORT_BUILTIN_MODULES { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, #define MODUOS_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_os), MP_ROM_PTR(&mp_module_uos) }, #define mp_import_stat(x) mp_posix_import_stat(x) #else #define MODUOS_PORT_BUILTINS #define MODUOS_PORT_BUILTIN_MODULES #define MODUOS_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_MODUOS */ #if MICROPY_PY_USOCKET #define SOCKET_PORT_BUILTIN_MODULES { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_usocket) }, #define SOCKET_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&mp_module_usocket) }, #else #define SOCKET_PORT_BUILTIN_MODULES #define SOCKET_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_USOCKET */ #ifdef MICROPYTHON_USING_UBINASCII #define MODUBINASCII_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_binascii), MP_ROM_PTR(&mp_module_ubinascii) }, #else #define MODUBINASCII_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UBINASCII */ #ifdef MICROPY_PY_COLLECTIONS #define MODUCOLLECTIONS_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_collections), MP_ROM_PTR(&mp_module_collections ) }, #else #define MODUCOLLECTIONS_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_COLLECTIONS */ #ifdef MICROPYTHON_USING_UERRNO #define MODUERRNO_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_module_uerrno ) }, #else #define MODUERRNO_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UERRNO */ #ifdef MICROPYTHON_USING_UHASHLIB #define MODUHASHLIB_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_hashlib), MP_ROM_PTR(&mp_module_uhashlib ) }, #else #define MODUHASHLIB_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UHASHLIB */ #ifdef MICROPYTHON_USING_UHEAPQ #define MODUHEAPQ_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_heapq), MP_ROM_PTR(&mp_module_uheapq ) }, #else #define MODUHEAPQ_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UHEAPQ */ #if MICROPY_PY_IO #define MODUIO_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_io), MP_ROM_PTR(&mp_module_io ) }, #else #define MODUIO_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UIO */ #ifdef MICROPYTHON_USING_UJSON #define MODUJSON_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_json), MP_ROM_PTR(&mp_module_ujson ) }, #else #define MODUJSON_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UJSON */ #ifdef MICROPYTHON_USING_URANDOM #define MODURANDOM_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mp_module_urandom ) }, #else #define MODURANDOM_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_URANDOM */ #ifdef MICROPYTHON_USING_URE #define MODURE_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_re), MP_ROM_PTR(&mp_module_ure ) }, #else #define MODURE_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_URE */ #ifdef MICROPYTHON_USING_USELECT #define MODUSELECT_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_module_uselect ) }, #else #define MODUSELECT_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_USELECT */ #if MICROPY_PY_USSL #define MODUSSL_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_ssl), MP_ROM_PTR(&mp_module_ussl ) }, #else #define MODUSSL_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_USSL */ #if MICROPY_PY_STRUCT #define MODUSTRUCT_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&mp_module_ustruct ) }, #else #define MODUSTRUCT_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_USTRUCT */ #ifdef MICROPY_PY_UTIME #define MODUTIME_PORT_BUILTIN_MODULES { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_time) }, #define MODUTIME_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_time ) }, #else #define SOCKET_PORT_BUILTIN_MODULES #define MODUTIME_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UTIME */ #ifdef MICROPYTHON_USING_UZLIB #define MODUZLIB_PORT_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_zlib), MP_ROM_PTR(&mp_module_uzlib ) }, #else #define MODUZLIB_PORT_BUILTIN_MODULE_WEAK_LINKS #endif /* MICROPY_PY_UZLIB */ #if MICROPY_PY_FFI #define MODFFI_PORT_BUILTIN_MODULES { MP_ROM_QSTR(MP_QSTR_ffi), MP_ROM_PTR(&mp_module_ffi) }, #else #define MODFFI_PORT_BUILTIN_MODULES #endif #if MICROPY_PY_NETWORK #define MODNETWORK_PORT_BUILTIN_MODULES { MP_ROM_QSTR(MP_QSTR_network), MP_ROM_PTR(&mp_module_network) }, #else #define MODNETWORK_PORT_BUILTIN_MODULES #endif #define USERFUNC_PORT_BUILTIN_MODULES { MP_ROM_QSTR(MP_QSTR_userfunc), MP_ROM_PTR(&mp_module_userfunc) }, // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&machine_soft_reset_obj) }, \ { MP_ROM_QSTR(MP_QSTR_quit), MP_ROM_PTR(&machine_soft_reset_obj) }, \ MODUOS_PORT_BUILTINS \ #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ { MP_ROM_QSTR(MP_QSTR_pyb), MP_ROM_PTR(&pyb_module) }, \ RTTHREAD_PORT_BUILTIN_MODULES \ MODUOS_PORT_BUILTIN_MODULES \ SOCKET_PORT_BUILTIN_MODULES \ MODUTIME_PORT_BUILTIN_MODULES \ MODFFI_PORT_BUILTIN_MODULES \ MODNETWORK_PORT_BUILTIN_MODULES \ USERFUNC_PORT_BUILTIN_MODULES \ MICROPY_USER_MODULES \ #define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUTIME_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUOS_PORT_BUILTIN_MODULE_WEAK_LINKS \ SOCKET_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUBINASCII_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUCOLLECTIONS_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUERRNO_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUHASHLIB_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUHEAPQ_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUIO_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUJSON_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODURANDOM_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODURE_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUSELECT_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUSSL_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUSTRUCT_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUTIME_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUZLIB_PORT_BUILTIN_MODULE_WEAK_LINKS \ MODUSTRUCT_PORT_BUILTIN_MODULE_WEAK_LINKS \ #define MP_RTT_NOT_IMPL_PRINT mp_printf(&mp_plat_print, "Not implement on %s:%ld, Please add for your board!\n", __FILE__, __LINE__) ================================================ FILE: port/mpgetcharport.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include "py/runtime.h" #include "lib/utils/interrupt_char.h" #include "mpgetcharport.h" #define UART_FIFO_SIZE 256 static struct rt_ringbuffer *rx_fifo = NULL; static rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size) = NULL; static rt_err_t getchar_rx_ind(rt_device_t dev, rt_size_t size) { uint8_t ch; rt_size_t i; rt_base_t int_lvl; for (i = 0; i < size; i++) { /* read a char */ if (rt_device_read(dev, 0, &ch, 1)) { if (ch == mp_interrupt_char) { mp_keyboard_interrupt(); } else { int_lvl = rt_hw_interrupt_disable(); rt_ringbuffer_put_force(rx_fifo, &ch, 1); rt_hw_interrupt_enable(int_lvl); } } } return RT_EOK; } void mp_getchar_init(void) { rt_base_t int_lvl; rt_device_t console; /* create RX FIFO */ rx_fifo = rt_ringbuffer_create(UART_FIFO_SIZE); /* created must success */ RT_ASSERT(rx_fifo); int_lvl = rt_hw_interrupt_disable(); console = rt_console_get_device(); if (console) { /* backup RX indicate */ odev_rx_ind = console->rx_indicate; rt_device_set_rx_indicate(console, getchar_rx_ind); } rt_hw_interrupt_enable(int_lvl); } void mp_getchar_deinit(void) { rt_base_t int_lvl; rt_device_t console; rt_ringbuffer_destroy(rx_fifo); int_lvl = rt_hw_interrupt_disable(); console = rt_console_get_device(); if (console && odev_rx_ind) { /* restore RX indicate */ rt_device_set_rx_indicate(console, odev_rx_ind); } rt_hw_interrupt_enable(int_lvl); } int mp_getchar(void) { uint8_t ch; rt_base_t int_lvl; int_lvl = rt_hw_interrupt_disable(); if (!rt_ringbuffer_getchar(rx_fifo, &ch)) { ch = 0xFF; } rt_hw_interrupt_enable(int_lvl); return ch; } ================================================ FILE: port/mpgetcharport.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef _MPGETCHARPORT_H_ #define _MPGETCHARPORT_H_ void mp_getchar_init(void); void mp_getchar_deinit(void); int mp_getchar(void); #endif /* _MPGETCHARPORT_H_ */ ================================================ FILE: port/mphalport.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include "mphalport.h" #include "mpgetcharport.h" #include "mpputsnport.h" const char rtthread_help_text[] = "Welcome to MicroPython on RT-Thread!\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, do a soft reset of the board\n" " CTRL-E -- on a blank line, enter paste mode\n" "\n" "For further help on a specific object, type help(obj)\n" ; int mp_hal_stdin_rx_chr(void) { char ch; while (1) { ch = mp_getchar(); if (ch != (char)0xFF) { break; } MICROPY_EVENT_POLL_HOOK; rt_thread_delay(1); } return ch; } // Send string of given length void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { mp_putsn(str, len); #ifdef PKG_USING_OPENMV_CP extern void serial_dbg_send_strn(const char *str, int len); serial_dbg_send_strn(str, len); #endif } void mp_hal_stdout_tx_strn_stream(const char *str, size_t len) { mp_putsn_stream(str, len); #ifdef PKG_USING_OPENMV_CP extern void serial_dbg_send_strn_cooked(const char *str, int len); serial_dbg_send_strn_cooked(str, len); #endif } mp_uint_t mp_hal_ticks_us(void) { return rt_tick_get() * 1000000UL / RT_TICK_PER_SECOND; } mp_uint_t mp_hal_ticks_ms(void) { return rt_tick_get() * 1000 / RT_TICK_PER_SECOND; } mp_uint_t mp_hal_ticks_cpu(void) { return rt_tick_get(); } void mp_hal_delay_us(mp_uint_t us) { rt_tick_t t0 = rt_tick_get(), t1, dt; uint64_t dtick = us * RT_TICK_PER_SECOND / 1000000L; while (1) { t1 = rt_tick_get(); dt = t1 - t0; if (dt >= dtick) { break; } mp_handle_pending(true); } } void mp_hal_delay_ms(mp_uint_t ms) { rt_tick_t t0 = rt_tick_get(), t1, dt; uint64_t dtick = ms * RT_TICK_PER_SECOND / 1000L; while (1) { t1 = rt_tick_get(); dt = t1 - t0; if (dt >= dtick) { break; } MICROPY_EVENT_POLL_HOOK; rt_thread_delay(1); } } ================================================ FILE: port/mphalport.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #define MP_HAL_PIN_FMT "%s" extern void mp_hal_set_interrupt_char (int c); extern void mp_pin_od_write(void *machine_pin, int stat); extern void mp_hal_pin_open_set(void *machine_pin, int mode); extern char* mp_hal_pin_get_name(void *machine_pin); extern void mp_hal_stdout_tx_strn_stream(const char *str, size_t len); #define mp_hal_quiet_timing_enter() MICROPY_BEGIN_ATOMIC_SECTION() #define mp_hal_quiet_timing_exit(irq_state) MICROPY_END_ATOMIC_SECTION(irq_state) // needed for machine.I2C #define mp_hal_delay_us_fast(us) mp_hal_delay_us(us) #define mp_hal_pin_od_low(pin) mp_pin_od_write(pin, PIN_LOW) #define mp_hal_pin_od_high(pin) mp_pin_od_write(pin, PIN_HIGH) #define mp_hal_pin_open_drain(p) mp_hal_pin_open_set(p, PIN_MODE_OUTPUT_OD) // needed for soft machine.SPI #define mp_hal_pin_output(p) mp_hal_pin_open_set(p, PIN_MODE_OUTPUT) #define mp_hal_pin_input(p) mp_hal_pin_open_set(p, PIN_MODE_INPUT) #define mp_hal_pin_name(p) mp_hal_pin_get_name(p) #define mp_hal_pin_high(p) mp_hal_pin_write(p, 1) ================================================ FILE: port/mpputsnport.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include static rt_device_t console_dev = NULL; static struct rt_device dummy_console = { 0 }; static rt_uint16_t console_open_flag; void mp_putsn(const char *str, size_t len) { if (console_dev) { rt_device_write(console_dev, 0, str, len); } } void mp_putsn_stream(const char *str, size_t len) { if (console_dev) { rt_uint16_t old_flag = console_dev->open_flag; console_dev->open_flag |= RT_DEVICE_FLAG_STREAM; rt_device_write(console_dev, 0, str, len); console_dev->open_flag = old_flag; } } void mp_putsn_init(void) { {/* register dummy console device */ #ifdef RT_USING_DEVICE_OPS static struct rt_device_ops _ops = {0}; dummy_console.ops = &_ops; #endif dummy_console.type = RT_Device_Class_Char; rt_device_register(&dummy_console, "dummy", RT_DEVICE_FLAG_RDWR); } /* backup the console device */ console_dev = rt_console_get_device(); console_open_flag = console_dev->open_flag; console_dev->open_flag = 0; /* set the new console device to dummy console */ rt_console_set_device(dummy_console.parent.name); /* reopen the old console device for mp_putsn */ rt_device_open(console_dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX); } void mp_putsn_deinit(void) { /* close the old console, it's already in mp_putsn_init */ rt_device_close(console_dev); /* restore the old console device */ rt_console_set_device(console_dev->parent.name); console_dev->open_flag = console_open_flag; rt_device_unregister(&dummy_console); } ================================================ FILE: port/mpputsnport.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef _MPPUTCHARPORT_H_ #define _MPPUTCHARPORT_H_ void mp_putsn_init(void); void mp_putsn_deinit(void); void mp_putsn(const char *str, size_t len); void mp_putsn_stream(const char *str, size_t len); #endif /* _MPPUTCHARPORT_H_ */ ================================================ FILE: port/mpthreadport.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "stdio.h" #include "py/mpconfig.h" #include "py/mpstate.h" #include "py/gc.h" #include "py/mpthread.h" #include "mpthreadport.h" #include #if MICROPY_PY_THREAD #define MP_THREAD_MIN_STACK_SIZE (4 * 1024) #define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024) #define MP_THREAD_STATUS_READY 0 #define MP_THREAD_STATUS_RUNNING 1 #define MP_THREAD_STATUS_FINISH 2 typedef struct _thread_t { rt_thread_t id; // system id of thread int status; // whether the thread is ready, running and finish void *arg; // thread Python args, a GC root pointer void *stack; // pointer to the stack size_t stack_len; // number of words in the stack struct _thread_t *next; } thread_t; // the mutex controls access to the linked list STATIC mp_thread_mutex_t thread_mutex; STATIC thread_t thread_root_node; STATIC thread_t *thread_root; // root pointer, handled by mp_thread_gc_others /** * thread port initialization * * @param stack MicroPython main thread stack start address * @param stack_len number of words in the stack */ void mp_thread_init(void *stack, uint32_t stack_len) { mp_thread_set_state(&mp_state_ctx.thread); thread_root = &thread_root_node; thread_root->id = rt_thread_self(); thread_root->status = MP_THREAD_STATUS_RUNNING; thread_root->arg = NULL; thread_root->stack = stack; thread_root->stack_len = stack_len; thread_root->next = NULL; mp_thread_mutex_init(&thread_mutex); } void mp_thread_gc_others(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (thread_t *th = thread_root; th != NULL; th = th->next) { // the root node not using the mpy heap if (th != &thread_root_node) { gc_collect_root((void**)&th, 1); gc_collect_root(&th->arg, 1); // probably not needed } if (th->status == MP_THREAD_STATUS_READY) { continue; } gc_collect_root((void**) &th->id, 1); // probably not needed gc_collect_root(th->stack, th->stack_len); // probably not needed } mp_thread_mutex_unlock(&thread_mutex); } mp_state_thread_t *mp_thread_get_state(void) { return (mp_state_thread_t *)(rt_thread_self()->user_data); } void mp_thread_set_state(mp_state_thread_t *state) { rt_thread_self()->user_data = (rt_uint32_t)state; } void mp_thread_start(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (thread_t *th = thread_root; th != NULL; th = th->next) { if (th->id == rt_thread_self()) { th->status = MP_THREAD_STATUS_RUNNING; break; } } mp_thread_mutex_unlock(&thread_mutex); } void mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack_size, int priority, char *name) { static uint8_t count = 0; if (*stack_size == 0) { *stack_size = MP_THREAD_DEFAULT_STACK_SIZE; // default stack size } else if (*stack_size < MP_THREAD_MIN_STACK_SIZE) { *stack_size = MP_THREAD_MIN_STACK_SIZE; // minimum stack size } // allocate the linked-list node, TCB and stack (must be outside thread_mutex lock) thread_t *th = m_new_obj(thread_t); if (th == NULL) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread obj")); } else { th->id = m_new_obj(struct rt_thread); if (th->id == NULL) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread id")); } th->stack = m_new(uint8_t, *stack_size); if (th->stack == NULL) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread stack")); } } mp_thread_mutex_lock(&thread_mutex, 1); // adjust the stack_size to provide room to recover from hitting the limit *stack_size -= 1024; // add thread to linked list of all threads th->status = MP_THREAD_STATUS_READY; th->arg = arg; th->stack_len = *stack_size / 4; th->next = thread_root; thread_root = th; rt_thread_init(th->id, name, (void (*)(void *))entry, arg, th->stack, *stack_size, priority, count++); rt_thread_startup(th->id); mp_thread_mutex_unlock(&thread_mutex); } void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) { static uint8_t count = 0; int priority = rt_thread_self()->current_priority; char name[RT_NAME_MAX]; if (priority > 0) { priority --; } /* build name */ rt_snprintf(name, sizeof(name), "mp%02d", count++); mp_thread_create_ex(entry, arg, stack_size, priority, name); } void mp_thread_finish(void) { thread_t *prev = NULL; mp_thread_mutex_lock(&thread_mutex, 1); for (thread_t *th = thread_root; th != NULL;prev = th, th = th->next) { // unlink the node from the list if (th->id == rt_thread_self()) { if (prev != NULL) { prev->next = th->next; } else { // move the start pointer thread_root = th->next; } th->status = MP_THREAD_STATUS_FINISH; // explicitly release all its memory m_del_obj(struct rt_thread, th->id); m_del(uint8_t, th->stack, th->stack_len); m_del_obj(thread_t, th); break; } } mp_thread_mutex_unlock(&thread_mutex); } void mp_thread_mutex_init(mp_thread_mutex_t *mutex) { static uint8_t count = 0; char name[RT_NAME_MAX]; if (!mutex->is_init) { /* build name */ rt_snprintf(name, sizeof(name), "mp%02d", count++); rt_mutex_init(&(mutex->mutex), name, RT_IPC_FLAG_FIFO); mutex->is_init = 1; } } int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { return (RT_EOK == rt_mutex_take(&(mutex->mutex), wait ? RT_WAITING_FOREVER : 0)); } void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { rt_mutex_release(&(mutex->mutex)); } void mp_thread_deinit(void) { // detach all ready and running mpy thread for (thread_t *th = thread_root; th != NULL; th = th->next) { if (th != &thread_root_node && th->status != MP_THREAD_STATUS_FINISH) { rt_thread_detach(th->id); } } // allow RT-Thread to clean-up the threads rt_thread_delay(200); } #endif /* MICROPY_PY_THREAD */ ================================================ FILE: port/mpthreadport.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2018 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef _MPTHREADPORT_H #define _MPTHREADPORT_H #include "rtthread.h" typedef struct _mp_thread_mutex_t { struct rt_mutex mutex; int is_init; } mp_thread_mutex_t; void mp_thread_init(void *stack, uint32_t stack_len); void mp_thread_gc_others(void); void mp_thread_deinit(void); #endif // _MPTHREADPORT_H ================================================ FILE: port/mpy_main.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #ifdef RT_USING_DFS #include #include #include #endif #include #include #include #include #include #include #include #include #include #include "mpgetcharport.h" #include "mpputsnport.h" #define THREAD_STACK_NO_SYNC 4096 #define THREAD_STACK_WITH_SYNC 8192 #if MICROPY_ENABLE_COMPILER void do_str(const char *src, mp_parse_input_kind_t input_kind) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); mp_call_function_0(module_fun); nlr_pop(); } else { // uncaught exception mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); } } #endif #ifdef RT_USING_DFS static int mp_sys_resource_bak(struct dfs_fdtable **table_bak) { struct dfs_fdtable *fd_table; struct dfs_fdtable *fd_table_bak; struct dfs_fd **fds; fd_table = dfs_fdtable_get(); if (!fd_table) { return RT_FALSE; } fd_table_bak = (struct dfs_fdtable *)rt_malloc(sizeof(struct dfs_fdtable)); if (!fd_table_bak) { goto _exit_tab; } fds = (struct dfs_fd **)rt_malloc((int)fd_table->maxfd * sizeof(struct dfs_fd *)); if (!fds) { goto _exit_fds; } else { rt_memcpy(fds, fd_table->fds, (int)fd_table->maxfd * sizeof(struct dfs_fd *)); fd_table_bak->maxfd = (int)fd_table->maxfd; fd_table_bak->fds = fds; } *table_bak = fd_table_bak; return RT_TRUE; _exit_fds: rt_free(fd_table_bak); _exit_tab: return RT_FALSE; } static void mp_sys_resource_gc(struct dfs_fdtable *fd_table_bak) { struct dfs_fdtable *fd_table; if (!fd_table_bak) return; fd_table = dfs_fdtable_get(); for(int i = 0; i < fd_table->maxfd; i++) { if (fd_table->fds[i] != RT_NULL) { if ((i < fd_table_bak->maxfd && fd_table_bak->fds[i] == RT_NULL) || (i >= fd_table_bak->maxfd)) { close(i + DFS_FD_OFFSET); } } } rt_free(fd_table_bak->fds); rt_free(fd_table_bak); } #endif static void *stack_top = RT_NULL; static char *heap = RT_NULL; void mpy_main(const char *filename) { int stack_dummy; int stack_size_check; stack_top = (void *)&stack_dummy; #ifdef RT_USING_DFS struct dfs_fdtable *fd_table_bak = NULL; mp_sys_resource_bak(&fd_table_bak); #endif mp_getchar_init(); mp_putsn_init(); #if defined(MICROPYTHON_USING_FILE_SYNC_VIA_IDE) stack_size_check = THREAD_STACK_WITH_SYNC; #else stack_size_check = THREAD_STACK_NO_SYNC; #endif if (rt_thread_self()->stack_size < stack_size_check) { #if (RTTHREAD_VERSION < RT_VERSION_CHECK(5, 0, 1)) mp_printf(&mp_plat_print, "The stack (%.*s) size for executing MicroPython must be >= %d\n", RT_NAME_MAX, rt_thread_self()->name, stack_size_check); #else mp_printf(&mp_plat_print, "The stack (%.*s) size for executing MicroPython must be >= %d\n", RT_NAME_MAX, rt_thread_self()->parent.name, stack_size_check); #endif } #if MICROPY_PY_THREAD mp_thread_init(rt_thread_self()->stack_addr, ((rt_uint32_t)stack_top - (rt_uint32_t)rt_thread_self()->stack_addr) / 4); #endif mp_stack_set_top(stack_top); // Make MicroPython's stack limit somewhat smaller than full stack available mp_stack_set_limit(rt_thread_self()->stack_size - 1024); #if MICROPY_ENABLE_GC heap = rt_malloc(MICROPY_HEAP_SIZE); if (!heap) { mp_printf(&mp_plat_print, "No memory for MicroPython Heap!\n"); return; } gc_init(heap, heap + MICROPY_HEAP_SIZE); #endif /* MicroPython initialization */ mp_init(); /* system path initialization */ mp_obj_list_init(mp_sys_path, 0); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script) mp_obj_list_append(mp_sys_path, mp_obj_new_str(MICROPY_PY_PATH_FIRST, strlen(MICROPY_PY_PATH_FIRST))); mp_obj_list_append(mp_sys_path, mp_obj_new_str(MICROPY_PY_PATH_SECOND, strlen(MICROPY_PY_PATH_SECOND))); mp_obj_list_init(mp_sys_argv, 0); readline_init0(); if (filename) { #ifndef MICROPYTHON_USING_UOS mp_printf(&mp_plat_print, "Please enable uos module in sys module option first.\n"); #else pyexec_file(filename); #endif } else { #ifdef MICROPYTHON_USING_UOS // run boot-up scripts void *frozen_data; const char *_boot_file = "_boot.py", *boot_file = "boot.py", *main_file = MICROPY_MAIN_PY_PATH; if (mp_find_frozen_module(_boot_file, strlen(_boot_file), &frozen_data) != MP_FROZEN_NONE) { pyexec_frozen_module(_boot_file); } if (!access(boot_file, 0)) { pyexec_file(boot_file); } // run main scripts if (!access(main_file, 0)) { if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { pyexec_file(main_file); } } #endif /* MICROPYTHON_USING_UOS */ mp_printf(&mp_plat_print, "\n"); for (;;) { if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { if (pyexec_raw_repl() != 0) { break; } } else { if (pyexec_friendly_repl() != 0) { break; } } } } gc_sweep_all(); mp_deinit(); #if MICROPY_PY_THREAD mp_thread_deinit(); #endif rt_free(heap); mp_putsn_deinit(); mp_getchar_deinit(); #ifdef RT_USING_DFS mp_sys_resource_gc(fd_table_bak); #endif } #if !MICROPY_PY_MODUOS_FILE mp_import_stat_t mp_import_stat(const char *path) { return MP_IMPORT_STAT_NO_EXIST; } #endif NORETURN void nlr_jump_fail(void *val) { mp_printf(MICROPY_ERROR_PRINTER, "nlr_jump_fail\n"); while (1); } #ifndef NDEBUG NORETURN void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { mp_printf(MICROPY_ERROR_PRINTER, "Assertion '%s' failed, at file %s:%d\n", expr, file, line); while (1); } #endif #include int DEBUG_printf(const char *format, ...) { static char log_buf[512]; va_list args; /* args point to the first variable parameter */ va_start(args, format); /* must use vprintf to print */ rt_vsprintf(log_buf, format, args); mp_printf(&mp_plat_print, "%s", log_buf); va_end(args); return 0; } #ifndef MICROPYTHON_USING_UOS mp_lexer_t *mp_lexer_new_from_file(const char *filename) { mp_raise_OSError(MP_ENOENT); } #endif #if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) #include static void python(uint8_t argc, char **argv) { if (argc > 1) { mpy_main(argv[1]); } else { mpy_main(NULL); } } MSH_CMD_EXPORT(python, MicroPython: `python [file.py]` execute python script); #endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */ ================================================ FILE: port/mpy_project_cfg.h ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2017 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * MicroPython Project compile configuration */ #define NDEBUG 0 #define N_X64 0 #define N_X86 0 #define N_THUMB 0 #define N_ARM 0 #define N_XTENSA 0 ================================================ FILE: port/native/easyflash_module.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #if defined(MICROPYTHON_USING_FFI) && defined(PKG_EASYFLASH_ENV) #include RTM_EXPORT(ef_set_env) RTM_EXPORT(ef_get_env) RTM_EXPORT(ef_del_env) RTM_EXPORT(ef_print_env) #endif /* defined(MICROPYTHON_USING_FFI) && defined(PKG_EASYFLASH_ENV) */ ================================================ FILE: port/native/easyflash_module.py ================================================ import ffi print = ffi.func("v", "ef_print_env", "v") set = ffi.func("i", "ef_set_env", "ss") get = ffi.func("s", "ef_get_env", "s") remove = ffi.func("i", "ef_del_env", "s") ================================================ FILE: port/native/native_module.c ================================================ /* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2019 Armink (armink.ztl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #ifdef MICROPYTHON_USING_FFI /* * Native module implement by C function export. * The python module will using ffi to import all of functions. * You can call those functions by: * * STEP1: * generate the python module from 'native_module.c' to 'native_module.py' * run 'python native_gen.py native_module.c' command * * STEP2: * push the 'native_module.py' file to '/libs/mpy/' folder on target * * STEP3: * import native_modbule * * STEP4: * native_module.show("Hello native module") * native_module.add(1, 2) */ void native_module_show(const char *str) { mp_printf(&mp_plat_print, "Native module show: %s\n", str); } RTM_EXPORT(native_module_show) int native_module_add(int a, int b) { return a + b; } RTM_EXPORT(native_module_add) #endif /* MICROPYTHON_USING_FFI */ ================================================ FILE: port/native/native_module.py ================================================ import ffi show = ffi.func("v", "native_module_show", "s") add = ffi.func("i", "native_module_add", "ii") ================================================ FILE: port/qstrdefsport.h ================================================ // qstrs specific to this port ================================================ FILE: py/argcheck.c ================================================ /* * 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 yet implemented - use normal args instead")); } #endif ================================================ FILE: py/asmarm.c ================================================ /* * 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) void asm_arm_end_pass(asm_arm_t *as) { if (as->base.pass == MP_ASM_PASS_EMIT) { #if defined(__linux__) && defined(__GNUC__) char *start = mp_asm_base_get_code(&as->base); char *end = start + mp_asm_base_get_code_size(&as->base); __builtin___clear_cache(start, end); #elif defined(__arm__) // flush I- and D-cache asm volatile ( "0:" "mrc p15, 0, r15, c7, c10, 3\n" "bne 0b\n" "mov r0, #0\n" "mcr p15, 0, r0, c7, c7, 0\n" : : : "r0", "cc"); #endif } } // 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_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_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_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_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_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 ================================================ FILE: py/asmarm.h ================================================ /* * 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; void asm_arm_end_pass(asm_arm_t *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_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_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_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_IMM_FIX_U16(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_arm_mov_reg_i32((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_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_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 ================================================ FILE: py/asmbase.c ================================================ /* * 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->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 uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write) { uint8_t *c = NULL; 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); 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 ================================================ FILE: py/asmbase.h ================================================ /* * 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 { int pass; 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(mp_asm_base_t *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 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 ================================================ FILE: py/asmthumb.c ================================================ /* * 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/persistentcode.h" #include "py/mphal.h" #include "py/asmthumb.h" #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)) 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); } void asm_thumb_end_pass(asm_thumb_t *as) { (void)as; // could check labels are resolved... #if __ICACHE_PRESENT == 1 if (as->base.pass == MP_ASM_PASS_EMIT) { // flush D-cache, so the code emitted is stored in memory MP_HAL_CLEAN_DCACHE(as->base.code_base, as->base.code_size); // invalidate I-cache SCB_InvalidateICache(); } #endif } /* 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__)) #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 (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)); } } as->push_reglist = reglist; as->stack_adjust = stack_adjust; } void asm_thumb_exit(asm_thumb_t *as) { if (as->stack_adjust > 0) { 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)); } } 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! size_t 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); size_t loc = mp_asm_base_get_code_pos(&as->base); // 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)); return loc; } #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 arithmetics 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 { asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); return true; } } #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); 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); 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 (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); } } #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)) 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; assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); 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; assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); 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 -= 4 + 4; // adjust for mov_reg_i16 and then PC+4 prefetch of add_reg_reg rel |= 1; // to stay in Thumb state when jumping to this address asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, rlo_dest, rel); // 4 bytes asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes } 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)); } 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 { asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset); } } // 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)); } else { goto large_jump; } } else { // is a forwards jump, so need to assume it's large large_jump: asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel)); } } 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)); } else { goto large_jump; } } else { // is a forwards jump, so need to assume it's large large_jump: asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(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 ================================================ FILE: py/asmthumb.h ================================================ /* * 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" #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; void asm_thumb_end_pass(asm_thumb_t *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_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); } // 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); } // 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 byte_offset) { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_STRH, rlo_src, rlo_base, byte_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 byte_offset) { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, byte_offset); } // 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); size_t 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 byte_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 // 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_IMM_FIX_U16(as, reg_dest, imm) asm_thumb_mov_reg_i16((as), ASM_THUMB_OP_MOVW, (reg_dest), (imm)) #define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_thumb_mov_reg_i32((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_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_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 ================================================ FILE: py/asmx64.c ================================================ /* * 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_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) { assert(src_r64 < 8); if (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, 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) { assert(src_r64 < 8); if (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, 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) { assert(src_r64 < 8); if (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, 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 assert(src_r64 < 8); assert(dest_r64 < 8); asm_x64_write_byte_2(as, REX_PREFIX | REX_W, 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_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 ================================================ FILE: py/asmx64.h ================================================ /* * 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_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_IMM_FIX_U16(as, reg_dest, imm) asm_x64_mov_i32_to_r64((as), (imm), (reg_dest)) #define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_x64_mov_i32_to_r64((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_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_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 ================================================ FILE: py/asmx86.c ================================================ /* * 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_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_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 ================================================ FILE: py/asmx86.h ================================================ /* * 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_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_IMM_FIX_U16(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest)) #define ASM_MOV_REG_IMM_FIX_WORD(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_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_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 ================================================ FILE: py/asmxtensa.c ================================================ /* * 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/mpconfig.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 (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_call_ind(asm_xtensa_t *as, uint idx) { if (idx < 16) { asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx); } else { asm_xtensa_op_l32i(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) { if (idx < 16) { asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx); } else { asm_xtensa_op_l32i(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 ================================================ FILE: py/asmxtensa.h ================================================ /* * 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)); } static inline void asm_xtensa_op_movi_n(asm_xtensa_t *as, uint reg_dest, int imm4) { asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RI7(12, reg_dest, imm4)); } 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_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_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_IMM_FIX_U16(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_xtensa_mov_reg_i32((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_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_op_l32i_n((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_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_op_s32i_n((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 ================================================ FILE: py/bc.c ================================================ /* * 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/runtime.h" #include "py/bc0.h" #include "py/bc.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_PERSISTENT_CODE 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; } #endif 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 the offset in bytes from the pointer // code_state->fun_bc->bytecode to the entry n_state (0 for bytecode, non-zero for native) void mp_setup_code_state(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; // ip comes in as an offset into bytecode, so turn it into a true pointer code_state->ip = self->bytecode + (size_t)code_state->ip; #if MICROPY_STACKLESS code_state->prev = NULL; #endif #if MICROPY_PY_SYS_SETTRACE code_state->prev_state = NULL; code_state->frame = NULL; #endif // 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); (void)n_state_unused; (void)n_exc_stack_unused; code_state->sp = &code_state->state[0] - 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; } // get pointer to arg_names array const mp_obj_t *arg_names = (const mp_obj_t *)self->const_table; 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]; for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) { if (wanted_arg_name == arg_names[j]) { if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) { 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_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]); 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 for (size_t i = 0; i < n_kwonly_args; i++) { 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, arg_names[n_pos_args + i], 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'"), MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i])); } } } } 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); } } // read the size part of the prelude const byte *ip = code_state->ip; MP_BC_PRELUDE_SIZE_DECODE(ip); // jump over code info (source file and line-number mapping) 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]); } #if !MICROPY_PERSISTENT_CODE // so bytecode is aligned ip = MP_ALIGN(ip, sizeof(mp_uint_t)); #endif // 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); } #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE // The following table encodes the number of bytes that a specific opcode // takes up. Some opcodes have an extra byte, defined by MP_BC_MASK_EXTRA_BYTE. // There are 4 special opcodes that have an extra byte only when // MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE is enabled (and they take a qstr): // MP_BC_LOAD_NAME // MP_BC_LOAD_GLOBAL // MP_BC_LOAD_ATTR // MP_BC_STORE_ATTR uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint) { uint f = MP_BC_FORMAT(*ip); const byte *ip_start = ip; if (f == MP_BC_FORMAT_QSTR) { if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { if (*ip == MP_BC_LOAD_NAME || *ip == MP_BC_LOAD_GLOBAL || *ip == MP_BC_LOAD_ATTR || *ip == MP_BC_STORE_ATTR) { ip += 1; } } ip += 3; } else { int extra_byte = (*ip & MP_BC_MASK_EXTRA_BYTE) == 0; ip += 1; if (f == MP_BC_FORMAT_VAR_UINT) { if (count_var_uint) { while ((*ip++ & 0x80) != 0) { } } } else if (f == MP_BC_FORMAT_OFFSET) { ip += 2; } ip += extra_byte; } *opcode_size = ip - ip_start; return f; } #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE ================================================ FILE: py/bc.h ================================================ /* * 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" #include "py/objfun.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 // C = n_cells number of bytes/cells in closure section // // source info section: // simple_name : var qstr // source_file : var qstr // // // closure section: // local_num0 : byte // ... : byte // local_numN : byte N = n_cells-1 // // only needed if bytecode contains pointers // // // // // constant table layout: // // argname0 : obj (qstr) // ... : obj (qstr) // argnameN : obj (qstr) N = num_pos_args + num_kwonly_args // const0 : obj // constN : obj #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) #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) // 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; qstr qstr_source_file; const byte *line_info; 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; 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. 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; 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, volatile 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_bytecode_print(const mp_print_t *print, const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table); void mp_bytecode_print2(const mp_print_t *print, const byte *code, size_t len, const mp_uint_t *const_table); const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip); #define mp_bytecode_print_inst(print, code, const_table) mp_bytecode_print2(print, code, 1, const_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))) #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint); #endif static inline size_t mp_bytecode_get_source_line(const byte *line_info, size_t bc_offset) { size_t source_line = 1; size_t c; while ((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 ================================================ FILE: py/bc0.h ================================================ /* * 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 #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_JUMP (MP_BC_BASE_JUMP_E + 0x02) // rel byte code offset, 16-bit signed, in excess #define MP_BC_POP_JUMP_IF_TRUE (MP_BC_BASE_JUMP_E + 0x03) // rel byte code offset, 16-bit signed, in excess #define MP_BC_POP_JUMP_IF_FALSE (MP_BC_BASE_JUMP_E + 0x04) // rel byte code offset, 16-bit signed, in excess #define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // rel byte code offset, 16-bit signed, in excess #define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // rel byte code offset, 16-bit signed, in excess #define MP_BC_UNWIND_JUMP (MP_BC_BASE_JUMP_E + 0x00) // rel byte code offset, 16-bit signed, in excess; then a byte #define MP_BC_SETUP_WITH (MP_BC_BASE_JUMP_E + 0x07) // rel byte code offset, 16-bit unsigned #define MP_BC_SETUP_EXCEPT (MP_BC_BASE_JUMP_E + 0x08) // rel byte code offset, 16-bit unsigned #define MP_BC_SETUP_FINALLY (MP_BC_BASE_JUMP_E + 0x09) // rel byte code offset, 16-bit unsigned #define MP_BC_POP_EXCEPT_JUMP (MP_BC_BASE_JUMP_E + 0x0a) // rel byte code offset, 16-bit unsigned #define MP_BC_FOR_ITER (MP_BC_BASE_JUMP_E + 0x0b) // rel byte code offset, 16-bit unsigned #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 ================================================ FILE: py/binary.c ================================================ /* * 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 'f': size = sizeof(float); break; case 'd': size = sizeof(double); 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 '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; } 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; } 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 == '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 '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 (BYTES_PER_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_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 (BYTES_PER_WORD < 8 && size > sizeof(val)) { int c = (is_signed(val_type) && (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_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; } } ================================================ FILE: py/binary.h ================================================ /* * 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 ================================================ FILE: py/builtin.h ================================================ /* * 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" mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args); mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); 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); // Defined by a port, but declared here for simplicity MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj); MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_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); 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_uarray; extern const mp_obj_module_t mp_module_collections; extern const mp_obj_module_t mp_module_io; extern const mp_obj_module_t mp_module_math; extern const mp_obj_module_t mp_module_cmath; extern const mp_obj_module_t mp_module_micropython; extern const mp_obj_module_t mp_module_ustruct; extern const mp_obj_module_t mp_module_sys; extern const mp_obj_module_t mp_module_gc; extern const mp_obj_module_t mp_module_thread; extern const mp_obj_dict_t mp_module_builtins_globals; // extmod modules extern const mp_obj_module_t mp_module_uasyncio; extern const mp_obj_module_t mp_module_uerrno; extern const mp_obj_module_t mp_module_uctypes; extern const mp_obj_module_t mp_module_uzlib; extern const mp_obj_module_t mp_module_ujson; extern const mp_obj_module_t mp_module_ure; extern const mp_obj_module_t mp_module_uheapq; extern const mp_obj_module_t mp_module_uhashlib; extern const mp_obj_module_t mp_module_ucryptolib; extern const mp_obj_module_t mp_module_ubinascii; extern const mp_obj_module_t mp_module_urandom; extern const mp_obj_module_t mp_module_uselect; extern const mp_obj_module_t mp_module_ussl; extern const mp_obj_module_t mp_module_utimeq; extern const mp_obj_module_t mp_module_machine; extern const mp_obj_module_t mp_module_lwip; extern const mp_obj_module_t mp_module_uwebsocket; extern const mp_obj_module_t mp_module_webrepl; extern const mp_obj_module_t mp_module_framebuf; extern const mp_obj_module_t mp_module_btree; extern const mp_obj_module_t mp_module_ubluetooth; extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; #endif // MICROPY_INCLUDED_PY_BUILTIN_H ================================================ FILE: py/builtinevex.c ================================================ /* * 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 const mp_obj_type_t mp_type_code = { { &mp_type_type }, .name = MP_QSTR_code, }; STATIC mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { // save context and set new context mp_obj_dict_t *old_globals = mp_globals_get(); mp_obj_dict_t *old_locals = mp_locals_get(); mp_globals_set(globals); mp_locals_set(locals); // a bit of a hack: fun_bc will re-set globals, so need to make sure it's // the correct one if (mp_obj_is_type(self->module_fun, &mp_type_fun_bc)) { mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun); fun_bc->globals = globals; } // execute code nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t ret = mp_call_function_0(self->module_fun); nlr_pop(); mp_globals_set(old_globals); mp_locals_set(old_locals); return ret; } else { // exception; restore context and re-raise same exception mp_globals_set(old_globals); mp_locals_set(old_locals); nlr_jump(nlr.ret_val); } } 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 = m_new_obj(mp_obj_code_t); code->base.type = &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 // Extract the source code. mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); // 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(bufinfo.buf); parse_input_kind = MP_PARSE_FILE_INPUT; } else { 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 ================================================ FILE: py/builtinhelp.c ================================================ /* * 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 l = strlen(name); // name should end in '.py' and we strip it off mp_obj_list_append(list, mp_obj_new_str(name, l - 3)); name += l + 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); #if MICROPY_MODULE_FROZEN_STR extern const char mp_frozen_str_names[]; mp_help_add_from_names(list, mp_frozen_str_names); #endif #if MICROPY_MODULE_FROZEN_MPY extern const char mp_frozen_mpy_names[]; mp_help_add_from_names(list, mp_frozen_mpy_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 (type->locals_dict != NULL) { map = &type->locals_dict->map; } } if (map != NULL) { for (uint i = 0; i < map->alloc; i++) { if (map->table[i].key != MP_OBJ_NULL) { mp_help_print_info_about_object(map->table[i].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 ================================================ FILE: py/builtinimport.c ================================================ /* * 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 * * 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 #define PATH_SEP_CHAR '/' bool mp_obj_is_package(mp_obj_t module) { mp_obj_t dest[2]; mp_load_method_maybe(module, MP_QSTR___path__, dest); return dest[0] != MP_OBJ_NULL; } // Stat either frozen or normal module by a given path // (whatever is available, if at all). STATIC mp_import_stat_t mp_import_stat_any(const char *path) { #if MICROPY_MODULE_FROZEN mp_import_stat_t st = mp_frozen_stat(path); if (st != MP_IMPORT_STAT_NO_EXIST) { return st; } #endif return mp_import_stat(path); } STATIC mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) { mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(path)); if (stat == MP_IMPORT_STAT_FILE) { return stat; } #if MICROPY_PERSISTENT_CODE_LOAD vstr_ins_byte(path, path->len - 2, 'm'); stat = mp_import_stat_any(vstr_null_terminated_str(path)); if (stat == MP_IMPORT_STAT_FILE) { return stat; } #endif return MP_IMPORT_STAT_NO_EXIST; } STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) { mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(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); } STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *dest) { #if MICROPY_PY_SYS // extract the list of paths size_t path_num; mp_obj_t *path_items; mp_obj_list_get(mp_sys_path, &path_num, &path_items); if (path_num != 0) { // go through each path looking for a directory or file 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) { vstr_add_strn(dest, p, p_len); vstr_add_char(dest, PATH_SEP_CHAR); } vstr_add_strn(dest, file_str, file_len); mp_import_stat_t stat = stat_dir_or_file(dest); if (stat != MP_IMPORT_STAT_NO_EXIST) { return stat; } } // could not find a directory or file return MP_IMPORT_STAT_NO_EXIST; } #endif // mp_sys_path is empty, so just use the given file name vstr_add_strn(dest, file_str, file_len); return stat_dir_or_file(dest); } #if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) { #if MICROPY_PY___FILE__ qstr source_name = lex->source_name; mp_store_attr(module_obj, 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 = mp_obj_module_get_globals(module_obj); mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals); } #endif #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_MODULE_FROZEN_MPY STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code, const char *source_name) { (void)source_name; #if MICROPY_PY___FILE__ mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(qstr_from_str(source_name))); #endif // execute the module in its context mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj); // save context mp_obj_dict_t *volatile old_globals = mp_globals_get(); mp_obj_dict_t *volatile old_locals = mp_locals_get(); // set new context mp_globals_set(mod_globals); mp_locals_set(mod_globals); nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t module_fun = mp_make_function_from_raw_code(raw_code, MP_OBJ_NULL, MP_OBJ_NULL); mp_call_function_0(module_fun); // finish nlr block, restore context nlr_pop(); mp_globals_set(old_globals); mp_locals_set(old_locals); } else { // exception; restore context and re-raise same exception mp_globals_set(old_globals); mp_locals_set(old_locals); nlr_jump(nlr.ret_val); } } #endif STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { #if MICROPY_MODULE_FROZEN || MICROPY_ENABLE_COMPILER || (MICROPY_PERSISTENT_CODE_LOAD && MICROPY_HAS_FILE_READER) 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 = mp_find_frozen_module(file_str, file->len, &modref); #endif // 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) { do_execute_raw_code(module_obj, modref, file_str); return; } #endif // 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_raw_code_t *raw_code = mp_raw_code_load_file(file_str); do_execute_raw_code(module_obj, raw_code, file_str); 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_str); 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 } STATIC void chop_component(const char *start, const char **end) { const char *p = *end; while (p > start) { if (*--p == '.') { *end = p; return; } } *end = p; } mp_obj_t mp_builtin___import__(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(args[i], PRINT_REPR); DEBUG_printf("\n"); } #endif mp_obj_t module_name = args[0]; mp_obj_t fromtuple = mp_const_none; 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 mod_len; const char *mod_str = mp_obj_str_get_data(module_name, &mod_len); if (level != 0) { // What we want to do here is to take name of current module, // chop trailing components, and concatenate with passed-in // module name, thus resolving relative import name into absolute. // This even appears to be correct per // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "Relative imports use a module's __name__ attribute to determine that // module's position in the package hierarchy." level--; mp_obj_t this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(this_name_q != MP_OBJ_NULL); #if MICROPY_CPYTHON_COMPAT if (MP_OBJ_QSTR_VALUE(this_name_q) == MP_QSTR___main__) { // This is a module run by -m command-line switch, get its real name from backup attribute this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } #endif mp_map_t *globals_map = &mp_globals_get()->map; mp_map_elem_t *elem = mp_map_lookup(globals_map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); bool is_pkg = (elem != NULL); #if DEBUG_PRINT DEBUG_printf("Current module/package: "); mp_obj_print(this_name_q, PRINT_REPR); DEBUG_printf(", is_package: %d", is_pkg); DEBUG_printf("\n"); #endif size_t this_name_l; const char *this_name = mp_obj_str_get_data(this_name_q, &this_name_l); const char *p = this_name + this_name_l; if (!is_pkg) { // We have module, but relative imports are anchored at package, so // go there. chop_component(this_name, &p); } while (level--) { chop_component(this_name, &p); } // We must have some component left over to import from if (p == this_name) { mp_raise_ValueError(MP_ERROR_TEXT("can't perform relative import")); } uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len); char *new_mod = mp_local_alloc(new_mod_l); memcpy(new_mod, this_name, p - this_name); if (mod_len != 0) { new_mod[p - this_name] = '.'; memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len); } qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l); mp_local_free(new_mod); DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q)); module_name = MP_OBJ_NEW_QSTR(new_mod_q); mod_str = qstr_str(new_mod_q); mod_len = new_mod_l; } if (mod_len == 0) { mp_raise_ValueError(NULL); } // check if module already exists qstr module_name_qstr = mp_obj_str_get_qstr(module_name); mp_obj_t module_obj = mp_module_get(module_name_qstr); if (module_obj != MP_OBJ_NULL) { DEBUG_printf("Module already loaded\n"); // If it's not a package, return module right away char *p = strchr(mod_str, '.'); if (p == NULL) { return module_obj; } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); return mp_module_get(pkg_name); } DEBUG_printf("Module not yet loaded\n"); uint last = 0; VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) module_obj = MP_OBJ_NULL; mp_obj_t top_module_obj = MP_OBJ_NULL; mp_obj_t outer_module_obj = MP_OBJ_NULL; uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); DEBUG_printf("Processing module: %s\n", qstr_str(mod_name)); DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path)); // find the file corresponding to the module name mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file stat = find_file(mod_str, i, &path); } else { // latter module in the dotted-name; append to path vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); if (stat == MP_IMPORT_STAT_NO_EXIST) { module_obj = MP_OBJ_NULL; #if MICROPY_MODULE_WEAK_LINKS // check if there is a weak link to this module if (i == mod_len) { module_obj = mp_module_search_umodule(mod_str); if (module_obj != MP_OBJ_NULL) { // found weak linked module mp_module_call_init(mod_name, module_obj); } } #endif if (module_obj == MP_OBJ_NULL) { // couldn't find the file, 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'"), mod_name); #endif } } else { // found the file, so get the module module_obj = mp_module_get(mod_name); } if (module_obj == MP_OBJ_NULL) { // module not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); // if args[3] (fromtuple) has magic value False, set up // this module for command-line "-m" option (set module's // name to __main__ instead of real name). 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 package name itself, which is important for future // imports). if (i == mod_len && fromtuple == mp_const_false && 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. 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(mod_name)); #endif } if (stat == MP_IMPORT_STAT_DIR) { DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." 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_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) { // mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { do_load(module_obj, &path); } path.len = orig_path_len; } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // This should be the last component in the import path. If there are // remaining components then it's an ImportError because the current path // (the module that was just loaded) is not a package. This will be caught // on the next iteration because the file will not exist. } } if (outer_module_obj != MP_OBJ_NULL) { qstr s = qstr_from_strn(mod_str + last, i - last); mp_store_attr(outer_module_obj, s, module_obj); } outer_module_obj = module_obj; if (top_module_obj == MP_OBJ_NULL) { top_module_obj = module_obj; } last = i + 1; } } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package return top_module_obj; } #else // MICROPY_ENABLE_EXTERNAL_IMPORT mp_obj_t mp_builtin___import__(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 module already exists, and return it if it does qstr module_name_qstr = mp_obj_str_get_qstr(args[0]); mp_obj_t module_obj = mp_module_get(module_name_qstr); if (module_obj != MP_OBJ_NULL) { return module_obj; } #if MICROPY_MODULE_WEAK_LINKS // Check if there is a weak link to this module module_obj = mp_module_search_umodule(qstr_str(module_name_qstr)); if (module_obj != MP_OBJ_NULL) { // Found weak-linked module mp_module_call_init(module_name_qstr, module_obj); return module_obj; } #endif // 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__); ================================================ FILE: py/compile.c ================================================ /* * 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/persistentcode.h" #if MICROPY_ENABLE_COMPILER // TODO need to mangle __attr names #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; #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 { qstr source_file; 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 } compiler_t; 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, comp->source_file, 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 c_tuple(compiler_t *comp, mp_parse_node_t pn, mp_parse_node_struct_t *pns_list) { int total = 0; if (!MP_PARSE_NODE_IS_NULL(pn)) { compile_node(comp, pn); total += 1; } if (pns_list != NULL) { int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_list); for (int i = 0; i < n; i++) { compile_node(comp, pns_list->nodes[i]); } total += n; } EMIT_ARG(build, total, MP_EMIT_BUILD_TUPLE); } STATIC void compile_generic_tuple(compiler_t *comp, mp_parse_node_struct_t *pns) { // a simple tuple expression c_tuple(comp, MP_PARSE_NODE_NULL, pns); } 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; } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_atom_paren) { // cond is something in parenthesis if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) { // empty tuple, acts as false for the condition if (jump_if == false) { EMIT_ARG(jump, label); } } else { assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)); // non-empty tuple, acts as true for the condition if (jump_if == true) { EMIT_ARG(jump, 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")); } // we need to allow for a caller passing in 1 initial node (node_head) followed by an array of nodes (nodes_tail) STATIC void c_assign_tuple(compiler_t *comp, mp_parse_node_t node_head, uint num_tail, mp_parse_node_t *nodes_tail) { uint num_head = (node_head == MP_PARSE_NODE_NULL) ? 0 : 1; // look for star expression uint have_star_index = -1; if (num_head != 0 && MP_PARSE_NODE_IS_STRUCT_KIND(node_head, PN_star_expr)) { EMIT_ARG(unpack_ex, 0, num_tail); have_star_index = 0; } 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, num_head + i, num_tail - i - 1); have_star_index = num_head + 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_head + num_tail); } if (num_head != 0) { if (0 == have_star_index) { c_assign(comp, ((mp_parse_node_struct_t *)node_head)->nodes[0], ASSIGN_STORE); } else { c_assign(comp, node_head, ASSIGN_STORE); } } for (uint i = 0; i < num_tail; i++) { if (num_head + 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_NULL, 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, MP_PARSE_NODE_NULL, 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, pns->nodes[0], 0, NULL); } break; default: goto cannot_assign; } return; testlist_comp: // lhs is a sequence if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) { mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t *)pns->nodes[1]; if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) { // sequence of one item, with trailing comma assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0])); c_assign_tuple(comp, pns->nodes[0], 0, NULL); } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) { // sequence of many items uint n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns2); c_assign_tuple(comp, pns->nodes[0], n, pns2->nodes); } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) { goto cannot_assign; } else { // sequence with 2 items goto sequence_with_2_items; } } else { // sequence with 2 items sequence_with_2_items: c_assign_tuple(comp, MP_PARSE_NODE_NULL, 2, 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_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; // TODO perhaps factorise testlist_comp code with other uses of PN_testlist_comp 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_testlist_comp_3b) { // sequence of one item, with trailing comma assert(MP_PARSE_NODE_IS_NULL(pns1->nodes[0])); c_del_stmt(comp, pns->nodes[0]); } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3c) { // sequence of many items int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1); c_del_stmt(comp, pns->nodes[0]); for (int i = 0; i < n; i++) { c_del_stmt(comp, pns1->nodes[i]); } } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_comp_for) { goto cannot_delete; } else { // sequence with 2 items goto sequence_with_2_items; } } else { // sequence with 2 items sequence_with_2_items: c_del_stmt(comp, pns->nodes[0]); c_del_stmt(comp, pns->nodes[1]); } } } 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]); } int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); int len = n - 1; for (int 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 (int 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 (int 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_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); if (is_global) { compile_declare_global(comp, (mp_parse_node_t)pns, id_info); } else { compile_declare_nonlocal(comp, (mp_parse_node_t)pns, id_info); } } } } 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; } if ( // optimisation: don't jump over non-existent elif/else blocks !(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3])) // optimisation: don't jump if last instruction was return && !EMIT(last_emit_was_return_value) ) { // 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; } // optimisation: don't jump if last instruction was return if (!EMIT(last_emit_was_return_value)) { 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 if (!EMIT(last_emit_was_return_value)) { 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(load_const_tok, MP_TOKEN_KW_NONE); compile_store_id(comp, qstr_exception_local); compile_delete_id(comp, qstr_exception_local); 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); } EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); EMIT_ARG(label_assign, l_finally_block); compile_node(comp, pn_finally); 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) { // comp->break_label |= MP_EMIT_BREAK_FROM_FOR; qstr context = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]); 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); compile_node(comp, pns->nodes[1]); // iterator EMIT_ARG(load_method, MP_QSTR___aiter__, false); EMIT_ARG(call_method, 0, 0, 0); compile_store_id(comp, context); START_BREAK_CONTINUE_BLOCK EMIT_ARG(label_assign, continue_label); compile_increase_except_level(comp, try_exception_label, MP_EMIT_SETUP_BLOCK_EXCEPT); compile_load_id(comp, context); compile_await_object_method(comp, MP_QSTR___anext__); c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable 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); 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); } 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 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); scope_t *old_scope = comp->scope_cur; if (SCOPE_IS_COMP_LIKE(comp->scope_cur->kind)) { // Use parent's scope for assigned value so it can "escape" comp->scope_cur = comp->scope_cur->parent; } compile_store_id(comp, MP_PARSE_NODE_LEAF_ARG(pn_name)); comp->scope_cur = old_scope; } 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_parse_node_struct_t *star_args_node = NULL, *dblstar_args_node = NULL; 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_SINGLE) { compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("can't have multiple *x")); return; } star_flags |= MP_EMIT_STAR_FLAG_SINGLE; star_args_node = pns_arg; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("can't have multiple **x")); return; } star_flags |= MP_EMIT_STAR_FLAG_DOUBLE; dblstar_args_node = pns_arg; } 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 += 1; } else { compile_comprehension(comp, pns_arg, SCOPE_GEN_EXPR); n_positional++; } } else { goto normal_argument; } } else { normal_argument: if (star_flags) { compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after */**")); return; } if (n_keyword > 0) { compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after keyword arg")); return; } compile_node(comp, args[i]); n_positional++; } } // compile the star/double-star arguments if we had them // if we had one but not the other then we load "null" as a place holder if (star_flags != 0) { if (star_args_node == NULL) { EMIT(load_null); } else { compile_node(comp, star_args_node->nodes[0]); } if (dblstar_args_node == NULL) { EMIT(load_null); } else { compile_node(comp, dblstar_args_node->nodes[0]); } } // 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 c_tuple(comp, MP_PARSE_NODE_NULL, NULL); } else { assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)); pns = (mp_parse_node_struct_t *)pns->nodes[0]; assert(!MP_PARSE_NODE_IS_NULL(pns->nodes[1])); if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) { mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t *)pns->nodes[1]; if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) { // tuple of one item, with trailing comma assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0])); c_tuple(comp, pns->nodes[0], NULL); } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) { // tuple of many items c_tuple(comp, pns->nodes[0], pns2); } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) { // generator expression compile_comprehension(comp, pns, SCOPE_GEN_EXPR); } else { // tuple with 2 items goto tuple_with_2_items; } } else { // tuple with 2 items tuple_with_2_items: c_tuple(comp, MP_PARSE_NODE_NULL, 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_IS_STRUCT(pns2->nodes[1])) { mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t *)pns2->nodes[1]; if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3b) { // list of one item, with trailing comma assert(MP_PARSE_NODE_IS_NULL(pns3->nodes[0])); compile_node(comp, pns2->nodes[0]); EMIT_ARG(build, 1, MP_EMIT_BUILD_LIST); } else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3c) { // list of many items compile_node(comp, pns2->nodes[0]); compile_generic_all_nodes(comp, pns3); EMIT_ARG(build, 1 + MP_PARSE_NODE_STRUCT_NUM_NODES(pns3), MP_EMIT_BUILD_LIST); } else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_comp_for) { // list comprehension compile_comprehension(comp, pns2, SCOPE_LIST_COMP); } else { // list with 2 items goto list_with_2_items; } } else { // list with 2 items list_with_2_items: compile_node(comp, pns2->nodes[0]); compile_node(comp, pns2->nodes[1]); EMIT_ARG(build, 2, 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) { 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) { #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 } 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); #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 fits in target runtime's small-int EMIT_ARG(load_const_small_int, arg); } else { // integer doesn't fit, so create a multi-precision int object // (but only create the actual object on the last pass) if (comp->pass != MP_PASS_EMIT) { EMIT_ARG(load_const_obj, mp_const_none); } else { EMIT_ARG(load_const_obj, mp_obj_new_int_from_ll(arg)); } } #else EMIT_ARG(load_const_small_int, arg); #endif } 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_BYTES: // only create and load the actual bytes object on the last pass if (comp->pass != MP_PASS_EMIT) { EMIT_ARG(load_const_obj, mp_const_none); } else { size_t len; const byte *data = qstr_data(arg, &len); EMIT_ARG(load_const_obj, mp_obj_new_bytes(data, len)); } 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 void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { comp->pass = pass; comp->scope_cur = scope; comp->next_label = 0; 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 return if it wasn't the last opcode if (!EMIT(last_emit_was_return_value)) { 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); } EMIT(end_pass); // make sure we match all the exception levels assert(comp->cur_except_level == 0); } #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++) { if (!MP_PARSE_NODE_IS_SMALL_INT(pn_arg[j])) { 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_PARSE_NODE_LEAF_SMALL_INT(pn_arg[j])); } } } 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, 0, 0, NULL, #endif comp->scope_cur->num_pos_args, 0, 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 mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { // put compiler state on the stack, it's relatively small compiler_t comp_state = {0}; compiler_t *comp = &comp_state; comp->source_file = source_file; comp->is_repl = is_repl; comp->break_label = INVALID_LABEL; comp->continue_label = INVALID_LABEL; // 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(); // compile pass 1 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 maximim 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 pass 2 and 3 #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->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 if (comp->compile_error == MP_OBJ_NULL) { 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, comp->source_file, comp->compile_error_line, comp->scope_cur->simple_name); } // 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 mp_raw_code_t *outer_raw_code = module_scope->raw_code; 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); } else { return outer_raw_code; } } mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, is_repl); // return function that executes the outer module return mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL); } #endif // MICROPY_ENABLE_COMPILER ================================================ FILE: py/compile.h ================================================ /* * 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" // the compiler will raise an exception if an error occurred // the compiler will clear the parse tree before it returns 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 mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); #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 ================================================ FILE: py/dynruntime.h ================================================ /* * 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/nativeglue.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_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_OBJ_NEW_QSTR_##x #define mp_type_type (*mp_fun_table.type_type) #define mp_type_str (*mp_fun_table.type_str) #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_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_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_raise(o, bufinfo, fl) (mp_fun_table.get_buffer_raise((o), (bufinfo), (fl))) #define mp_get_stream_raise(s, flags) (mp_fun_table.get_stream_raise((s), (flags))) #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_list_append(list, item) (mp_fun_table.list_append((list), (item))) 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 (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); } /******************************************************************************/ // General runtime functions #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_raw_code(rc, def_args, def_kw_args) \ (mp_fun_table.make_function_from_raw_code((rc), (def_args), (def_kw_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->globals); \ mp_raw_code_t rc; \ rc.kind = MP_CODE_NATIVE_VIPER; \ rc.scope_flags = 0; \ rc.const_table = (void *)self->const_table; \ (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_raw_code((rc.fun_data = (f), &rc), MP_OBJ_NULL, MP_OBJ_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_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 #endif // MICROPY_INCLUDED_PY_DYNRUNTIME_H ================================================ FILE: py/dynruntime.mk ================================================ # Makefile fragment for generating native .mpy files from C source # MPY_DIR must be set to the top of the MicroPython source tree BUILD ?= build ECHO = @echo RM = /bin/rm MKDIR = /bin/mkdir PYTHON = python3 MPY_CROSS = $(MPY_DIR)/mpy-cross/mpy-cross MPY_TOOL = $(PYTHON) $(MPY_DIR)/tools/mpy-tool.py MPY_LD = $(PYTHON) $(MPY_DIR)/tools/mpy_ld.py Q = @ ifeq ("$(origin V)", "command line") ifeq ($(V),1) Q = MPY_LD += '-vvv' endif endif ARCH_UPPER = $(shell echo $(ARCH) | tr '[:lower:]' '[:upper:]') CONFIG_H = $(BUILD)/$(MOD).config.h CFLAGS += -I. -I$(MPY_DIR) CFLAGS += -std=c99 CFLAGS += -Os CFLAGS += -Wall -Werror -DNDEBUG CFLAGS += -DNO_QSTR CFLAGS += -DMICROPY_ENABLE_DYNRUNTIME CFLAGS += -DMP_CONFIGFILE='<$(CONFIG_H)>' CFLAGS += -fpic -fno-common CFLAGS += -U _FORTIFY_SOURCE # prevent use of __*_chk libc functions #CFLAGS += -fdata-sections -ffunction-sections MPY_CROSS_FLAGS += -march=$(ARCH) SRC_O += $(addprefix $(BUILD)/, $(patsubst %.c,%.o,$(filter %.c,$(SRC)))) SRC_MPY += $(addprefix $(BUILD)/, $(patsubst %.py,%.mpy,$(filter %.py,$(SRC)))) ################################################################################ # Architecture configuration ifeq ($(ARCH),x86) # x86 CROSS = CFLAGS += -m32 -fno-stack-protector MPY_CROSS_FLAGS += -mcache-lookup-bc MICROPY_FLOAT_IMPL ?= double else ifeq ($(ARCH),x64) # x64 CROSS = CFLAGS += -fno-stack-protector MPY_CROSS_FLAGS += -mcache-lookup-bc MICROPY_FLOAT_IMPL ?= double else ifeq ($(ARCH),armv7m) # thumb CROSS = arm-none-eabi- CFLAGS += -mthumb -mcpu=cortex-m3 MICROPY_FLOAT_IMPL ?= none else ifeq ($(ARCH),armv7emsp) # thumb CROSS = arm-none-eabi- CFLAGS += -mthumb -mcpu=cortex-m4 CFLAGS += -mfpu=fpv4-sp-d16 -mfloat-abi=hard MICROPY_FLOAT_IMPL ?= float else ifeq ($(ARCH),armv7emdp) # thumb CROSS = arm-none-eabi- CFLAGS += -mthumb -mcpu=cortex-m7 CFLAGS += -mfpu=fpv5-d16 -mfloat-abi=hard MICROPY_FLOAT_IMPL ?= double else ifeq ($(ARCH),xtensa) # xtensa CROSS = xtensa-lx106-elf- CFLAGS += -mforce-l32 MICROPY_FLOAT_IMPL ?= none else ifeq ($(ARCH),xtensawin) # xtensawin CROSS = xtensa-esp32-elf- CFLAGS += MICROPY_FLOAT_IMPL ?= float else $(error architecture '$(ARCH)' not supported) endif MICROPY_FLOAT_IMPL_UPPER = $(shell echo $(MICROPY_FLOAT_IMPL) | tr '[:lower:]' '[:upper:]') CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_$(MICROPY_FLOAT_IMPL_UPPER) CFLAGS += $(CFLAGS_EXTRA) ################################################################################ # Build rules .PHONY: all clean all: $(MOD).mpy clean: $(RM) -rf $(BUILD) $(CLEAN_EXTRA) # Create build destination directories first BUILD_DIRS = $(sort $(dir $(CONFIG_H) $(SRC_O) $(SRC_MPY))) $(CONFIG_H) $(SRC_O) $(SRC_MPY): | $(BUILD_DIRS) $(BUILD_DIRS): $(Q)$(MKDIR) -p $@ # Preprocess all source files to generate $(CONFIG_H) $(CONFIG_H): $(SRC) $(ECHO) "GEN $@" $(Q)$(MPY_LD) --arch $(ARCH) --preprocess -o $@ $^ # Build .o from .c source files $(BUILD)/%.o: %.c $(CONFIG_H) Makefile $(ECHO) "CC $<" $(Q)$(CROSS)gcc $(CFLAGS) -o $@ -c $< # Build .mpy from .py source files $(BUILD)/%.mpy: %.py $(ECHO) "MPY $<" $(Q)$(MPY_CROSS) $(MPY_CROSS_FLAGS) -o $@ $< # Build native .mpy from object files $(BUILD)/$(MOD).native.mpy: $(SRC_O) $(ECHO) "LINK $<" $(Q)$(MPY_LD) --arch $(ARCH) --qstrs $(CONFIG_H) -o $@ $^ # Build final .mpy from all intermediate .mpy files $(MOD).mpy: $(BUILD)/$(MOD).native.mpy $(SRC_MPY) $(ECHO) "GEN $@" $(Q)$(MPY_TOOL) --merge -o $@ $^ ================================================ FILE: py/emit.h ================================================ /* * 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 } 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_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_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); void (*end_pass)(emit_t *emit); bool (*last_emit_was_return_value)(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; 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); } void 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(void); emit_t *emit_native_x64_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_x86_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_thumb_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_arm_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_xtensa_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_xtensawin_new(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); void mp_emit_bc_end_pass(emit_t *emit); bool mp_emit_bc_last_emit_was_return_value(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 ================================================ FILE: py/emitbc.c ================================================ /* * 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 "py/mpstate.h" #include "py/emit.h" #include "py/bc0.h" #if MICROPY_ENABLE_COMPILER #define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7) #define DUMMY_DATA_SIZE (BYTES_FOR_INT) 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; mp_uint_t last_emit_was_return_value : 8; int stack_size; scope_t *scope; mp_uint_t last_source_line_offset; mp_uint_t last_source_line; mp_uint_t max_num_labels; mp_uint_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 size_t n_info; size_t n_cell; #if MICROPY_PERSISTENT_CODE uint16_t ct_cur_obj; uint16_t ct_num_obj; uint16_t ct_cur_raw_code; #endif mp_uint_t *const_table; }; emit_t *emit_bc_new(void) { emit_t *emit = m_new0(emit_t, 1); 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(mp_uint_t, emit->max_num_labels); } void emit_bc_free(emit_t *emit) { m_del(mp_uint_t, emit->label_offsets, emit->max_num_labels); m_del_obj(emit_t, emit); } typedef byte *(*emit_allocator_t)(emit_t *emit, int nbytes); STATIC void emit_write_uint(emit_t *emit, emit_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[BYTES_FOR_INT]; 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(emit, buf + sizeof(buf) - p); while (p != buf + sizeof(buf) - 1) { *c++ = *p++ | 0x80; } *c = *p; } // all functions must go through this one to emit code info STATIC byte *emit_get_cur_to_write_code_info(emit_t *emit, int num_bytes_to_write) { 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) { #if MICROPY_PERSISTENT_CODE assert((qst >> 16) == 0); byte *c = emit_get_cur_to_write_code_info(emit, 2); c[0] = qst; c[1] = qst >> 8; #else emit_write_uint(emit, emit_get_cur_to_write_code_info, qst); #endif } #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 byte *emit_get_cur_to_write_bytecode(emit_t *emit, int num_bytes_to_write) { 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 emit_write_bytecode_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[BYTES_FOR_INT]; 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); emit_write_uint(emit, emit_get_cur_to_write_bytecode, val); } #if MICROPY_PERSISTENT_CODE STATIC void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n, mp_uint_t c) { if (emit->pass == MP_PASS_EMIT) { emit->const_table[n] = c; } emit_write_bytecode_byte_uint(emit, stack_adj, b, n); } #endif STATIC void emit_write_bytecode_byte_qstr(emit_t *emit, int stack_adj, byte b, qstr qst) { #if MICROPY_PERSISTENT_CODE assert((qst >> 16) == 0); mp_emit_bc_adjust_stack_size(emit, stack_adj); byte *c = emit_get_cur_to_write_bytecode(emit, 3); c[0] = b; c[1] = qst; c[2] = qst >> 8; #else emit_write_bytecode_byte_uint(emit, stack_adj, b, qst); #endif } STATIC void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) { #if MICROPY_PERSISTENT_CODE emit_write_bytecode_byte_const(emit, stack_adj, b, emit->scope->num_pos_args + emit->scope->num_kwonly_args + emit->ct_cur_obj++, (mp_uint_t)obj); #else // aligns the pointer so it is friendly to GC emit_write_bytecode_byte(emit, stack_adj, b); emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(mp_obj_t)); mp_obj_t *c = (mp_obj_t *)emit_get_cur_to_write_bytecode(emit, sizeof(mp_obj_t)); // Verify thar c is already uint-aligned assert(c == MP_ALIGN(c, sizeof(mp_obj_t))); *c = obj; #endif } STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) { #if MICROPY_PERSISTENT_CODE emit_write_bytecode_byte_const(emit, stack_adj, b, emit->scope->num_pos_args + emit->scope->num_kwonly_args + emit->ct_num_obj + emit->ct_cur_raw_code++, (mp_uint_t)(uintptr_t)rc); #else // aligns the pointer so it is friendly to GC emit_write_bytecode_byte(emit, stack_adj, b); emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(void *)); void **c = (void **)emit_get_cur_to_write_bytecode(emit, sizeof(void *)); // Verify thar c is already uint-aligned assert(c == MP_ALIGN(c, sizeof(void *))); *c = rc; #endif #if MICROPY_PY_SYS_SETTRACE rc->line_of_definition = emit->last_source_line; #endif } // unsigned labels are relative to ip following this instruction, stored as 16 bits STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { mp_emit_bc_adjust_stack_size(emit, stack_adj); mp_uint_t bytecode_offset; if (emit->pass < MP_PASS_EMIT) { bytecode_offset = 0; } else { bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3; } byte *c = emit_get_cur_to_write_bytecode(emit, 3); c[0] = b1; c[1] = bytecode_offset; c[2] = bytecode_offset >> 8; } // signed labels are relative to ip following this instruction, stored as 16 bits, in excess STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { mp_emit_bc_adjust_stack_size(emit, stack_adj); int bytecode_offset; if (emit->pass < MP_PASS_EMIT) { bytecode_offset = 0; } else { bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3 + 0x8000; } byte *c = emit_get_cur_to_write_bytecode(emit, 3); c[0] = b1; c[1] = bytecode_offset; c[2] = bytecode_offset >> 8; } void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { emit->pass = pass; emit->stack_size = 0; emit->last_emit_was_return_value = false; emit->scope = scope; emit->last_source_line_offset = 0; emit->last_source_line = 1; #ifndef NDEBUG // With debugging enabled labels are checked for unique assignment if (pass < MP_PASS_EMIT && emit->label_offsets != NULL) { memset(emit->label_offsets, -1, emit->max_num_labels * sizeof(mp_uint_t)); } #endif emit->bytecode_offset = 0; emit->code_info_offset = 0; // 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 (pass >= MP_PASS_CODE_SIZE) { MP_BC_PRELUDE_SIZE_ENCODE(emit->n_info, emit->n_cell, emit_write_code_info_byte, emit); } emit->n_info = emit->code_info_offset; // Write the name and source file of this function. emit_write_code_info_qstr(emit, scope->simple_name); emit_write_code_info_qstr(emit, scope->source_file); #if MICROPY_PERSISTENT_CODE emit->ct_cur_obj = 0; emit->ct_cur_raw_code = 0; #endif if (pass == MP_PASS_EMIT) { // Write argument names (needed to resolve positional args passed as // keywords). We store them as full word-sized objects for efficient access // in mp_setup_code_state this is the start of the prelude and is guaranteed // to be aligned on a word boundary. // 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->const_table[i] = (mp_uint_t)MP_OBJ_NEW_QSTR(qst); } } } void mp_emit_bc_end_pass(emit_t *emit) { if (emit->pass == MP_PASS_SCOPE) { return; } // check stack is back to zero size assert(emit->stack_size == 0); emit_write_code_info_byte(emit, 0); // end of line number info // 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 MICROPY_PERSISTENT_CODE assert(emit->pass <= MP_PASS_STACK_SIZE || (emit->ct_num_obj == emit->ct_cur_obj)); emit->ct_num_obj = emit->ct_cur_obj; #endif if (emit->pass == MP_PASS_CODE_SIZE) { #if !MICROPY_PERSISTENT_CODE // so bytecode is aligned emit->code_info_offset = (size_t)MP_ALIGN(emit->code_info_offset, sizeof(mp_uint_t)); #endif // 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); #if MICROPY_PERSISTENT_CODE emit->const_table = m_new0(mp_uint_t, emit->scope->num_pos_args + emit->scope->num_kwonly_args + emit->ct_cur_obj + emit->ct_cur_raw_code); #else emit->const_table = m_new0(mp_uint_t, emit->scope->num_pos_args + emit->scope->num_kwonly_args); #endif } else if (emit->pass == MP_PASS_EMIT) { mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS emit->code_info_size + emit->bytecode_size, #endif emit->const_table, #if MICROPY_PERSISTENT_CODE_SAVE emit->ct_cur_obj, emit->ct_cur_raw_code, #endif emit->scope->scope_flags); } } bool mp_emit_bc_last_emit_was_return_value(emit_t *emit) { return emit->last_emit_was_return_value; } 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; } emit->last_emit_was_return_value = false; } 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) { mp_emit_bc_adjust_stack_size(emit, 0); if (emit->pass == MP_PASS_SCOPE) { return; } assert(l < emit->max_num_labels); if (emit->pass < MP_PASS_EMIT) { // assign label offset assert(emit->label_offsets[l] == (mp_uint_t)-1); emit->label_offsets[l] = emit->bytecode_offset; } else { // ensure label offset has not changed from MP_PASS_CODE_SIZE to MP_PASS_EMIT assert(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) { 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); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { emit_write_bytecode_raw_byte(emit, 0); } } 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); } if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { emit_write_bytecode_raw_byte(emit, 0); } } 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_signed_label(emit, 0, MP_BC_JUMP, label); } void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) { if (cond) { emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label); } else { emit_write_bytecode_byte_signed_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_signed_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label); } else { emit_write_bytecode_byte_signed_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_signed_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); } else { emit_write_bytecode_byte_signed_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); } } 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_unsigned_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_unsigned_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_unsigned_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label); } 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_raw_code(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code); } else { emit_write_bytecode_byte_raw_code(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_raw_code(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_raw_code(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) { stack_adj -= (int)n_positional + 2 * (int)n_keyword + 2; 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->last_emit_was_return_value = 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); } 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_last_emit_was_return_value, 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 ================================================ FILE: py/emitcommon.c ================================================ /* * 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" #if MICROPY_ENABLE_COMPILER void 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 (SCOPE_IS_FUNC_LIKE(scope->kind) && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { // rebind as a local variable id->kind = ID_INFO_KIND_LOCAL; } } 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) { 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 ================================================ FILE: py/emitglue.c ================================================ /* * 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/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, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS size_t len, #endif const mp_uint_t *const_table, #if MICROPY_PERSISTENT_CODE_SAVE uint16_t n_obj, uint16_t n_raw_code, #endif mp_uint_t scope_flags) { rc->kind = MP_CODE_BYTECODE; rc->scope_flags = scope_flags; rc->fun_data = code; rc->const_table = const_table; #if MICROPY_PERSISTENT_CODE_SAVE rc->fun_data_len = len; rc->n_obj = n_obj; rc->n_raw_code = n_raw_code; #endif #if MICROPY_PY_SYS_SETTRACE mp_bytecode_prelude_t *prelude = &rc->prelude; mp_prof_extract_prelude(code, prelude); #endif #ifdef DEBUG_PRINT #if !MICROPY_DEBUG_PRINTERS 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_DEBUG_PRINTERS if (mp_verbose_flag >= 2) { mp_bytecode_print(&mp_plat_print, rc, code, len, const_table); } #endif } #if MICROPY_EMIT_MACHINE_CODE void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, const mp_uint_t *const_table, #if MICROPY_PERSISTENT_CODE_SAVE uint16_t prelude_offset, uint16_t n_obj, uint16_t n_raw_code, uint16_t n_qstr, mp_qstr_link_entry_t *qstr_link, #endif mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig) { assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM); rc->kind = kind; rc->scope_flags = scope_flags; rc->n_pos_args = n_pos_args; rc->fun_data = fun_data; rc->const_table = const_table; rc->type_sig = type_sig; #if MICROPY_PERSISTENT_CODE_SAVE rc->fun_data_len = fun_len; rc->prelude_offset = prelude_offset; rc->n_obj = n_obj; rc->n_raw_code = n_raw_code; rc->n_qstr = n_qstr; rc->qstr_link = qstr_link; #endif #ifdef DEBUG_PRINT DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, (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", ((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_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args) { DEBUG_OP_printf("make_function_from_raw_code %p\n", rc); assert(rc != NULL); // def_args must be MP_OBJ_NULL or a tuple assert(def_args == MP_OBJ_NULL || mp_obj_is_type(def_args, &mp_type_tuple)); // def_kw_args must be MP_OBJ_NULL or a dict assert(def_kw_args == MP_OBJ_NULL || mp_obj_is_type(def_kw_args, &mp_type_dict)); // 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: case MP_CODE_NATIVE_VIPER: fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->fun_data, rc->const_table); // Check for a generator function, and if so change the type of the object if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) { ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap; } break; #endif #if MICROPY_EMIT_INLINE_ASM case MP_CODE_NATIVE_ASM: fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->fun_data, rc->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, def_kw_args, rc->fun_data, rc->const_table); // check for generator functions and if so change the type of the object if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) { ((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_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args) { DEBUG_OP_printf("make_closure_from_raw_code %p " UINT_FMT " %p\n", rc, 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_raw_code(rc, args[0], args[1]); } else { // default positional and keyword args not given ffun = mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL); } // wrap function in closure object return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2)); } ================================================ FILE: py/emitglue.h ================================================ /* * 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. // 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; typedef struct _mp_qstr_link_entry_t { uint16_t off; uint16_t qst; } mp_qstr_link_entry_t; typedef struct _mp_raw_code_t { mp_uint_t kind : 3; // of type mp_raw_code_kind_t mp_uint_t scope_flags : 7; mp_uint_t n_pos_args : 11; const void *fun_data; const mp_uint_t *const_table; #if MICROPY_PERSISTENT_CODE_SAVE size_t fun_data_len; uint16_t n_obj; uint16_t n_raw_code; #if MICROPY_PY_SYS_SETTRACE mp_bytecode_prelude_t prelude; // 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. mp_uint_t line_of_definition; #endif #if MICROPY_EMIT_MACHINE_CODE uint16_t prelude_offset; uint16_t n_qstr; mp_qstr_link_entry_t *qstr_link; #endif #endif #if MICROPY_EMIT_MACHINE_CODE mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc #endif } mp_raw_code_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, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS size_t len, #endif const mp_uint_t *const_table, #if MICROPY_PERSISTENT_CODE_SAVE uint16_t n_obj, uint16_t n_raw_code, #endif mp_uint_t scope_flags); void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, const mp_uint_t *const_table, #if MICROPY_PERSISTENT_CODE_SAVE uint16_t prelude_offset, uint16_t n_obj, uint16_t n_raw_code, uint16_t n_qstr, mp_qstr_link_entry_t *qstr_link, #endif mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig); mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args); mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); #endif // MICROPY_INCLUDED_PY_EMITGLUE_H ================================================ FILE: py/emitinlinethumb.c ================================================ /* * 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; }; 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; } #if MICROPY_EMIT_INLINE_THUMB_FLOAT 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; } #endif 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 #if MICROPY_EMIT_INLINE_THUMB_FLOAT // 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" }, }; #endif // shorthand alias for whether we allow ARMv7-M instructions #define ARMV7M MICROPY_EMIT_INLINE_THUMB_ARMV7M 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 MICROPY_EMIT_INLINE_THUMB_FLOAT if (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; } #endif 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]); if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, op_len == 5 && op_str[4] == 'w')) { 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 & 0xff00) == 0) { asm_thumb_op16(&emit->as, 0xb400 | reglist); } 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 & 0xff00) == 0) { asm_thumb_op16(&emit->as, 0xbc00 | reglist); } 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 ================================================ FILE: py/emitinlinextensa.c ================================================ /* * 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 ================================================ FILE: py/emitnarm.c ================================================ // 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 NLR_BUF_IDX_LOCAL_2 (4) // r5 #define NLR_BUF_IDX_LOCAL_3 (5) // r6 #define N_ARM (1) #define EXPORT_FUN(name) emit_native_arm_##name #include "py/emitnative.c" #endif ================================================ FILE: py/emitnative.c ================================================ /* * 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/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] // emit->code_state_start: mp_code_state_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 // // Then REG_GENERATOR_STATE points to: // 0=emit->code_state_start: mp_code_state_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] // 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_t) / sizeof(uintptr_t)) #define OFFSETOF_CODE_STATE_STATE (offsetof(mp_code_state_t, state) / sizeof(uintptr_t)) #define OFFSETOF_CODE_STATE_FUN_BC (offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t)) #define OFFSETOF_CODE_STATE_IP (offsetof(mp_code_state_t, ip) / sizeof(uintptr_t)) #define OFFSETOF_CODE_STATE_SP (offsetof(mp_code_state_t, sp) / sizeof(uintptr_t)) #define OFFSETOF_OBJ_FUN_BC_GLOBALS (offsetof(mp_obj_fun_bc_t, globals) / sizeof(uintptr_t)) #define OFFSETOF_OBJ_FUN_BC_BYTECODE (offsetof(mp_obj_fun_bc_t, bytecode) / sizeof(uintptr_t)) #define OFFSETOF_OBJ_FUN_BC_CONST_TABLE (offsetof(mp_obj_fun_bc_t, const_table) / 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 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) (NLR_BUF_IDX_LOCAL_2) #define LOCAL_IDX_RET_VAL(emit) (NLR_BUF_IDX_LOCAL_3) #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)) #define REG_GENERATOR_STATE (REG_LOCAL_3) #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_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 start_offset; int n_state; uint16_t code_state_start; uint16_t stack_start; int stack_size; uint16_t n_cell; uint16_t const_table_cur_obj; uint16_t const_table_num_obj; uint16_t const_table_cur_raw_code; mp_uint_t *const_table; #if MICROPY_PERSISTENT_CODE_SAVE uint16_t qstr_link_cur; mp_qstr_link_entry_t *qstr_link; #endif bool last_emit_was_return_value; scope_t *scope; ASM_T *as; }; STATIC const uint8_t reg_local_table[REG_LOCAL_NUM] = {REG_LOCAL_1, REG_LOCAL_2, REG_LOCAL_3}; 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_obj_t * error_slot, uint *label_slot, mp_uint_t max_num_labels) { emit_t *emit = m_new0(emit_t, 1); 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 size_t loc = ASM_MOV_REG_IMM_FIX_U16(emit->as, arg_reg, qst); size_t link_idx = emit->qstr_link_cur++; if (emit->pass == MP_PASS_EMIT) { emit->qstr_link[link_idx].off = loc << 2 | 1; emit->qstr_link[link_idx].qst = 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 size_t loc = ASM_MOV_REG_IMM_FIX_WORD(emit->as, reg_dest, (mp_uint_t)MP_OBJ_NEW_QSTR(qst)); size_t link_idx = emit->qstr_link_cur++; if (emit->pass == MP_PASS_EMIT) { emit->qstr_link[link_idx].off = loc << 2 | 2; emit->qstr_link[link_idx].qst = 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; #if N_PRELUDE_AS_BYTES_OBJ emit->const_table_cur_obj = emit->do_viper_types ? 0 : 1; // reserve first obj for prelude bytes obj #else emit->const_table_cur_obj = 0; #endif emit->const_table_cur_raw_code = 0; #if MICROPY_PERSISTENT_CODE_SAVE emit->qstr_link_cur = 0; #endif emit->last_emit_was_return_value = false; 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] = VTYPE_UNBOUND; } // 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_t or reduced version for viper) emit->code_state_start = 0; if (NEED_GLOBAL_EXC_HANDLER(emit)) { emit->code_state_start = SIZEOF_NLR_BUF; } 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 > REG_LOCAL_NUM) { num_locals_in_regs = REG_LOCAL_NUM; } // Need a spot for REG_LOCAL_3 if 4 or more args (see below) if (scope->num_pos_args >= 4) { --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_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, 0); // 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_3 #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_3); #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_3, 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_3, 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_3 points to the args array so be sure not to overwrite it if it's still needed if (i < REG_LOCAL_NUM && CAN_USE_REGS_FOR_LOCALS(emit) && (i != 2 || emit->scope->num_pos_args == 3)) { 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 3rd local from the stack back into REG_LOCAL_3 if this reg couldn't be written to above if (emit->scope->num_pos_args >= 4 && CAN_USE_REGS_FOR_LOCALS(emit)) { ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_3, LOCAL_IDX_LOCAL_VAR(emit, 2)); } emit_native_global_exc_entry(emit); } else { // work out size of state (locals plus stack) emit->n_state = scope->num_locals + scope->stack_size; if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { emit->code_state_start = 0; emit->stack_start = SIZEOF_CODE_STATE; #if N_PRELUDE_AS_BYTES_OBJ // Load index of prelude bytes object in const_table mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)(emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1)); #else mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->prelude_offset); #endif mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->start_offset); ASM_ENTRY(emit->as, SIZEOF_NLR_BUF); // 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_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_TEMP0, emit->scope->num_pos_args + emit->scope->num_kwonly_args); } 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_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args); // Set code_state.fun_bc ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); // Set code_state.ip (offset from start of this function to prelude info) #if N_PRELUDE_AS_BYTES_OBJ // Prelude is a bytes object in const_table; store ip = prelude->data - fun_bc->bytecode ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, offsetof(mp_obj_str_t, data) / sizeof(uintptr_t)); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_PARENT_ARG_1, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_BYTECODE); ASM_SUB_REG_REG(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1); emit_native_mov_state_reg(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, REG_LOCAL_3); #else // TODO this encoding may change size in the final pass, need to make it fixed emit_native_mov_state_imm_via(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, emit->prelude_offset, REG_PARENT_ARG_1); #endif // 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(mp_code_state_t, n_state) / sizeof(uintptr_t), 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 < REG_LOCAL_NUM && 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; } } if (pass == MP_PASS_EMIT) { // write argument names as qstr objects // see comment in corresponding part of emitbc.c about the logic here 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->const_table[i] = (mp_uint_t)MP_OBJ_NEW_QSTR(qst); } } } } static inline void emit_native_write_code_info_byte(emit_t *emit, byte val) { mp_asm_base_data(&emit->as->base, 1, val); } STATIC void 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); 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); #if MICROPY_PERSISTENT_CODE size_t n_info = 4; #else size_t n_info = 1; #endif MP_BC_PRELUDE_SIZE_ENCODE(n_info, emit->n_cell, emit_native_write_code_info_byte, emit); #if MICROPY_PERSISTENT_CODE mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name); mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name >> 8); mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file); mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file >> 8); #else mp_asm_base_data(&emit->as->base, 1, 1); #endif // 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; #if N_PRELUDE_AS_BYTES_OBJ // Prelude bytes object is after qstr arg names and mp_fun_table size_t table_off = emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1; if (emit->pass == MP_PASS_EMIT) { void *buf = emit->as->base.code_base + emit->prelude_offset; size_t n = emit->as->base.code_offset - emit->prelude_offset; emit->const_table[table_off] = (uintptr_t)mp_obj_new_bytes(buf, n); } #endif } ASM_END_PASS(emit->as); // check stack is back to zero size assert(emit->stack_size == 0); assert(emit->exc_stack_size == 0); // Deal with const table accounting assert(emit->pass <= MP_PASS_STACK_SIZE || (emit->const_table_num_obj == emit->const_table_cur_obj)); emit->const_table_num_obj = emit->const_table_cur_obj; if (emit->pass == MP_PASS_CODE_SIZE) { size_t const_table_alloc = 1 + emit->const_table_num_obj + emit->const_table_cur_raw_code; size_t nqstr = 0; if (!emit->do_viper_types) { // Add room for qstr names of arguments nqstr = emit->scope->num_pos_args + emit->scope->num_kwonly_args; const_table_alloc += nqstr; } emit->const_table = m_new(mp_uint_t, const_table_alloc); #if !MICROPY_DYNAMIC_COMPILER // Store mp_fun_table pointer just after qstrs // (but in dynamic-compiler mode eliminate dependency on mp_fun_table) emit->const_table[nqstr] = (mp_uint_t)(uintptr_t)&mp_fun_table; #endif #if MICROPY_PERSISTENT_CODE_SAVE size_t qstr_link_alloc = emit->qstr_link_cur; if (qstr_link_alloc > 0) { emit->qstr_link = m_new(mp_qstr_link_entry_t, qstr_link_alloc); } #endif } 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_emit_glue_assign_native(emit->scope->raw_code, emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, f, f_len, emit->const_table, #if MICROPY_PERSISTENT_CODE_SAVE emit->prelude_offset, emit->const_table_cur_obj, emit->const_table_cur_raw_code, emit->qstr_link_cur, emit->qstr_link, #endif emit->scope->num_pos_args, emit->scope->scope_flags, 0); } } STATIC bool emit_native_last_emit_was_return_value(emit_t *emit) { return emit->last_emit_was_return_value; } 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; } #ifdef 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) { emit->last_emit_was_return_value = false; } // 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); } } } } 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) { 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; } } STATIC void need_stack_settled(emit_t *emit) { DEBUG_printf(" need_stack_settled; stack_size=%d\n", emit->stack_size); 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); } } 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; si->vtype = load_reg_stack_imm(emit, REG_TEMP0, si, false); emit_native_mov_state_reg(emit, emit->stack_start + i, REG_TEMP0); } } } // 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 poped 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) { emit->last_emit_was_return_value = false; 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) { emit->last_emit_was_return_value = false; adjust_stack(emit, -1); } STATIC void emit_pre_pop_reg(emit_t *emit, vtype_kind_t *vtype, int reg_dest) { emit->last_emit_was_return_value = false; 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_ptr(emit_t *emit, int reg, mp_uint_t ptr, size_t table_off) { if (!emit->do_viper_types) { // Skip qstr names of arguments table_off += emit->scope->num_pos_args + emit->scope->num_kwonly_args; } if (emit->pass == MP_PASS_EMIT) { emit->const_table[table_off] = ptr; } 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_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, reg, REG_TEMP0, table_off); } STATIC void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj) { // First entry is for mp_fun_table size_t table_off = 1 + emit->const_table_cur_obj++; emit_load_reg_with_ptr(emit, reg, (mp_uint_t)obj, table_off); } STATIC void emit_load_reg_with_raw_code(emit_t *emit, int reg, mp_raw_code_t *rc) { // First entry is for mp_fun_table, then constant objects size_t table_off = 1 + emit->const_table_num_obj + emit->const_table_cur_raw_code++; emit_load_reg_with_ptr(emit, reg, (mp_uint_t)rc, 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_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(emit->as, REG_LOCAL_2, LOCAL_IDX_EXC_HANDLER_UNWIND(emit)); 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_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_LOCAL_2); 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); #if N_NLR_SETJMP // Reload REG_FUN_TABLE, since it may be clobbered by longjmp emit_native_mov_reg_state(emit, REG_LOCAL_1, LOCAL_IDX_FUN_OBJ(emit)); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_1, REG_LOCAL_1, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_1, emit->scope->num_pos_args + emit->scope->num_kwonly_args); #endif 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) { #if MICROPY_PERSISTENT_CODE_SAVE emit_native_load_const_obj(emit, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); #else emit_post_push_imm(emit, VTYPE_PYOBJ, (mp_uint_t)MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); #endif } 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->scope->scope_flags |= MP_SCOPE_FLAG_HASCONSTS; 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 < REG_LOCAL_NUM && 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); 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 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 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 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); 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 < REG_LOCAL_NUM && 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_val; emit_pre_pop_reg_reg(emit, &vtype_base, REG_ARG_1, &vtype_val, REG_ARG_3); // arg1 = base, arg3 = value assert(vtype_base == VTYPE_PYOBJ); assert(vtype_val == 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_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_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); } 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; emit_pre_pop_reg(emit, &vtype, REG_ARG_2); if (vtype == VTYPE_PYOBJ) { emit_call_with_imm_arg(emit, MP_F_UNARY_OP, op, 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("unary op %q not implemented"), mp_unary_op_method_name[op]); } } 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 (MP_BINARY_OP_LESS <= op && op <= MP_BINARY_OP_NOT_EQUAL) { // comparison ops are (in enum order): // 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 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); 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); #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); if (n_pos_defaults == 0 && n_kw_defaults == 0) { need_reg_all(emit); ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); ASM_MOV_REG_IMM(emit->as, REG_ARG_3, (mp_uint_t)MP_OBJ_NULL); } else { vtype_kind_t vtype_def_tuple, vtype_def_dict; emit_pre_pop_reg_reg(emit, &vtype_def_dict, REG_ARG_3, &vtype_def_tuple, REG_ARG_2); assert(vtype_def_tuple == VTYPE_PYOBJ); assert(vtype_def_dict == VTYPE_PYOBJ); need_reg_all(emit); } emit_load_reg_with_raw_code(emit, REG_ARG_1, scope->raw_code); ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_RAW_CODE); 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) { emit_native_pre(emit); if (n_pos_defaults == 0 && n_kw_defaults == 0) { emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over); ASM_MOV_REG_IMM(emit->as, REG_ARG_2, n_closed_over); } else { emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over + 2); ASM_MOV_REG_IMM(emit->as, REG_ARG_2, 0x100 | n_closed_over); } emit_load_reg_with_raw_code(emit, REG_ARG_1, scope->raw_code); ASM_CALL_IND(emit->as, MP_F_MAKE_CLOSURE_FROM_RAW_CODE); 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 + 3); // 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 + 4); // 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); emit->last_emit_was_return_value = true; 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); emit->last_emit_was_return_value = true; } 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); } 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_last_emit_was_return_value, 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 ================================================ FILE: py/emitnthumb.c ================================================ // 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 NLR_BUF_IDX_LOCAL_2 (4) // r5 #define NLR_BUF_IDX_LOCAL_3 (5) // r6 #define N_THUMB (1) #define EXPORT_FUN(name) emit_native_thumb_##name #include "py/emitnative.c" #endif ================================================ FILE: py/emitnx64.c ================================================ // 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 NLR_BUF_IDX_LOCAL_2 (6) // r12 #define NLR_BUF_IDX_LOCAL_3 (7) // r13 #define N_X64 (1) #define EXPORT_FUN(name) emit_native_x64_##name #include "py/emitnative.c" #endif ================================================ FILE: py/emitnx86.c ================================================ // 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 #define NLR_BUF_IDX_LOCAL_2 (7) // esi #define NLR_BUF_IDX_LOCAL_3 (6) // edi // 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_RAW_CODE] = 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_MAKE_CLOSURE_FROM_RAW_CODE] = 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 ================================================ FILE: py/emitnxtensa.c ================================================ // 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 NLR_BUF_IDX_LOCAL_2 (9) // a13 #define NLR_BUF_IDX_LOCAL_3 (10) // a14 #define N_XTENSA (1) #define EXPORT_FUN(name) emit_native_xtensa_##name #include "py/emitnative.c" #endif ================================================ FILE: py/emitnxtensawin.c ================================================ // 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 NLR_BUF_IDX_LOCAL_2 (2 + 5) // a5 #define NLR_BUF_IDX_LOCAL_3 (2 + 6) // a6 #define N_NLR_SETJMP (1) #define N_PRELUDE_AS_BYTES_OBJ (1) #define N_XTENSAWIN (1) #define EXPORT_FUN(name) emit_native_xtensawin_##name #include "py/emitnative.c" #endif ================================================ FILE: py/formatfloat.c ================================================ /* * 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" #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 funcion 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 #define FLT_EXP_MASK 0x7F800000 #define FLT_MAN_MASK 0x007FFFFF union floatbits { float f; uint32_t u; }; static inline int fp_signbit(float x) { union floatbits fb = {x}; return fb.u & FLT_SIGN_MASK; } #define fp_isnan(x) isnan(x) #define fp_isinf(x) isinf(x) static inline int fp_iszero(float x) { union floatbits fb = {x}; return fb.u == 0; } static inline int fp_isless1(float x) { union floatbits fb = {x}; return fb.u < 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 static const FPTYPE g_pos_pow[] = { #if FPDECEXP > 32 MICROPY_FLOAT_CONST(1e256), MICROPY_FLOAT_CONST(1e128), MICROPY_FLOAT_CONST(1e64), #endif MICROPY_FLOAT_CONST(1e32), MICROPY_FLOAT_CONST(1e16), MICROPY_FLOAT_CONST(1e8), MICROPY_FLOAT_CONST(1e4), MICROPY_FLOAT_CONST(1e2), MICROPY_FLOAT_CONST(1e1) }; static const FPTYPE g_neg_pow[] = { #if FPDECEXP > 32 MICROPY_FLOAT_CONST(1e-256), MICROPY_FLOAT_CONST(1e-128), MICROPY_FLOAT_CONST(1e-64), #endif MICROPY_FLOAT_CONST(1e-32), MICROPY_FLOAT_CONST(1e-16), MICROPY_FLOAT_CONST(1e-8), MICROPY_FLOAT_CONST(1e-4), MICROPY_FLOAT_CONST(1e-2), MICROPY_FLOAT_CONST(1e-1) }; 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, e1; int dec = 0; char e_sign = '\0'; int num_digits = 0; const FPTYPE *pos_pow = g_pos_pow; const FPTYPE *neg_pow = g_neg_pow; 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)) { // We need to figure out what an integer digit will be used // in case 'f' is used (or we revert other format to it below). // As we just tested number to be <1, this is obviously 0, // but we can round it up to 1 below. char first_dig = '0'; if (f >= FPROUND_TO_ONE) { first_dig = '1'; } // Build negative exponent for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { if (*neg_pow > f) { e += e1; f *= *pos_pow; } } char e_sign_char = '-'; if (fp_isless1(f) && f >= FPROUND_TO_ONE) { f = FPCONST(1.0); if (e == 0) { e_sign_char = '+'; } } else if (fp_isless1(f)) { e++; f *= FPCONST(10.0); } // 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 = -1; *s++ = first_dig; 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; if (num_digits) { *s++ = '.'; while (--e && num_digits) { *s++ = '0'; num_digits--; } } } else { // For e & g formats, we'll be printing the exponent, so set the // sign. e_sign = e_sign_char; dec = 0; if (prec > (buf_remaining - FPMIN_BUF_SIZE)) { prec = buf_remaining - FPMIN_BUF_SIZE; if (fmt == 'g') { prec++; } } } } else { // Build positive exponent for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { if (*pos_pow <= f) { e += e1; f *= *neg_pow; } } // It can be that f was right on the edge of an entry in pos_pow needs to be reduced if ((int)f >= 10) { e += 1; f *= FPCONST(0.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 = '+'; } } if (prec < 0) { // This can happen when the prec is trimmed to prevent buffer overflow prec = 0; } // We now have num.f as a floating point number between >= 1 and < 10 // (or equal to zero), and e contains the absolute value of the power of // 10 exponent. and (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; } // Print the digits of the mantissa for (int i = 0; i < num_digits; ++i, --dec) { int32_t d = (int32_t)f; if (d < 0) { *s++ = '0'; } else { *s++ = '0' + d; } if (dec == 0 && prec > 0) { *s++ = '.'; } f -= (FPTYPE)d; f *= FPCONST(10.0); } // Round // If we print non-exponential format (i.e. 'f'), but a digit we're going // to round by (e) is too far away, then there's nothing to round. if ((org_fmt != 'f' || e <= num_digits) && f >= FPCONST(5.0)) { 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' 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 ================================================ FILE: py/formatfloat.h ================================================ /* * 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 ================================================ FILE: py/frozenmod.c ================================================ /* * 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. */ #include #include #include "py/lexer.h" #include "py/frozenmod.h" #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 extern const char mp_frozen_str_names[]; extern const uint32_t mp_frozen_str_sizes[]; extern const char mp_frozen_str_content[]; // On input, *len contains size of name, on output - size of content const char *mp_find_frozen_str(const char *str, size_t *len) { const char *name = mp_frozen_str_names; size_t offset = 0; for (int i = 0; *name != 0; i++) { size_t l = strlen(name); if (l == *len && !memcmp(str, name, l)) { *len = mp_frozen_str_sizes[i]; return mp_frozen_str_content + offset; } name += l + 1; offset += mp_frozen_str_sizes[i] + 1; } return NULL; } STATIC mp_lexer_t *mp_lexer_frozen_str(const char *str, size_t len) { size_t name_len = len; const char *content = mp_find_frozen_str(str, &len); if (content == NULL) { return NULL; } qstr source = qstr_from_strn(str, name_len); mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, content, len, 0); return lex; } #endif #if MICROPY_MODULE_FROZEN_MPY #include "py/emitglue.h" extern const char mp_frozen_mpy_names[]; extern const mp_raw_code_t *const mp_frozen_mpy_content[]; STATIC const mp_raw_code_t *mp_find_frozen_mpy(const char *str, size_t len) { const char *name = mp_frozen_mpy_names; for (size_t i = 0; *name != 0; i++) { size_t l = strlen(name); if (l == len && !memcmp(str, name, l)) { return mp_frozen_mpy_content[i]; } name += l + 1; } return NULL; } #endif #if MICROPY_MODULE_FROZEN STATIC mp_import_stat_t mp_frozen_stat_helper(const char *name, const char *str) { size_t len = strlen(str); for (int i = 0; *name != 0; i++) { size_t l = strlen(name); if (l >= len && !memcmp(str, name, len)) { if (name[len] == 0) { return MP_IMPORT_STAT_FILE; } else if (name[len] == '/') { return MP_IMPORT_STAT_DIR; } } name += l + 1; } return MP_IMPORT_STAT_NO_EXIST; } mp_import_stat_t mp_frozen_stat(const char *str) { mp_import_stat_t stat; #if MICROPY_MODULE_FROZEN_STR stat = mp_frozen_stat_helper(mp_frozen_str_names, str); if (stat != MP_IMPORT_STAT_NO_EXIST) { return stat; } #endif #if MICROPY_MODULE_FROZEN_MPY stat = mp_frozen_stat_helper(mp_frozen_mpy_names, str); if (stat != MP_IMPORT_STAT_NO_EXIST) { return stat; } #endif return MP_IMPORT_STAT_NO_EXIST; } int mp_find_frozen_module(const char *str, size_t len, void **data) { #if MICROPY_MODULE_FROZEN_STR mp_lexer_t *lex = mp_lexer_frozen_str(str, len); if (lex != NULL) { *data = lex; return MP_FROZEN_STR; } #endif #if MICROPY_MODULE_FROZEN_MPY const mp_raw_code_t *rc = mp_find_frozen_mpy(str, len); if (rc != NULL) { *data = (void *)rc; return MP_FROZEN_MPY; } #endif return MP_FROZEN_NONE; } #endif ================================================ FILE: py/frozenmod.h ================================================ /* * 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/lexer.h" enum { MP_FROZEN_NONE, MP_FROZEN_STR, MP_FROZEN_MPY, }; int mp_find_frozen_module(const char *str, size_t len, void **data); const char *mp_find_frozen_str(const char *str, size_t *len); mp_import_stat_t mp_frozen_stat(const char *str); #endif // MICROPY_INCLUDED_PY_FROZENMOD_H ================================================ FILE: py/gc.c ================================================ /* * 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_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) / BYTES_PER_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) #define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1))) #define ATB_GET_KIND(block) ((MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3) #define ATB_ANY_TO_FREE(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0) #define ATB_FREE_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0) #define ATB_FREE_TO_TAIL(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0) #define ATB_HEAD_TO_MARK(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0) #define ATB_MARK_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0) #define BLOCK_FROM_PTR(ptr) (((byte *)(ptr) - MP_STATE_MEM(gc_pool_start)) / BYTES_PER_BLOCK) #define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (uintptr_t)MP_STATE_MEM(gc_pool_start))) #define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB) #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(block) ((MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1) #define FTB_SET(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0) #define FTB_CLEAR(block) do { MP_STATE_MEM(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 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); // 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 MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len * BITS_PER_BYTE / (BITS_PER_BYTE + BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK); #else MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); #endif MP_STATE_MEM(gc_alloc_table_start) = (byte *)start; #if MICROPY_ENABLE_FINALISER size_t gc_finaliser_table_byte_len = (MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB; MP_STATE_MEM(gc_finaliser_table_start) = MP_STATE_MEM(gc_alloc_table_start) + MP_STATE_MEM(gc_alloc_table_byte_len); #endif size_t gc_pool_block_len = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; MP_STATE_MEM(gc_pool_start) = (byte *)end - gc_pool_block_len * BYTES_PER_BLOCK; MP_STATE_MEM(gc_pool_end) = end; #if MICROPY_ENABLE_FINALISER assert(MP_STATE_MEM(gc_pool_start) >= MP_STATE_MEM(gc_finaliser_table_start) + gc_finaliser_table_byte_len); #endif // clear ATBs memset(MP_STATE_MEM(gc_alloc_table_start), 0, MP_STATE_MEM(gc_alloc_table_byte_len)); #if MICROPY_ENABLE_FINALISER // clear FTBs memset(MP_STATE_MEM(gc_finaliser_table_start), 0, gc_finaliser_table_byte_len); #endif // set last free ATB index to start of heap MP_STATE_MEM(gc_last_free_atb_index) = 0; // unlock the GC MP_STATE_MEM(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 DEBUG_printf("GC layout:\n"); DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_alloc_table_start), MP_STATE_MEM(gc_alloc_table_byte_len), MP_STATE_MEM(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", MP_STATE_MEM(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", MP_STATE_MEM(gc_pool_start), gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len); } void gc_lock(void) { GC_ENTER(); MP_STATE_MEM(gc_lock_depth)++; GC_EXIT(); } void gc_unlock(void) { GC_ENTER(); MP_STATE_MEM(gc_lock_depth)--; GC_EXIT(); } bool gc_is_locked(void) { return MP_STATE_MEM(gc_lock_depth) != 0; } // 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(gc_pool_start) /* must be above start of pool */ \ && ptr < (void *)MP_STATE_MEM(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. STATIC void gc_mark_subtree(size_t block) { // Start with the block passed in the argument. size_t sp = 0; for (;;) { // 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(block + n_blocks) == AT_TAIL); // check this block's children void **ptrs = (void **)PTR_FROM_BLOCK(block); for (size_t i = n_blocks * BYTES_PER_BLOCK / sizeof(void *); i > 0; i--, ptrs++) { void *ptr = *ptrs; if (VERIFY_PTR(ptr)) { // Mark and push this pointer size_t childblock = BLOCK_FROM_PTR(ptr); if (ATB_GET_KIND(childblock) == AT_HEAD) { // an unmarked head, mark it, and push it on gc stack TRACE_MARK(childblock, ptr); ATB_HEAD_TO_MARK(childblock); if (sp < MICROPY_ALLOC_GC_STACK_SIZE) { MP_STATE_MEM(gc_stack)[sp++] = childblock; } 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 block = MP_STATE_MEM(gc_stack)[--sp]; } } 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 (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { // trace (again) if mark bit set if (ATB_GET_KIND(block) == AT_MARK) { gc_mark_subtree(block); } } } } 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; for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { switch (ATB_GET_KIND(block)) { case AT_HEAD: #if MICROPY_ENABLE_FINALISER if (FTB_GET(block)) { mp_obj_base_t *obj = (mp_obj_base_t *)PTR_FROM_BLOCK(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(block); } #endif free_tail = 1; DEBUG_printf("gc_sweep(%p)\n", PTR_FROM_BLOCK(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(block); #if CLEAR_ON_SWEEP memset((void *)PTR_FROM_BLOCK(block), 0, BYTES_PER_BLOCK); #endif } break; case AT_MARK: ATB_MARK_TO_HEAD(block); free_tail = 0; break; } } } void gc_collect_start(void) { GC_ENTER(); MP_STATE_MEM(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 } void gc_collect_root(void **ptrs, size_t len) { for (size_t i = 0; i < len; i++) { void *ptr = ptrs[i]; if (VERIFY_PTR(ptr)) { size_t block = BLOCK_FROM_PTR(ptr); if (ATB_GET_KIND(block) == AT_HEAD) { // An unmarked head: mark it, and mark all its children TRACE_MARK(block, ptr); ATB_HEAD_TO_MARK(block); gc_mark_subtree(block); } } } } void gc_collect_end(void) { gc_deal_with_stack_overflow(); gc_sweep(); MP_STATE_MEM(gc_last_free_atb_index) = 0; MP_STATE_MEM(gc_lock_depth)--; GC_EXIT(); } void gc_sweep_all(void) { GC_ENTER(); MP_STATE_MEM(gc_lock_depth)++; MP_STATE_MEM(gc_stack_overflow) = 0; gc_collect_end(); } void gc_info(gc_info_t *info) { GC_ENTER(); info->total = MP_STATE_MEM(gc_pool_end) - MP_STATE_MEM(gc_pool_start); info->used = 0; info->free = 0; info->max_free = 0; info->num_1block = 0; info->num_2block = 0; info->max_block = 0; bool finish = false; for (size_t block = 0, len = 0, len_free = 0; !finish;) { size_t kind = ATB_GET_KIND(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 == MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB); // Get next block type if possible if (!finish) { kind = ATB_GET_KIND(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; 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; } GC_ENTER(); // check if GC is locked if (MP_STATE_MEM(gc_lock_depth) > 0) { GC_EXIT(); return NULL; } 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_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 (;;) { // look for a run of n_blocks available blocks n_free = 0; for (i = MP_STATE_MEM(gc_last_free_atb_index); i < MP_STATE_MEM(gc_alloc_table_byte_len); i++) { byte a = MP_STATE_MEM(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* } GC_EXIT(); // nothing found! if (collected) { 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) { MP_STATE_MEM(gc_last_free_atb_index) = (i + 1) / BLOCKS_PER_ATB; } // mark first block as used head ATB_FREE_TO_HEAD(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(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 *)(MP_STATE_MEM(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(start_block); GC_EXIT(); } #else (void)has_finaliser; #endif #if EXTENSIVE_HEAP_PROFILING gc_dump_alloc_table(); #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) { GC_ENTER(); if (MP_STATE_MEM(gc_lock_depth) > 0) { // TODO how to deal with this error? GC_EXIT(); return; } DEBUG_printf("gc_free(%p)\n", ptr); if (ptr == NULL) { GC_EXIT(); } else { // get the GC block number corresponding to this pointer assert(VERIFY_PTR(ptr)); size_t block = BLOCK_FROM_PTR(ptr); assert(ATB_GET_KIND(block) == AT_HEAD); #if MICROPY_ENABLE_FINALISER FTB_CLEAR(block); #endif // set the last_free pointer to this block if it's earlier in the heap if (block / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) { MP_STATE_MEM(gc_last_free_atb_index) = block / BLOCKS_PER_ATB; } // free head and all of its tail blocks do { ATB_ANY_TO_FREE(block); block += 1; } while (ATB_GET_KIND(block) == AT_TAIL); GC_EXIT(); #if EXTENSIVE_HEAP_PROFILING gc_dump_alloc_table(); #endif } } size_t gc_nbytes(const void *ptr) { GC_ENTER(); if (VERIFY_PTR(ptr)) { size_t block = BLOCK_FROM_PTR(ptr); if (ATB_GET_KIND(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(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; } void *ptr = ptr_in; GC_ENTER(); if (MP_STATE_MEM(gc_lock_depth) > 0) { GC_EXIT(); return NULL; } // get the GC block number corresponding to this pointer assert(VERIFY_PTR(ptr)); size_t block = BLOCK_FROM_PTR(ptr); assert(ATB_GET_KIND(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 = MP_STATE_MEM(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(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(bl); } // set the last_free pointer to end of this block if it's earlier in the heap if ((block + new_blocks) / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) { MP_STATE_MEM(gc_last_free_atb_index) = (block + new_blocks) / BLOCKS_PER_ATB; } GC_EXIT(); #if EXTENSIVE_HEAP_PROFILING gc_dump_alloc_table(); #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 for (size_t bl = block + n_blocks; bl < block + new_blocks; bl++) { assert(ATB_GET_KIND(bl) == AT_FREE); ATB_FREE_TO_TAIL(bl); } 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(); #endif return ptr_in; } #if MICROPY_ENABLE_FINALISER bool ftb_state = FTB_GET(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(void) { gc_info_t info; gc_info(&info); mp_printf(&mp_plat_print, "GC: total: %u, used: %u, free: %u\n", (uint)info.total, (uint)info.used, (uint)info.free); mp_printf(&mp_plat_print, " 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(void) { GC_ENTER(); static const size_t DUMP_BYTES_PER_LINE = 64; #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(&mp_plat_print, "GC memory layout; from %p:", MP_STATE_MEM(gc_pool_start)); #endif for (size_t bl = 0; bl < MP_STATE_MEM(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 < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB && ATB_GET_KIND(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(&mp_plat_print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); if (bl >= MP_STATE_MEM(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(&mp_plat_print, "\n%05x: ", (uint)(PTR_FROM_BLOCK(bl) & (uint32_t)0xfffff)); mp_printf(&mp_plat_print, "\n%05x: ", (uint)((bl * BYTES_PER_BLOCK) & (uint32_t)0xfffff)); } int c = ' '; switch (ATB_GET_KIND(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 (VERIFY_PTR(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 (VERIFY_PTR(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 **)(MP_STATE_MEM(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(&mp_plat_print, "%c", c); } mp_print_str(&mp_plat_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(); 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(); } #endif #endif // MICROPY_ENABLE_GC ================================================ FILE: py/gc.h ================================================ /* * 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 "py/mpconfig.h" #include "py/misc.h" void gc_init(void *start, void *end); // 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; } gc_info_t; void gc_info(gc_info_t *info); void gc_dump_info(void); void gc_dump_alloc_table(void); #endif // MICROPY_INCLUDED_PY_GC_H ================================================ FILE: py/grammar.h ================================================ /* * 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)) ================================================ FILE: py/lexer.c ================================================ /* * 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; } 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, '\'', '\"') || (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"')) || ((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; } lex->chr0 = lex->chr1; lex->chr1 = lex->chr2; 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=" // + += "-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) { // 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; 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 (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_DYNAMIC) { if (c < 0x110000 && lex->tok_kind == MP_TOKEN_STRING) { vstr_add_char(&lex->vstr, c); } else if (c < 0x100 && lex->tok_kind == MP_TOKEN_BYTES) { vstr_add_byte(&lex->vstr, c); } else { // unicode character out of range // this raises a generic SyntaxError; could provide more info lex->tok_kind = MP_TOKEN_INVALID; } } else { // without unicode everything is just added as an 8-bit byte if (c < 0x100) { vstr_add_byte(&lex->vstr, c); } else { // 8-bit character out of range // this raises a generic SyntaxError; could provide more info 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); } STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) { bool had_physical_newline = false; while (!is_end(lex)) { if (is_physical_newline(lex)) { if (stop_at_newline && lex->nested_bracket_level == 0) { break; } had_physical_newline = 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); } // had_physical_newline will be set on next loop } else if (is_char_and(lex, '\\', '\n')) { // line-continuation, so don't set had_physical_newline next_char(lex); next_char(lex); } else { break; } } return had_physical_newline; } void mp_lexer_to_next(mp_lexer_t *lex) { // start new token text vstr_reset(&lex->vstr); // skip white space and comments bool had_physical_newline = skip_whitespace(lex, false); // 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 && lex->nested_bracket_level == 0) { 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; 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; } } // 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); // 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); // 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. If it's not then 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_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(const char *filename) { mp_reader_t reader; mp_reader_new_file(&reader, filename); return mp_lexer_new(qstr_from_str(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); 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 ================================================ FILE: py/lexer.h ================================================ /* * 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, 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 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 } 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); void mp_lexer_free(mp_lexer_t *lex); void mp_lexer_to_next(mp_lexer_t *lex); /******************************************************************/ // platform specific import function; must be implemented for a specific port // TODO tidy up, rename, or put elsewhere typedef enum { MP_IMPORT_STAT_NO_EXIST, MP_IMPORT_STAT_DIR, MP_IMPORT_STAT_FILE, } mp_import_stat_t; mp_import_stat_t mp_import_stat(const char *path); mp_lexer_t *mp_lexer_new_from_file(const char *filename); #if MICROPY_HELPER_LEXER_UNIX mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd); #endif #endif // MICROPY_INCLUDED_PY_LEXER_H ================================================ FILE: py/makecompresseddata.py ================================================ from __future__ import print_function import collections import re import sys import gzip import zlib _COMPRESSED_MARKER = 0xFF def check_non_ascii(msg): for c in msg: if ord(c) >= 0x80: print( 'Unable to generate compressed data: message "{}" contains a non-ascii character "{}".'.format( msg, c ), file=sys.stderr, ) sys.exit(1) # Replace with . # Trival scheme to demo/test. def space_compression(error_strings): for line in error_strings: check_non_ascii(line) result = "" for i in range(len(line)): if i > 0 and line[i] == " ": result = result[:-1] result += "\\{:03o}".format(ord(line[i - 1])) else: result += line[i] error_strings[line] = result return None # Replace common words with <0x80 | index>. # Index is into a table of words stored as aaaaa<0x80|a>bbb<0x80|b>... # Replaced words are assumed to have spaces either side to avoid having to store the spaces in the compressed strings. def word_compression(error_strings): topn = collections.Counter() for line in error_strings.keys(): check_non_ascii(line) for word in line.split(" "): topn[word] += 1 # Order not just by frequency, but by expected saving. i.e. prefer a longer string that is used less frequently. # Use the word itself for ties so that compression is deterministic. def bytes_saved(item): w, n = item return -((len(w) + 1) * (n - 1)), w top128 = sorted(topn.items(), key=bytes_saved)[:128] index = [w for w, _ in top128] index_lookup = {w: i for i, w in enumerate(index)} for line in error_strings.keys(): result = "" need_space = False for word in line.split(" "): if word in index_lookup: result += "\\{:03o}".format(0b10000000 | index_lookup[word]) need_space = False else: if need_space: result += " " need_space = True result += word error_strings[line] = result.strip() return "".join(w[:-1] + "\\{:03o}".format(0b10000000 | ord(w[-1])) for w in index) # Replace chars in text with variable length bit sequence. # For comparison only (the table is not emitted). def huffman_compression(error_strings): # https://github.com/tannewt/huffman import huffman all_strings = "".join(error_strings) cb = huffman.codebook(collections.Counter(all_strings).items()) for line in error_strings: b = "1" for c in line: b += cb[c] n = len(b) if n % 8 != 0: n += 8 - (n % 8) result = "" for i in range(0, n, 8): result += "\\{:03o}".format(int(b[i : i + 8], 2)) if len(result) > len(line) * 4: result = line error_strings[line] = result # TODO: This would be the prefix lengths and the table ordering. return "_" * (10 + len(cb)) # Replace common N-letter sequences with <0x80 | index>, where # the common sequences are stored in a separate table. # This isn't very useful, need a smarter way to find top-ngrams. def ngram_compression(error_strings): topn = collections.Counter() N = 2 for line in error_strings.keys(): check_non_ascii(line) if len(line) < N: continue for i in range(0, len(line) - N, N): topn[line[i : i + N]] += 1 def bytes_saved(item): w, n = item return -(len(w) * (n - 1)) top128 = sorted(topn.items(), key=bytes_saved)[:128] index = [w for w, _ in top128] index_lookup = {w: i for i, w in enumerate(index)} for line in error_strings.keys(): result = "" for i in range(0, len(line) - N + 1, N): word = line[i : i + N] if word in index_lookup: result += "\\{:03o}".format(0b10000000 | index_lookup[word]) else: result += word if len(line) % N != 0: result += line[len(line) - len(line) % N :] error_strings[line] = result.strip() return "".join(index) def main(collected_path, fn): error_strings = collections.OrderedDict() max_uncompressed_len = 0 num_uses = 0 # Read in all MP_ERROR_TEXT strings. with open(collected_path, "r") as f: for line in f: line = line.strip() if not line: continue num_uses += 1 error_strings[line] = None max_uncompressed_len = max(max_uncompressed_len, len(line)) # So that objexcept.c can figure out how big the buffer needs to be. print("#define MP_MAX_UNCOMPRESSED_TEXT_LEN ({})".format(max_uncompressed_len)) # Run the compression. compressed_data = fn(error_strings) # Print the data table. print('MP_COMPRESSED_DATA("{}")'.format(compressed_data)) # Print the replacements. for uncomp, comp in error_strings.items(): if uncomp == comp: prefix = "" else: prefix = "\\{:03o}".format(_COMPRESSED_MARKER) print('MP_MATCH_COMPRESSED("{}", "{}{}")'.format(uncomp, prefix, comp)) # Used to calculate the "true" length of the (escaped) compressed strings. def unescape(s): return re.sub(r"\\\d\d\d", "!", s) # Stats. Note this doesn't include the cost of the decompressor code. uncomp_len = sum(len(s) + 1 for s in error_strings.keys()) comp_len = sum(1 + len(unescape(s)) + 1 for s in error_strings.values()) data_len = len(compressed_data) + 1 if compressed_data else 0 print("// Total input length: {}".format(uncomp_len)) print("// Total compressed length: {}".format(comp_len)) print("// Total data length: {}".format(data_len)) print("// Predicted saving: {}".format(uncomp_len - comp_len - data_len)) # Somewhat meaningless comparison to zlib/gzip. all_input_bytes = "\\0".join(error_strings.keys()).encode() print() if hasattr(gzip, "compress"): gzip_len = len(gzip.compress(all_input_bytes)) + num_uses * 4 print("// gzip length: {}".format(gzip_len)) print("// Percentage of gzip: {:.1f}%".format(100 * (comp_len + data_len) / gzip_len)) if hasattr(zlib, "compress"): zlib_len = len(zlib.compress(all_input_bytes)) + num_uses * 4 print("// zlib length: {}".format(zlib_len)) print("// Percentage of zlib: {:.1f}%".format(100 * (comp_len + data_len) / zlib_len)) if __name__ == "__main__": main(sys.argv[1], word_compression) ================================================ FILE: py/makemoduledefs.py ================================================ #!/usr/bin/env python # This pre-processor parses provided objects' c files for # MP_REGISTER_MODULE(module_name, obj_module, enabled_define) # These are used to generate a header with the required entries for # "mp_rom_map_elem_t mp_builtin_module_table[]" in py/objmodule.c from __future__ import print_function import re import io import os import argparse pattern = re.compile(r"[\n;]\s*MP_REGISTER_MODULE\((.*?),\s*(.*?),\s*(.*?)\);", flags=re.DOTALL) def find_c_file(obj_file, vpath): """Search vpaths for the c file that matches the provided object_file. :param str obj_file: object file to find the matching c file for :param List[str] vpath: List of base paths, similar to gcc vpath :return: str path to c file or None """ c_file = None relative_c_file = os.path.splitext(obj_file)[0] + ".c" relative_c_file = relative_c_file.lstrip("/\\") for p in vpath: possible_c_file = os.path.join(p, relative_c_file) if os.path.exists(possible_c_file): c_file = possible_c_file break return c_file def find_module_registrations(c_file): """Find any MP_REGISTER_MODULE definitions in the provided c file. :param str c_file: path to c file to check :return: List[(module_name, obj_module, enabled_define)] """ global pattern if c_file is None: # No c file to match the object file, skip return set() with io.open(c_file, encoding="utf-8") as c_file_obj: return set(re.findall(pattern, c_file_obj.read())) def generate_module_table_header(modules): """Generate header with module table entries for builtin modules. :param List[(module_name, obj_module, enabled_define)] modules: module defs :return: None """ # Print header file for all external modules. mod_defs = [] print("// Automatically generated by makemoduledefs.py.\n") for module_name, obj_module, enabled_define in modules: mod_def = "MODULE_DEF_{}".format(module_name.upper()) mod_defs.append(mod_def) print( ( "#if ({enabled_define})\n" " extern const struct _mp_obj_module_t {obj_module};\n" " #define {mod_def} {{ MP_ROM_QSTR({module_name}), MP_ROM_PTR(&{obj_module}) }},\n" "#else\n" " #define {mod_def}\n" "#endif\n" ).format( module_name=module_name, obj_module=obj_module, enabled_define=enabled_define, mod_def=mod_def, ) ) print("\n#define MICROPY_REGISTERED_MODULES \\") for mod_def in mod_defs: print(" {mod_def} \\".format(mod_def=mod_def)) print("// MICROPY_REGISTERED_MODULES") def main(): parser = argparse.ArgumentParser() parser.add_argument( "--vpath", default=".", help="comma separated list of folders to search for c files in" ) parser.add_argument("files", nargs="*", help="list of c files to search") args = parser.parse_args() vpath = [p.strip() for p in args.vpath.split(",")] modules = set() for obj_file in args.files: c_file = find_c_file(obj_file, vpath) modules |= find_module_registrations(c_file) generate_module_table_header(sorted(modules)) if __name__ == "__main__": main() ================================================ FILE: py/makeqstrdata.py ================================================ """ Process raw qstr file and output qstr data with length, hash and data bytes. This script works with Python 2.6, 2.7, 3.3 and 3.4. """ from __future__ import print_function import re import sys # Python 2/3 compatibility: # - iterating through bytes is different # - codepoint2name lives in a different module import platform if platform.python_version_tuple()[0] == "2": bytes_cons = lambda val, enc=None: bytearray(val) from htmlentitydefs import codepoint2name elif platform.python_version_tuple()[0] == "3": bytes_cons = bytes from html.entities import codepoint2name # end compatibility code codepoint2name[ord("-")] = "hyphen" # add some custom names to map characters that aren't in HTML codepoint2name[ord(" ")] = "space" codepoint2name[ord("'")] = "squot" codepoint2name[ord(",")] = "comma" codepoint2name[ord(".")] = "dot" codepoint2name[ord(":")] = "colon" codepoint2name[ord(";")] = "semicolon" codepoint2name[ord("/")] = "slash" codepoint2name[ord("%")] = "percent" codepoint2name[ord("#")] = "hash" codepoint2name[ord("(")] = "paren_open" codepoint2name[ord(")")] = "paren_close" codepoint2name[ord("[")] = "bracket_open" codepoint2name[ord("]")] = "bracket_close" codepoint2name[ord("{")] = "brace_open" codepoint2name[ord("}")] = "brace_close" codepoint2name[ord("*")] = "star" codepoint2name[ord("!")] = "bang" codepoint2name[ord("\\")] = "backslash" codepoint2name[ord("+")] = "plus" codepoint2name[ord("$")] = "dollar" codepoint2name[ord("=")] = "equals" codepoint2name[ord("?")] = "question" codepoint2name[ord("@")] = "at_sign" codepoint2name[ord("^")] = "caret" codepoint2name[ord("|")] = "pipe" codepoint2name[ord("~")] = "tilde" # static qstrs, should be sorted static_qstr_list = [ "", "__dir__", # Put __dir__ after empty qstr for builtin dir() to work "\n", " ", "*", "/", "", "_", "__call__", "__class__", "__delitem__", "__enter__", "__exit__", "__getattr__", "__getitem__", "__hash__", "__init__", "__int__", "__iter__", "__len__", "__main__", "__module__", "__name__", "__new__", "__next__", "__qualname__", "__repr__", "__setitem__", "__str__", "ArithmeticError", "AssertionError", "AttributeError", "BaseException", "EOFError", "Ellipsis", "Exception", "GeneratorExit", "ImportError", "IndentationError", "IndexError", "KeyError", "KeyboardInterrupt", "LookupError", "MemoryError", "NameError", "NoneType", "NotImplementedError", "OSError", "OverflowError", "RuntimeError", "StopIteration", "SyntaxError", "SystemExit", "TypeError", "ValueError", "ZeroDivisionError", "abs", "all", "any", "append", "args", "bool", "builtins", "bytearray", "bytecode", "bytes", "callable", "chr", "classmethod", "clear", "close", "const", "copy", "count", "dict", "dir", "divmod", "end", "endswith", "eval", "exec", "extend", "find", "format", "from_bytes", "get", "getattr", "globals", "hasattr", "hash", "id", "index", "insert", "int", "isalpha", "isdigit", "isinstance", "islower", "isspace", "issubclass", "isupper", "items", "iter", "join", "key", "keys", "len", "list", "little", "locals", "lower", "lstrip", "main", "map", "micropython", "next", "object", "open", "ord", "pop", "popitem", "pow", "print", "range", "read", "readinto", "readline", "remove", "replace", "repr", "reverse", "rfind", "rindex", "round", "rsplit", "rstrip", "self", "send", "sep", "set", "setattr", "setdefault", "sort", "sorted", "split", "start", "startswith", "staticmethod", "step", "stop", "str", "strip", "sum", "super", "throw", "to_bytes", "tuple", "type", "update", "upper", "utf-8", "value", "values", "write", "zip", ] # this must match the equivalent function in qstr.c def compute_hash(qstr, bytes_hash): hash = 5381 for b in qstr: hash = (hash * 33) ^ b # Make sure that valid hash is never zero, zero means "hash not computed" return (hash & ((1 << (8 * bytes_hash)) - 1)) or 1 def qstr_escape(qst): def esc_char(m): c = ord(m.group(0)) try: name = codepoint2name[c] except KeyError: name = "0x%02x" % c return "_" + name + "_" return re.sub(r"[^A-Za-z0-9_]", esc_char, qst) def parse_input_headers(infiles): qcfgs = {} qstrs = {} # add static qstrs for qstr in static_qstr_list: # work out the corresponding qstr name ident = qstr_escape(qstr) # don't add duplicates assert ident not in qstrs # add the qstr to the list, with order number to retain original order in file order = len(qstrs) - 300000 qstrs[ident] = (order, ident, qstr) # read the qstrs in from the input files for infile in infiles: with open(infile, "rt") as f: for line in f: line = line.strip() # is this a config line? match = re.match(r"^QCFG\((.+), (.+)\)", line) if match: value = match.group(2) if value[0] == "(" and value[-1] == ")": # strip parenthesis from config value value = value[1:-1] qcfgs[match.group(1)] = value continue # is this a QSTR line? match = re.match(r"^Q\((.*)\)$", line) if not match: continue # get the qstr value qstr = match.group(1) # special cases to specify control characters if qstr == "\\n": qstr = "\n" elif qstr == "\\r\\n": qstr = "\r\n" # work out the corresponding qstr name ident = qstr_escape(qstr) # don't add duplicates if ident in qstrs: continue # add the qstr to the list, with order number to retain original order in file order = len(qstrs) # but put special method names like __add__ at the top of list, so # that their id's fit into a byte if ident == "": # Sort empty qstr above all still order = -200000 elif ident == "__dir__": # Put __dir__ after empty qstr for builtin dir() to work order = -190000 elif ident.startswith("__"): order -= 100000 qstrs[ident] = (order, ident, qstr) if not qcfgs: sys.stderr.write("ERROR: Empty preprocessor output - check for errors above\n") sys.exit(1) return qcfgs, qstrs def make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr): qbytes = bytes_cons(qstr, "utf8") qlen = len(qbytes) qhash = compute_hash(qbytes, cfg_bytes_hash) if all(32 <= ord(c) <= 126 and c != "\\" and c != '"' for c in qstr): # qstr is all printable ASCII so render it as-is (for easier debugging) qdata = qstr else: # qstr contains non-printable codes so render entire thing as hex pairs qdata = "".join(("\\x%02x" % b) for b in qbytes) if qlen >= (1 << (8 * cfg_bytes_len)): print("qstr is too long:", qstr) assert False qlen_str = ("\\x%02x" * cfg_bytes_len) % tuple( ((qlen >> (8 * i)) & 0xFF) for i in range(cfg_bytes_len) ) qhash_str = ("\\x%02x" * cfg_bytes_hash) % tuple( ((qhash >> (8 * i)) & 0xFF) for i in range(cfg_bytes_hash) ) return '(const byte*)"%s%s" "%s"' % (qhash_str, qlen_str, qdata) def print_qstr_data(qcfgs, qstrs): # get config variables cfg_bytes_len = int(qcfgs["BYTES_IN_LEN"]) cfg_bytes_hash = int(qcfgs["BYTES_IN_HASH"]) # print out the starter of the generated C header file print("// This file was automatically generated by makeqstrdata.py") print("") # add NULL qstr with no hash or data print( 'QDEF(MP_QSTRnull, (const byte*)"%s%s" "")' % ("\\x00" * cfg_bytes_hash, "\\x00" * cfg_bytes_len) ) # go through each qstr and print it out for order, ident, qstr in sorted(qstrs.values(), key=lambda x: x[0]): qbytes = make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr) print("QDEF(MP_QSTR_%s, %s)" % (ident, qbytes)) def do_work(infiles): qcfgs, qstrs = parse_input_headers(infiles) print_qstr_data(qcfgs, qstrs) if __name__ == "__main__": do_work(sys.argv[1:]) ================================================ FILE: py/makeqstrdefs.py ================================================ """ This script processes the output from the C preprocessor and extracts all qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'. This script works with Python 2.6, 2.7, 3.3 and 3.4. """ from __future__ import print_function import re import subprocess import sys import io import os # Extract MP_QSTR_FOO macros. _MODE_QSTR = "qstr" # Extract MP_COMPRESSED_ROM_TEXT("") macros. (Which come from MP_ERROR_TEXT) _MODE_COMPRESS = "compress" def preprocess(): if any(src in args.dependencies for src in args.changed_sources): sources = args.sources elif any(args.changed_sources): sources = args.changed_sources else: sources = args.sources csources = [] cxxsources = [] for source in sources: if source.endswith(".cpp"): cxxsources.append(source) else: csources.append(source) try: os.makedirs(os.path.dirname(args.output[0])) except OSError: pass with open(args.output[0], "w") as out_file: if csources: subprocess.check_call(args.pp + args.cflags + csources, stdout=out_file) if cxxsources: subprocess.check_call(args.pp + args.cxxflags + cxxsources, stdout=out_file) def write_out(fname, output): if output: for m, r in [("/", "__"), ("\\", "__"), (":", "@"), ("..", "@@")]: fname = fname.replace(m, r) with open(args.output_dir + "/" + fname + "." + args.mode, "w") as f: f.write("\n".join(output) + "\n") def process_file(f): re_line = re.compile(r"#[line]*\s\d+\s\"([^\"]+)\"") if args.mode == _MODE_QSTR: re_match = re.compile(r"MP_QSTR_[_a-zA-Z0-9]+") elif args.mode == _MODE_COMPRESS: re_match = re.compile(r'MP_COMPRESSED_ROM_TEXT\("([^"]*)"\)') output = [] last_fname = None for line in f: if line.isspace(): continue # match gcc-like output (# n "file") and msvc-like output (#line n "file") if line.startswith(("# ", "#line")): m = re_line.match(line) assert m is not None fname = m.group(1) if os.path.splitext(fname)[1] not in [".c", ".cpp"]: continue if fname != last_fname: write_out(last_fname, output) output = [] last_fname = fname continue for match in re_match.findall(line): if args.mode == _MODE_QSTR: name = match.replace("MP_QSTR_", "") output.append("Q(" + name + ")") elif args.mode == _MODE_COMPRESS: output.append(match) if last_fname: write_out(last_fname, output) return "" def cat_together(): import glob import hashlib hasher = hashlib.md5() all_lines = [] outf = open(args.output_dir + "/out", "wb") for fname in glob.glob(args.output_dir + "/*." + args.mode): with open(fname, "rb") as f: lines = f.readlines() all_lines += lines all_lines.sort() all_lines = b"\n".join(all_lines) outf.write(all_lines) outf.close() hasher.update(all_lines) new_hash = hasher.hexdigest() # print(new_hash) old_hash = None try: with open(args.output_file + ".hash") as f: old_hash = f.read() except IOError: pass mode_full = "QSTR" if args.mode == _MODE_COMPRESS: mode_full = "Compressed data" if old_hash != new_hash: print(mode_full, "updated") try: # rename below might fail if file exists os.remove(args.output_file) except: pass os.rename(args.output_dir + "/out", args.output_file) with open(args.output_file + ".hash", "w") as f: f.write(new_hash) else: print(mode_full, "not updated") if __name__ == "__main__": if len(sys.argv) < 6: print("usage: %s command mode input_filename output_dir output_file" % sys.argv[0]) sys.exit(2) class Args: pass args = Args() args.command = sys.argv[1] if args.command == "pp": named_args = { s: [] for s in [ "pp", "output", "cflags", "cxxflags", "sources", "changed_sources", "dependencies", ] } for arg in sys.argv[1:]: if arg in named_args: current_tok = arg else: named_args[current_tok].append(arg) if not named_args["pp"] or len(named_args["output"]) != 1: print("usage: %s %s ..." % (sys.argv[0], " ... ".join(named_args))) sys.exit(2) for k, v in named_args.items(): setattr(args, k, v) preprocess() sys.exit(0) args.mode = sys.argv[2] args.input_filename = sys.argv[3] # Unused for command=cat args.output_dir = sys.argv[4] args.output_file = None if len(sys.argv) == 5 else sys.argv[5] # Unused for command=split if args.mode not in (_MODE_QSTR, _MODE_COMPRESS): print("error: mode %s unrecognised" % sys.argv[2]) sys.exit(2) try: os.makedirs(args.output_dir) except OSError: pass if args.command == "split": with io.open(args.input_filename, encoding="utf-8") as infile: process_file(infile) if args.command == "cat": cat_together() ================================================ FILE: py/makeversionhdr.py ================================================ """ Generate header file with macros defining MicroPython version info. This script works with Python 2.6, 2.7, 3.3 and 3.4. """ from __future__ import print_function import sys import os import datetime import subprocess def get_version_info_from_git(): # Python 2.6 doesn't have check_output, so check for that try: subprocess.check_output subprocess.check_call except AttributeError: return None # Note: git describe doesn't work if no tag is available try: git_tag = subprocess.check_output( ["git", "describe", "--dirty", "--always", "--match", "v[1-9].*"], stderr=subprocess.STDOUT, universal_newlines=True, ).strip() except subprocess.CalledProcessError as er: if er.returncode == 128: # git exit code of 128 means no repository found return None git_tag = "" except OSError: return None try: git_hash = subprocess.check_output( ["git", "rev-parse", "--short", "HEAD"], stderr=subprocess.STDOUT, universal_newlines=True, ).strip() except subprocess.CalledProcessError: git_hash = "unknown" except OSError: return None try: # Check if there are any modified files. subprocess.check_call( ["git", "diff", "--no-ext-diff", "--quiet", "--exit-code"], stderr=subprocess.STDOUT ) # Check if there are any staged files. subprocess.check_call( ["git", "diff-index", "--cached", "--quiet", "HEAD", "--"], stderr=subprocess.STDOUT ) except subprocess.CalledProcessError: git_hash += "-dirty" except OSError: return None return git_tag, git_hash def get_version_info_from_docs_conf(): with open(os.path.join(os.path.dirname(sys.argv[0]), "..", "docs", "conf.py")) as f: for line in f: if line.startswith("version = release = '"): ver = line.strip().split(" = ")[2].strip("'") git_tag = "v" + ver return git_tag, "" return None def make_version_header(filename): # Get version info using git, with fallback to docs/conf.py info = get_version_info_from_git() if info is None: info = get_version_info_from_docs_conf() git_tag, git_hash = info # Generate the file with the git and version info file_data = """\ // This file was generated by py/makeversionhdr.py #define MICROPY_GIT_TAG "%s" #define MICROPY_GIT_HASH "%s" #define MICROPY_BUILD_DATE "%s" """ % ( git_tag, git_hash, datetime.date.today().strftime("%Y-%m-%d"), ) # Check if the file contents changed from last time write_file = True if os.path.isfile(filename): with open(filename, "r") as f: existing_data = f.read() if existing_data == file_data: write_file = False # Only write the file if we need to if write_file: print("GEN %s" % filename) with open(filename, "w") as f: f.write(file_data) if __name__ == "__main__": make_version_header(sys.argv[1]) ================================================ FILE: py/malloc.c ================================================ /* * 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, %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_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 ================================================ FILE: py/map.c ================================================ /* * 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 // 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 *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); // 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_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 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; 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 } 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 ================================================ FILE: py/misc.h ================================================ /* * 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)])) /** 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_type, var_num) ((obj_type *)m_malloc(sizeof(obj_type) + sizeof(var_type) * (var_num))) #define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type *)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num))) #if MICROPY_ENABLE_FINALISER #define m_new_obj_with_finaliser(type) ((type *)(m_malloc_with_finaliser(sizeof(type)))) #define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type *)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num))) #else #define m_new_obj_with_finaliser(type) m_new_obj(type) #define m_new_obj_var_with_finaliser(type, var_type, var_num) m_new_obj_var(type, var_type, var_num) #endif #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_type, var_num, ptr) (m_free(ptr, sizeof(obj_type) + 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_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_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 : 1; } 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_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_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 #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 { #ifdef __clang__ // Fix "error: empty struct has size 0 in C, size 1 in C++". char dummy; #endif } *mp_rom_error_text_t; #include inline __attribute__((always_inline)) 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 ================================================ FILE: py/mkenv.mk ================================================ ifneq ($(lastword a b),b) $(error These Makefiles require make 3.81 or newer) endif # Set TOP to be the path to get from the current directory (where make was # invoked) to the top of the tree. $(lastword $(MAKEFILE_LIST)) returns # the name of this makefile relative to where make was invoked. # # We assume that this file is in the py directory so we use $(dir ) twice # to get to the top of the tree. THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST)) TOP := $(patsubst %/py/mkenv.mk,%,$(THIS_MAKEFILE)) # Turn on increased build verbosity by defining BUILD_VERBOSE in your main # Makefile or in your environment. You can also use V=1 on the make command # line. ifeq ("$(origin V)", "command line") BUILD_VERBOSE=$(V) endif ifndef BUILD_VERBOSE BUILD_VERBOSE = 0 endif ifeq ($(BUILD_VERBOSE),0) Q = @ else Q = endif # Since this is a new feature, advertise it ifeq ($(BUILD_VERBOSE),0) $(info Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.) endif # default settings; can be overridden in main Makefile PY_SRC ?= $(TOP)/py BUILD ?= build RM = rm ECHO = @echo CP = cp MKDIR = mkdir SED = sed CAT = cat TOUCH = touch PYTHON = python3 AS = $(CROSS_COMPILE)as CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ GDB = $(CROSS_COMPILE)gdb LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy SIZE = $(CROSS_COMPILE)size STRIP = $(CROSS_COMPILE)strip AR = $(CROSS_COMPILE)ar MAKE_MANIFEST = $(PYTHON) $(TOP)/tools/makemanifest.py MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py MPY_CROSS = $(TOP)/mpy-cross/mpy-cross MPY_TOOL = $(PYTHON) $(TOP)/tools/mpy-tool.py MPY_LIB_DIR = $(TOP)/../micropython-lib all: .PHONY: all .DELETE_ON_ERROR: MKENV_INCLUDED = 1 ================================================ FILE: py/mkrules.mk ================================================ ifneq ($(MKENV_INCLUDED),1) # We assume that mkenv is in the same directory as this file. THIS_MAKEFILE = $(lastword $(MAKEFILE_LIST)) include $(dir $(THIS_MAKEFILE))mkenv.mk endif # Extra deps that need to happen before object compilation. OBJ_EXTRA_ORDER_DEPS = ifeq ($(MICROPY_ROM_TEXT_COMPRESSION),1) # If compression is enabled, trigger the build of compressed.data.h... OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/compressed.data.h # ...and enable the MP_COMPRESSED_ROM_TEXT macro (used by MP_ERROR_TEXT). CFLAGS += -DMICROPY_ROM_TEXT_COMPRESSION=1 endif # QSTR generation uses the same CFLAGS, with these modifications. QSTR_GEN_FLAGS = -DNO_QSTR -I$(BUILD)/tmp # Note: := to force evalulation immediately. QSTR_GEN_CFLAGS := $(CFLAGS) QSTR_GEN_CFLAGS += $(QSTR_GEN_FLAGS) QSTR_GEN_CXXFLAGS := $(CXXFLAGS) QSTR_GEN_CXXFLAGS += $(QSTR_GEN_FLAGS) # This file expects that OBJ contains a list of all of the object files. # The directory portion of each object file is used to locate the source # and should not contain any ..'s but rather be relative to the top of the # tree. # # So for example, py/map.c would have an object file name py/map.o # The object files will go into the build directory and mantain the same # directory structure as the source tree. So the final dependency will look # like this: # # build/py/map.o: py/map.c # # We set vpath to point to the top of the tree so that the source files # can be located. By following this scheme, it allows a single build rule # to be used to compile all .c files. vpath %.S . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.S $(ECHO) "CC $<" $(Q)$(CC) $(CFLAGS) -c -o $@ $< vpath %.s . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.s $(ECHO) "AS $<" $(Q)$(AS) -o $@ $< define compile_c $(ECHO) "CC $<" $(Q)$(CC) $(CFLAGS) -c -MD -o $@ $< @# The following fixes the dependency file. @# See http://make.paulandlesley.org/autodep.html for details. @# Regex adjusted from the above to play better with Windows paths, etc. @$(CP) $(@:.o=.d) $(@:.o=.P); \ $(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \ $(RM) -f $(@:.o=.d) endef define compile_cxx $(ECHO) "CXX $<" $(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< @# The following fixes the dependency file. @# See http://make.paulandlesley.org/autodep.html for details. @# Regex adjusted from the above to play better with Windows paths, etc. @$(CP) $(@:.o=.d) $(@:.o=.P); \ $(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \ $(RM) -f $(@:.o=.d) endef vpath %.c . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.c $(call compile_c) vpath %.c . $(TOP) $(USER_C_MODULES) vpath %.cpp . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.cpp $(call compile_cxx) $(BUILD)/%.pp: %.c $(ECHO) "PreProcess $<" $(Q)$(CPP) $(CFLAGS) -Wp,-C,-dD,-dI -o $@ $< # The following rule uses | to create an order only prerequisite. Order only # prerequisites only get built if they don't exist. They don't cause timestamp # checking to be performed. # # We don't know which source files actually need the generated.h (since # it is #included from str.h). The compiler generated dependencies will cause # the right .o's to get recompiled if the generated.h file changes. Adding # an order-only dependency to all of the .o's will cause the generated .h # to get built before we try to compile any of them. $(OBJ): | $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/mpversion.h $(OBJ_EXTRA_ORDER_DEPS) # The logic for qstr regeneration (applied by makeqstrdefs.py) is: # - if anything in QSTR_GLOBAL_DEPENDENCIES is newer, then process all source files ($^) # - else, if list of newer prerequisites ($?) is not empty, then process just these ($?) # - else, process all source files ($^) [this covers "make -B" which can set $? to empty] # See more information about this process in docs/develop/qstr.rst. $(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(QSTR_GLOBAL_REQUIREMENTS) $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py pp $(CPP) output $(HEADER_BUILD)/qstr.i.last cflags $(QSTR_GEN_CFLAGS) cxxflags $(QSTR_GEN_CXXFLAGS) sources $^ dependencies $(QSTR_GLOBAL_DEPENDENCIES) changed_sources $? $(HEADER_BUILD)/qstr.split: $(HEADER_BUILD)/qstr.i.last $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py split qstr $< $(HEADER_BUILD)/qstr _ $(Q)$(TOUCH) $@ $(QSTR_DEFS_COLLECTED): $(HEADER_BUILD)/qstr.split $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat qstr _ $(HEADER_BUILD)/qstr $@ # Compressed error strings. $(HEADER_BUILD)/compressed.split: $(HEADER_BUILD)/qstr.i.last $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py split compress $< $(HEADER_BUILD)/compress _ $(Q)$(TOUCH) $@ $(HEADER_BUILD)/compressed.collected: $(HEADER_BUILD)/compressed.split $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat compress _ $(HEADER_BUILD)/compress $@ # $(sort $(var)) removes duplicates # # The net effect of this, is it causes the objects to depend on the # object directories (but only for existence), and the object directories # will be created if they don't exist. OBJ_DIRS = $(sort $(dir $(OBJ))) $(OBJ): | $(OBJ_DIRS) $(OBJ_DIRS): $(MKDIR) -p $@ $(HEADER_BUILD): $(MKDIR) -p $@ ifneq ($(FROZEN_MANIFEST),) # to build frozen_content.c from a manifest $(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h $(Q)$(MAKE_MANIFEST) -o $@ -v "MPY_DIR=$(TOP)" -v "MPY_LIB_DIR=$(MPY_LIB_DIR)" -v "PORT_DIR=$(shell pwd)" -v "BOARD_DIR=$(BOARD_DIR)" -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) $(FROZEN_MANIFEST) ifneq ($(FROZEN_DIR),) $(error FROZEN_DIR cannot be used in conjunction with FROZEN_MANIFEST) endif ifneq ($(FROZEN_MPY_DIR),) $(error FROZEN_MPY_DIR cannot be used in conjunction with FROZEN_MANIFEST) endif endif ifneq ($(FROZEN_DIR),) $(info Warning: FROZEN_DIR is deprecated in favour of FROZEN_MANIFEST) $(BUILD)/frozen.c: $(wildcard $(FROZEN_DIR)/*) $(HEADER_BUILD) $(FROZEN_EXTRA_DEPS) $(ECHO) "GEN $@" $(Q)$(MAKE_FROZEN) $(FROZEN_DIR) > $@ endif ifneq ($(FROZEN_MPY_DIR),) $(info Warning: FROZEN_MPY_DIR is deprecated in favour of FROZEN_MANIFEST) # make a list of all the .py files that need compiling and freezing FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | $(SED) -e 's=^$(FROZEN_MPY_DIR)/==') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) # to build .mpy files from .py files $(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py @$(ECHO) "MPY $<" $(Q)$(MKDIR) -p $(dir $@) $(Q)$(MPY_CROSS) -o $@ -s $(<:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $< # to build frozen_mpy.c from all .mpy files $(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h @$(ECHO) "GEN $@" $(Q)$(MPY_TOOL) -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h $(FROZEN_MPY_MPY_FILES) > $@ endif ifneq ($(PROG),) # Build a standalone executable (unix does this) all: $(PROG) $(PROG): $(OBJ) $(ECHO) "LINK $@" # Do not pass COPT here - it's *C* compiler optimizations. For example, # we may want to compile using Thumb, but link with non-Thumb libc. $(Q)$(CC) -o $@ $^ $(LIB) $(LDFLAGS) ifndef DEBUG $(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $(PROG) endif $(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $(PROG) clean: clean-prog clean-prog: $(RM) -f $(PROG) $(RM) -f $(PROG).map .PHONY: clean-prog endif submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) $(Q)git submodule update --init $(addprefix $(TOP)/,$(GIT_SUBMODULES)) endif .PHONY: submodules LIBMICROPYTHON = libmicropython.a # We can execute extra commands after library creation using # LIBMICROPYTHON_EXTRA_CMD. This may be needed e.g. to integrate # with 3rd-party projects which don't have proper dependency # tracking. Then LIBMICROPYTHON_EXTRA_CMD can e.g. touch some # other file to cause needed effect, e.g. relinking with new lib. lib $(LIBMICROPYTHON): $(OBJ) $(AR) rcs $(LIBMICROPYTHON) $^ $(LIBMICROPYTHON_EXTRA_CMD) clean: $(RM) -rf $(BUILD) $(CLEAN_EXTRA) .PHONY: clean # Clean every non-git file from FROZEN_DIR/FROZEN_MPY_DIR, but making a backup. # We run rmdir below to avoid empty backup dir (it will silently fail if backup # is non-empty). clean-frozen: if [ -n "$(FROZEN_MPY_DIR)" ]; then \ backup_dir=$(FROZEN_MPY_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \ cd $(FROZEN_MPY_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \ | xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \ rmdir ../$$backup_dir 2>/dev/null || true; \ git clean -d -f .; \ fi if [ -n "$(FROZEN_DIR)" ]; then \ backup_dir=$(FROZEN_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \ cd $(FROZEN_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \ | xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \ rmdir ../$$backup_dir 2>/dev/null || true; \ git clean -d -f .; \ fi .PHONY: clean-frozen print-cfg: $(ECHO) "PY_SRC = $(PY_SRC)" $(ECHO) "BUILD = $(BUILD)" $(ECHO) "OBJ = $(OBJ)" .PHONY: print-cfg print-def: @$(ECHO) "The following defines are built into the $(CC) compiler" $(TOUCH) __empty__.c @$(CC) -E -Wp,-dM __empty__.c @$(RM) -f __empty__.c -include $(OBJ:.o=.P) ================================================ FILE: py/modarray.c ================================================ /* * 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_uarray) }, { 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_uarray = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&mp_module_array_globals, }; MP_REGISTER_MODULE(MP_QSTR_uarray, mp_module_uarray, MICROPY_PY_ARRAY); #endif ================================================ FILE: py/modbuiltins.c ================================================ /* * 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 neede 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); uint8_t str[4]; int len = 0; if (c < 0x80) { *str = c; len = 1; } else if (c < 0x800) { str[0] = (c >> 6) | 0xC0; str[1] = (c & 0x3F) | 0x80; len = 2; } else if (c < 0x10000) { str[0] = (c >> 12) | 0xE0; str[1] = ((c >> 6) & 0x3F) | 0x80; str[2] = (c & 0x3F) | 0x80; len = 3; } else if (c < 0x110000) { str[0] = (c >> 18) | 0xF0; str[1] = ((c >> 12) & 0x3F) | 0x80; str[2] = ((c >> 6) & 0x3F) | 0x80; str[3] = (c & 0x3F) | 0x80; len = 4; } else { mp_raise_ValueError(MP_ERROR_TEXT("chr() arg not in range(0x110000)")); } return mp_obj_new_str_via_qstr((char *)str, 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 "lib/mp-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(&mp_type_str, &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_type(&mp_type_StopIteration); } 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_type(&mp_type_StopIteration); } 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_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_vstr(&mp_type_str, &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_type_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) }, { 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 }; 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, }; ================================================ FILE: py/modcmath.c ================================================ /* * 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, }; #endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_CMATH ================================================ FILE: py/modcollections.c ================================================ /* * 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_ucollections) }, #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, }; #endif // MICROPY_PY_COLLECTIONS ================================================ FILE: py/modgc.c ================================================ /* * 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); return MP_OBJ_NEW_SMALL_INT(info.free); } 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, }; #endif ================================================ FILE: py/modio.c ================================================ /* * 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 extern const mp_obj_type_t mp_type_fileio; extern const mp_obj_type_t mp_type_textio; #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 const mp_obj_type_t mp_type_iobase = { { &mp_type_type }, .name = MP_QSTR_IOBase, .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 = m_new_obj_var(mp_obj_bufwriter_t, byte, alloc); o->base.type = 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 const mp_obj_type_t bufwriter_type = { { &mp_type_type }, .name = MP_QSTR_BufferedWriter, .make_new = bufwriter_make_new, .protocol = &bufwriter_stream_p, .locals_dict = (mp_obj_dict_t *)&bufwriter_locals_dict, }; #endif // MICROPY_PY_IO_BUFFEREDWRITER #if MICROPY_PY_IO_RESOURCE_STREAM STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) { VSTR_FIXED(path_buf, MICROPY_ALLOC_PATH_MAX); size_t len; // As an extension to pkg_resources.resource_stream(), we support // package parameter being None, the path_in is interpreted as a // raw path. if (package_in != mp_const_none) { // Pass "True" as sentinel value in fromlist to force returning of leaf module mp_obj_t pkg = mp_import_name(mp_obj_str_get_qstr(package_in), mp_const_true, MP_OBJ_NEW_SMALL_INT(0)); mp_obj_t dest[2]; mp_load_method_maybe(pkg, MP_QSTR___path__, dest); if (dest[0] == MP_OBJ_NULL) { mp_raise_TypeError(NULL); } const char *path = mp_obj_str_get_data(dest[0], &len); vstr_add_strn(&path_buf, path, len); vstr_add_byte(&path_buf, '/'); } const char *path = mp_obj_str_get_data(path_in, &len); vstr_add_strn(&path_buf, path, len); len = path_buf.len; const char *data = mp_find_frozen_str(path_buf.buf, &len); if (data != NULL) { mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t); o->base.type = &mp_type_bytesio; o->vstr = m_new_obj(vstr_t); vstr_init_fixed_buf(o->vstr, len + 1, (char *)data); o->vstr->len = len; o->pos = 0; return MP_OBJ_FROM_PTR(o); } mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len); return mp_builtin_open(1, &path_out, (mp_map_t *)&mp_const_empty_map); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream); #endif STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uio) }, // 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 #if MICROPY_PY_IO_RESOURCE_STREAM { MP_ROM_QSTR(MP_QSTR_resource_stream), MP_ROM_PTR(&resource_stream_obj) }, #endif #if MICROPY_PY_IO_FILEIO { MP_ROM_QSTR(MP_QSTR_FileIO), MP_ROM_PTR(&mp_type_fileio) }, #if MICROPY_CPYTHON_COMPAT { MP_ROM_QSTR(MP_QSTR_TextIOWrapper), MP_ROM_PTR(&mp_type_textio) }, #endif #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(&bufwriter_type) }, #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, }; #endif ================================================ FILE: py/modmath.c ================================================ /* * 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))) { 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_a, ARG_b, ARG_rel_tol, ARG_abs_tol }; static const mp_arg_t allowed_args[] = { {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {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, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); const mp_float_t a = mp_obj_get_float(args[ARG_a].u_obj); const mp_float_t b = mp_obj_get_float(args[ARG_b].u_obj); 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 }, { 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, }; #endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH ================================================ FILE: py/modmicropython.c ================================================ /* * 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" // 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(); if (n_args == 1) { // arg given means dump gc allocation table gc_dump_alloc_table(); } #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_MEM(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_MEM(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, }; ================================================ FILE: py/modstruct.c ================================================ /* * 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 == '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 == '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 == '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_str_from_vstr(&mp_type_bytes, &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_ustruct) }, { 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_ustruct = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&mp_module_struct_globals, }; #endif ================================================ FILE: py/modsys.c ================================================ /* * 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/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" #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"); // version_info - Python language version that this implementation conforms to, as a tuple of ints #define I(n) MP_OBJ_NEW_SMALL_INT(n) // TODO: CPython is now at 5-element array, but save 2 els so far... STATIC const mp_obj_tuple_t mp_sys_version_info_obj = {{&mp_type_tuple}, 3, {I(3), I(4), I(0)}}; // sys.implementation object // this holds the MicroPython version STATIC const mp_obj_tuple_t mp_sys_implementation_version_info_obj = { {&mp_type_tuple}, 3, { I(MICROPY_VERSION_MAJOR), I(MICROPY_VERSION_MINOR), I(MICROPY_VERSION_MICRO) } }; #if MICROPY_PERSISTENT_CODE_LOAD #define SYS_IMPLEMENTATION_ELEMS \ MP_ROM_QSTR(MP_QSTR_micropython), \ MP_ROM_PTR(&mp_sys_implementation_version_info_obj), \ MP_ROM_INT(MPY_FILE_HEADER_INT) #else #define SYS_IMPLEMENTATION_ELEMS \ MP_ROM_QSTR(MP_QSTR_micropython), \ MP_ROM_PTR(&mp_sys_implementation_version_info_obj) #endif #if MICROPY_PY_ATTRTUPLE STATIC const qstr impl_fields[] = { MP_QSTR_name, MP_QSTR_version, #if MICROPY_PERSISTENT_CODE_LOAD MP_QSTR_mpy, #endif }; STATIC MP_DEFINE_ATTRTUPLE( mp_sys_implementation_obj, impl_fields, 2 + MICROPY_PERSISTENT_CODE_LOAD, SYS_IMPLEMENTATION_ELEMS ); #else STATIC const mp_rom_obj_tuple_t mp_sys_implementation_obj = { {&mp_type_tuple}, 2 + MICROPY_PERSISTENT_CODE_LOAD, { SYS_IMPLEMENTATION_ELEMS } }; #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 // 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) { mp_obj_t exc; if (n_args == 0) { exc = mp_obj_new_exception(&mp_type_SystemExit); } else { exc = mp_obj_new_exception_arg1(&mp_type_SystemExit, args[0]); } nlr_raise(exc); } 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 STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) }, { MP_ROM_QSTR(MP_QSTR_path), MP_ROM_PTR(&MP_STATE_VM(mp_sys_path_obj)) }, { MP_ROM_QSTR(MP_QSTR_argv), MP_ROM_PTR(&MP_STATE_VM(mp_sys_argv_obj)) }, { 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_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 /* * 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, }; #endif ================================================ FILE: py/modthread.c ================================================ /* * 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 = m_new_obj(mp_obj_thread_lock_t); self->base.type = &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 const mp_obj_type_t mp_type_thread_lock = { { &mp_type_type }, .name = MP_QSTR_lock, .locals_dict = (mp_obj_dict_t *)&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((uintptr_t)mp_thread_get_state()); } 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_set_state(&ts); mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan mp_stack_set_limit(args->stack_size); #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 // set locals and globals from the calling context mp_locals_set(args->dict_locals); mp_globals_set(args->dict_globals); MP_THREAD_GIL_ENTER(); // signal that we are set up and running mp_thread_start(); // TODO set more thread-specific state here: // mp_pending_exception? (root pointer) // 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, 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, 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! mp_thread_create(thread_entry, th_args, &th_args->stack_size); return mp_const_none; } 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, }; #endif // MICROPY_PY_THREAD ================================================ FILE: py/moduerrno.c ================================================ /* * 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_UERRNO // 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_UERRNO_LIST #define MICROPY_PY_UERRNO_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_UERRNO_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_UERRNO_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_uerrno_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uerrno) }, #if MICROPY_PY_UERRNO_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_UERRNO_LIST #undef X }; STATIC MP_DEFINE_CONST_DICT(mp_module_uerrno_globals, mp_module_uerrno_globals_table); const mp_obj_module_t mp_module_uerrno = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&mp_module_uerrno_globals, }; qstr mp_errno_to_str(mp_obj_t errno_val) { #if MICROPY_PY_UERRNO_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_uerrno_globals_table); ++i) { if (errno_val == mp_module_uerrno_globals_table[i].value) { return MP_OBJ_QSTR_VALUE(mp_module_uerrno_globals_table[i].key); } } return MP_QSTRnull; #endif } #endif // MICROPY_PY_UERRNO ================================================ FILE: py/mpconfig.h ================================================ /* * 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 #define MICROPY_VERSION_MAJOR 1 #define MICROPY_VERSION_MINOR 13 #define MICROPY_VERSION_MICRO 0 // Combined version as a 32-bit number for convenience #define MICROPY_VERSION ( \ MICROPY_VERSION_MAJOR << 16 \ | MICROPY_VERSION_MINOR << 8 \ | MICROPY_VERSION_MICRO) // String version #define MICROPY_VERSION_STRING \ MP_STRINGIFY(MICROPY_VERSION_MAJOR) "." \ MP_STRINGIFY(MICROPY_VERSION_MINOR) "." \ MP_STRINGIFY(MICROPY_VERSION_MICRO) // 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. #ifdef MP_CONFIGFILE #include MP_CONFIGFILE #else #include #endif // 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 * BYTES_PER_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 (1) #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 // 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 #define MICROPY_QSTR_BYTES_IN_HASH (2) #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 #ifndef MICROPY_PERSISTENT_CODE_SAVE #define MICROPY_PERSISTENT_CODE_SAVE (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 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 enable the thumb inline assembler #ifndef MICROPY_EMIT_INLINE_THUMB #define MICROPY_EMIT_INLINE_THUMB (0) #endif // Whether to enable ARMv7-M instruction support in the Thumb2 inline assembler #ifndef MICROPY_EMIT_INLINE_THUMB_ARMV7M #define MICROPY_EMIT_INLINE_THUMB_ARMV7M (1) #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) // Select prelude-as-bytes-object for certain emitters #define MICROPY_EMIT_NATIVE_PRELUDE_AS_BYTES_OBJ (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 (1) #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 // Configure dynamic compiler macros #if MICROPY_DYNAMIC_COMPILER #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC (mp_dynamic_compiler.opt_cache_map_lookup_in_bytecode) #define MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC (mp_dynamic_compiler.py_builtins_str_unicode) #else #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE #define MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC MICROPY_PY_BUILTINS_STR_UNICODE #endif // Whether to enable constant folding; eg 1+2 rewritten as 3 #ifndef MICROPY_COMP_CONST_FOLDING #define MICROPY_COMP_CONST_FOLDING (1) #endif // Whether to enable optimisations for constant literals, eg OrderedDict #ifndef MICROPY_COMP_CONST_LITERAL #define MICROPY_COMP_CONST_LITERAL (1) #endif // Whether to enable lookup of constants in modules; eg module.CONST #ifndef MICROPY_COMP_MODULE_CONST #define MICROPY_COMP_MODULE_CONST (0) #endif // Whether to enable constant optimisation; id = const(value) #ifndef MICROPY_COMP_CONST #define MICROPY_COMP_CONST (1) #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 (1) #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 (0) #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 (0) #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 /*****************************************************************************/ /* 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 // 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 // Whether to cache result of map lookups in LOAD_NAME, LOAD_GLOBAL, LOAD_ATTR, // STORE_ATTR bytecodes. Uses 1 byte extra RAM for each of these opcodes and // uses a bit of extra code ROM, but greatly improves lookup speed. #ifndef MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (0) #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 (0) #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 (0) #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 (1) #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 // Whether to include the garbage collector #ifndef MICROPY_ENABLE_GC #define MICROPY_ENABLE_GC (0) #endif // Whether to enable finalisers in the garbage collector (ie call __del__) #ifndef MICROPY_ENABLE_FINALISER #define MICROPY_ENABLE_FINALISER (0) #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 (0) #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 (0) #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 (0) #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 (0) #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 (0) #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 (0) #endif // Whether to implement auto-indent in REPL #ifndef MICROPY_REPL_AUTO_INDENT #define MICROPY_REPL_AUTO_INDENT (0) #endif // Whether port requires event-driven REPL functions #ifndef MICROPY_REPL_EVENT_DRIVEN #define MICROPY_REPL_EVENT_DRIVEN (0) #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 (0) #endif // Whether to include doc strings (increases RAM usage) #ifndef MICROPY_ENABLE_DOC_STRING #define MICROPY_ENABLE_DOC_STRING (0) #endif // 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 #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) #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 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 (0) #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 (1) #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 (1) #endif // Whether POSIX-semantics non-blocking streams are supported #ifndef MICROPY_STREAMS_NON_BLOCK #define MICROPY_STREAMS_NON_BLOCK (0) #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 to call __init__ when importing builtin modules for the first time #ifndef MICROPY_MODULE_BUILTIN_INIT #define MICROPY_MODULE_BUILTIN_INIT (0) #endif // Whether to support module-level __getattr__ (see PEP 562) #ifndef MICROPY_MODULE_GETATTR #define MICROPY_MODULE_GETATTR (1) #endif // Whether module weak links are supported #ifndef MICROPY_MODULE_WEAK_LINKS #define MICROPY_MODULE_WEAK_LINKS (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 (0) #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 (1) #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 // Support for internal scheduler #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (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 #define MICROPY_VFS_POSIX (0) #endif // Support for VFS FAT component, to mount a FAT filesystem within VFS #ifndef MICROPY_VFS #define MICROPY_VFS_FAT (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 (1) #endif // Whether to implement attributes on functions #ifndef MICROPY_PY_FUNCTION_ATTRS #define MICROPY_PY_FUNCTION_ATTRS (0) #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 (0) #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 (0) #endif // Support for async/await/async for/async with #ifndef MICROPY_PY_ASYNC_AWAIT #define MICROPY_PY_ASYNC_AWAIT (1) #endif // Support for assignment expressions with := (see PEP 572, Python 3.8+) #ifndef MICROPY_PY_ASSIGN_EXPR #define MICROPY_PY_ASSIGN_EXPR (1) #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 (1) #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 // Whether str object is proper unicode #ifndef MICROPY_PY_BUILTINS_STR_UNICODE #define MICROPY_PY_BUILTINS_STR_UNICODE (0) #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 (0) #endif // Whether str.count() method provided #ifndef MICROPY_PY_BUILTINS_STR_COUNT #define MICROPY_PY_BUILTINS_STR_COUNT (1) #endif // Whether str % (...) formatting operator provided #ifndef MICROPY_PY_BUILTINS_STR_OP_MODULO #define MICROPY_PY_BUILTINS_STR_OP_MODULO (1) #endif // Whether str.partition()/str.rpartition() method provided #ifndef MICROPY_PY_BUILTINS_STR_PARTITION #define MICROPY_PY_BUILTINS_STR_PARTITION (0) #endif // Whether str.splitlines() method provided #ifndef MICROPY_PY_BUILTINS_STR_SPLITLINES #define MICROPY_PY_BUILTINS_STR_SPLITLINES (0) #endif // Whether to support bytearray object #ifndef MICROPY_PY_BUILTINS_BYTEARRAY #define MICROPY_PY_BUILTINS_BYTEARRAY (1) #endif // Whether to support dict.fromkeys() class method #ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS #define MICROPY_PY_BUILTINS_DICT_FROMKEYS (1) #endif // Whether to support memoryview object #ifndef MICROPY_PY_BUILTINS_MEMORYVIEW #define MICROPY_PY_BUILTINS_MEMORYVIEW (0) #endif // Whether to support memoryview.itemsize attribute #ifndef MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE #define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (0) #endif // Whether to support set object #ifndef MICROPY_PY_BUILTINS_SET #define MICROPY_PY_BUILTINS_SET (1) #endif // Whether to support slice subscript operators and slice object #ifndef MICROPY_PY_BUILTINS_SLICE #define MICROPY_PY_BUILTINS_SLICE (1) #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 (0) #endif // Whether to support the .indices(len) method on slice objects #ifndef MICROPY_PY_BUILTINS_SLICE_INDICES #define MICROPY_PY_BUILTINS_SLICE_INDICES (0) #endif // Whether to support frozenset object #ifndef MICROPY_PY_BUILTINS_FROZENSET #define MICROPY_PY_BUILTINS_FROZENSET (0) #endif // Whether to support property object #ifndef MICROPY_PY_BUILTINS_PROPERTY #define MICROPY_PY_BUILTINS_PROPERTY (1) #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 (1) #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 (0) #endif // Support for callling next() with second argument #ifndef MICROPY_PY_BUILTINS_NEXT2 #define MICROPY_PY_BUILTINS_NEXT2 (0) #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 (0) #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 (0) #endif // Whether to support all inplace arithmetic operarion methods // (__imul__, etc.) #ifndef MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS #define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (0) #endif // Whether to support reverse arithmetic operarion methods // (__radd__, etc.). Additionally gated by // MICROPY_PY_ALL_SPECIAL_METHODS. #ifndef MICROPY_PY_REVERSE_SPECIAL_METHODS #define MICROPY_PY_REVERSE_SPECIAL_METHODS (0) #endif // Whether to support compile function #ifndef MICROPY_PY_BUILTINS_COMPILE #define MICROPY_PY_BUILTINS_COMPILE (0) #endif // Whether to support enumerate function(type) #ifndef MICROPY_PY_BUILTINS_ENUMERATE #define MICROPY_PY_BUILTINS_ENUMERATE (1) #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 (0) #endif // Whether to support filter function(type) #ifndef MICROPY_PY_BUILTINS_FILTER #define MICROPY_PY_BUILTINS_FILTER (1) #endif // Whether to support reversed function(type) #ifndef MICROPY_PY_BUILTINS_REVERSED #define MICROPY_PY_BUILTINS_REVERSED (1) #endif // Whether to define "NotImplemented" special constant #ifndef MICROPY_PY_BUILTINS_NOTIMPLEMENTED #define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (0) #endif // Whether to provide the built-in input() function. The implementation of this // uses mp-readline, so can only be enabled if the port uses this readline. #ifndef MICROPY_PY_BUILTINS_INPUT #define MICROPY_PY_BUILTINS_INPUT (0) #endif // Whether to support min/max functions #ifndef MICROPY_PY_BUILTINS_MIN_MAX #define MICROPY_PY_BUILTINS_MIN_MAX (1) #endif // Support for calls to pow() with 3 integer arguments #ifndef MICROPY_PY_BUILTINS_POW3 #define MICROPY_PY_BUILTINS_POW3 (0) #endif // Whether to provide the help function #ifndef MICROPY_PY_BUILTINS_HELP #define MICROPY_PY_BUILTINS_HELP (0) #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 (0) #endif // Whether to set __file__ for imported modules #ifndef MICROPY_PY___FILE__ #define MICROPY_PY___FILE__ (1) #endif // Whether to provide mem-info related functions in micropython module #ifndef MICROPY_PY_MICROPYTHON_MEM_INFO #define MICROPY_PY_MICROPYTHON_MEM_INFO (0) #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 (0) #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 (1) #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 (0) #endif // Whether to support attrtuple type (MicroPython extension) // It provides space-efficient tuples with attribute access #ifndef MICROPY_PY_ATTRTUPLE #define MICROPY_PY_ATTRTUPLE (1) #endif // Whether to provide "collections" module #ifndef MICROPY_PY_COLLECTIONS #define MICROPY_PY_COLLECTIONS (1) #endif // Whether to provide "ucollections.deque" type #ifndef MICROPY_PY_COLLECTIONS_DEQUE #define MICROPY_PY_COLLECTIONS_DEQUE (0) #endif // Whether to provide "collections.OrderedDict" type #ifndef MICROPY_PY_COLLECTIONS_ORDEREDDICT #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (0) #endif // Whether to provide the _asdict function for namedtuple #ifndef MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT #define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (0) #endif // Whether to provide "math" module #ifndef MICROPY_PY_MATH #define MICROPY_PY_MATH (1) #endif // Whether to provide special math functions: math.{erf,erfc,gamma,lgamma} #ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (0) #endif // Whether to provide math.factorial function #ifndef MICROPY_PY_MATH_FACTORIAL #define MICROPY_PY_MATH_FACTORIAL (0) #endif // Whether to provide math.isclose function #ifndef MICROPY_PY_MATH_ISCLOSE #define MICROPY_PY_MATH_ISCLOSE (0) #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 (0) #endif // Whether to provide "gc" module #ifndef MICROPY_PY_GC #define MICROPY_PY_GC (1) #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 (1) #endif // Whether to provide "io.IOBase" class to support user streams #ifndef MICROPY_PY_IO_IOBASE #define MICROPY_PY_IO_IOBASE (0) #endif // Whether to provide "uio.resource_stream()" function with // the semantics of CPython's pkg_resources.resource_stream() // (allows to access binary resources in frozen source packages). // Note that the same functionality can be achieved in "pure // Python" by prepocessing binary resources into Python source // and bytecode-freezing it (with a simple helper module available // e.g. in micropython-lib). #ifndef MICROPY_PY_IO_RESOURCE_STREAM #define MICROPY_PY_IO_RESOURCE_STREAM (0) #endif // Whether to provide "io.FileIO" class #ifndef MICROPY_PY_IO_FILEIO #define MICROPY_PY_IO_FILEIO (0) #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 (0) #endif // Whether to provide "struct" module #ifndef MICROPY_PY_STRUCT #define MICROPY_PY_STRUCT (1) #endif // Whether to provide "sys" module #ifndef MICROPY_PY_SYS #define MICROPY_PY_SYS (1) #endif // Whether to provide "sys.maxsize" constant #ifndef MICROPY_PY_SYS_MAXSIZE #define MICROPY_PY_SYS_MAXSIZE (0) #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.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 "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 (0) #endif // Whether to provide sys.{stdin,stdout,stderr} objects #ifndef MICROPY_PY_SYS_STDFILES #define MICROPY_PY_SYS_STDFILES (0) #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 (0) #endif // Whether to provide "uerrno" module #ifndef MICROPY_PY_UERRNO #define MICROPY_PY_UERRNO (0) #endif // Whether to provide the uerrno.errorcode dict #ifndef MICROPY_PY_UERRNO_ERRORCODE #define MICROPY_PY_UERRNO_ERRORCODE (1) #endif // Whether to provide "uselect" module (baremetal implementation) #ifndef MICROPY_PY_USELECT #define MICROPY_PY_USELECT (0) #endif // Whether to provide "utime" module functions implementation // in terms of mp_hal_* functions. #ifndef MICROPY_PY_UTIME_MP_HAL #define MICROPY_PY_UTIME_MP_HAL (0) #endif // Period of values returned by utime.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_UTIME_TICKS_PERIOD #define MICROPY_PY_UTIME_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_UASYNCIO #define MICROPY_PY_UASYNCIO (0) #endif #ifndef MICROPY_PY_UCTYPES #define MICROPY_PY_UCTYPES (0) #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 #ifndef MICROPY_PY_UZLIB #define MICROPY_PY_UZLIB (0) #endif #ifndef MICROPY_PY_UJSON #define MICROPY_PY_UJSON (0) #endif #ifndef MICROPY_PY_URE #define MICROPY_PY_URE (0) #endif #ifndef MICROPY_PY_URE_DEBUG #define MICROPY_PY_URE_DEBUG (0) #endif #ifndef MICROPY_PY_URE_MATCH_GROUPS #define MICROPY_PY_URE_MATCH_GROUPS (0) #endif #ifndef MICROPY_PY_URE_MATCH_SPAN_START_END #define MICROPY_PY_URE_MATCH_SPAN_START_END (0) #endif #ifndef MICROPY_PY_URE_SUB #define MICROPY_PY_URE_SUB (0) #endif #ifndef MICROPY_PY_UHEAPQ #define MICROPY_PY_UHEAPQ (0) #endif // Optimized heap queue for relative timestamps #ifndef MICROPY_PY_UTIMEQ #define MICROPY_PY_UTIMEQ (0) #endif #ifndef MICROPY_PY_UHASHLIB #define MICROPY_PY_UHASHLIB (0) #endif #ifndef MICROPY_PY_UHASHLIB_MD5 #define MICROPY_PY_UHASHLIB_MD5 (0) #endif #ifndef MICROPY_PY_UHASHLIB_SHA1 #define MICROPY_PY_UHASHLIB_SHA1 (0) #endif #ifndef MICROPY_PY_UHASHLIB_SHA256 #define MICROPY_PY_UHASHLIB_SHA256 (1) #endif #ifndef MICROPY_PY_UCRYPTOLIB #define MICROPY_PY_UCRYPTOLIB (0) #endif // Depends on MICROPY_PY_UCRYPTOLIB #ifndef MICROPY_PY_UCRYPTOLIB_CTR #define MICROPY_PY_UCRYPTOLIB_CTR (0) #endif #ifndef MICROPY_PY_UCRYPTOLIB_CONSTS #define MICROPY_PY_UCRYPTOLIB_CONSTS (0) #endif #ifndef MICROPY_PY_UBINASCII #define MICROPY_PY_UBINASCII (0) #endif // Depends on MICROPY_PY_UZLIB #ifndef MICROPY_PY_UBINASCII_CRC32 #define MICROPY_PY_UBINASCII_CRC32 (0) #endif #ifndef MICROPY_PY_URANDOM #define MICROPY_PY_URANDOM (0) #endif // Whether to include: randrange, randint, choice, random, uniform #ifndef MICROPY_PY_URANDOM_EXTRA_FUNCS #define MICROPY_PY_URANDOM_EXTRA_FUNCS (0) #endif #ifndef MICROPY_PY_MACHINE #define MICROPY_PY_MACHINE (0) #endif // Whether to include: time_pulse_us #ifndef MICROPY_PY_MACHINE_PULSE #define MICROPY_PY_MACHINE_PULSE (0) #endif #ifndef MICROPY_PY_MACHINE_I2C #define MICROPY_PY_MACHINE_I2C (0) #endif #ifndef MICROPY_PY_MACHINE_SPI #define MICROPY_PY_MACHINE_SPI (0) #endif #ifndef MICROPY_PY_USSL #define MICROPY_PY_USSL (0) // Whether to add finaliser code to ussl objects #define MICROPY_PY_USSL_FINALISER (0) #endif #ifndef MICROPY_PY_UWEBSOCKET #define MICROPY_PY_UWEBSOCKET (0) #endif #ifndef MICROPY_PY_FRAMEBUF #define MICROPY_PY_FRAMEBUF (0) #endif #ifndef MICROPY_PY_BTREE #define MICROPY_PY_BTREE (0) #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 module definitions - see objmodule.c:mp_builtin_module_table for format. #ifndef MICROPY_PORT_BUILTIN_MODULES #define MICROPY_PORT_BUILTIN_MODULES #endif // Additional constant definitions for the compiler - see compile.c:mp_constants_table. #ifndef MICROPY_PORT_CONSTANTS #define MICROPY_PORT_CONSTANTS #endif // Any root pointers for GC scanning - see mpstate.c #ifndef MICROPY_PORT_ROOT_POINTERS #define MICROPY_PORT_ROOT_POINTERS #endif /*****************************************************************************/ /* Hooks for a port to wrap functions with attributes */ #ifndef MICROPY_WRAP_MP_KEYBOARD_INTERRUPT #define MICROPY_WRAP_MP_KEYBOARD_INTERRUPT(f) f #endif #ifndef MICROPY_WRAP_MP_SCHED_SCHEDULE #define MICROPY_WRAP_MP_SCHED_SCHEDULE(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 // 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 // Allow to override static modifier for global objects, e.g. to use with // object code analysis tools which don't support static symbols. #ifndef STATIC #define STATIC static #endif // Number of bytes in a word #ifndef BYTES_PER_WORD #define BYTES_PER_WORD (sizeof(mp_uint_t)) #endif #ifndef BITS_PER_BYTE #define BITS_PER_BYTE (8) #endif #define BITS_PER_WORD (BITS_PER_BYTE * BYTES_PER_WORD) // mp_int_t value with most significant bit set #define WORD_MSBIT_HIGH (((mp_uint_t)1) << (BYTES_PER_WORD * 8 - 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 // 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 // Feature dependency check. #if MICROPY_PY_SYS_SETTRACE #if !MICROPY_PERSISTENT_CODE_SAVE #error "MICROPY_PY_SYS_SETTRACE requires MICROPY_PERSISTENT_CODE_SAVE to be enabled" #endif #if MICROPY_COMP_CONST #error "MICROPY_PY_SYS_SETTRACE requires MICROPY_COMP_CONST to be disabled" #endif #endif #endif // MICROPY_INCLUDED_PY_MPCONFIG_H ================================================ FILE: py/mperrno.h ================================================ /* * 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 #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 #endif #if MICROPY_PY_UERRNO #include "py/obj.h" qstr mp_errno_to_str(mp_obj_t errno_val); #endif #endif // MICROPY_INCLUDED_PY_MPERRNO_H ================================================ FILE: py/mphal.h ================================================ /* * 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 #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 void 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 #endif // MICROPY_INCLUDED_PY_MPHAL_H ================================================ FILE: py/mpprint.c ================================================ /* * 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) { prec = len; } chrs += mp_print_strn(print, str, prec, 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 if (prec < 0) { prec = strlen(str); } chrs += mp_print_strn(print, str, prec, 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; if (*fmt == 'u' || *fmt == 'd') { chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); break; } assert(!"unsupported fmt char"); } #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; } ================================================ FILE: py/mpprint.h ================================================ /* * 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; // 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 ================================================ FILE: py/mpstate.c ================================================ /* * 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; ================================================ FILE: py/mpstate.h ================================================ /* * 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. // 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 bool opt_cache_map_lookup_in_bytecode; bool py_builtins_str_unicode; 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 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 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; int gc_stack_overflow; MICROPY_GC_STACK_ENTRY_TYPE gc_stack[MICROPY_ALLOC_GC_STACK_SIZE]; uint16_t gc_lock_depth; // 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 size_t gc_last_free_atb_index; #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; // 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; // pending exception object (MP_OBJ_NULL if not pending) volatile mp_obj_t mp_pending_exception; #if MICROPY_ENABLE_SCHEDULER mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]; #endif // current exception being handled, for sys.exc_info() #if MICROPY_PY_SYS_EXC_INFO mp_obj_base_t *cur_exception; #endif #if MICROPY_PY_SYS_ATEXIT // exposed through sys.atexit function mp_obj_t sys_exitfunc; #endif // dictionary for the __main__ module mp_obj_dict_t dict_main; // these two lists must be initialised per port, after the call to mp_init mp_obj_list_t mp_sys_path_obj; mp_obj_list_t mp_sys_argv_obj; // dictionary for overridden builtins #if MICROPY_CAN_OVERRIDE_BUILTINS mp_obj_dict_t *mp_module_builtins_override_dict; #endif #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_obj_t track_reloc_code_list; #endif // include any root pointers defined by a port MICROPY_PORT_ROOT_POINTERS // root pointers for extmod #if MICROPY_REPL_EVENT_DRIVEN vstr_t *repl_line; #endif #if MICROPY_PY_OS_DUPTERM mp_obj_t dupterm_objs[MICROPY_PY_OS_DUPTERM]; #endif #if MICROPY_PY_LWIP_SLIP mp_obj_t lwip_slip_stream; #endif #if MICROPY_VFS struct _mp_vfs_mount_t *vfs_cur; struct _mp_vfs_mount_t *vfs_mount_table; #endif #if MICROPY_PY_BLUETOOTH mp_obj_t bluetooth; #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) byte *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; uint8_t sched_len; uint8_t sched_idx; #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 } mp_state_vm_t; // This structure holds state that is specific to a given thread. // Everything in this structure is scanned for root pointers. 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 //////////////////////////////////////////////////////////// // 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; #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) #if MICROPY_PY_THREAD extern mp_state_thread_t *mp_thread_get_state(void); #define MP_STATE_THREAD(x) (mp_thread_get_state()->x) #else #define MP_STATE_THREAD(x) (mp_state_ctx.thread.x) #endif #endif // MICROPY_INCLUDED_PY_MPSTATE_H ================================================ FILE: py/mpthread.h ================================================ /* * 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); void mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size); 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 ================================================ FILE: py/mpz.c ================================================ /* * 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. // We have different code here to handle different compile-time // configurations of mpz: // // 1. DIG_SIZE is stricly less than half the number of bits // available in mpz_dbl_dig_t. In this case we can use a // slightly more optimal (in time and space) routine that // uses the extra bits in mpz_dbl_dig_signed_t to store a // sign bit. // // 2. DIG_SIZE is exactly half the number of bits available in // mpz_dbl_dig_t. In this (common) case we need to be careful // not to overflow the borrow variable. And the shifting of // borrow needs some special logic (it's a shift right with // round up). // const mpz_dig_t *d = den_dig; mpz_dbl_dig_t d_norm = 0; mpz_dbl_dig_t borrow = 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); mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK); #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2 borrow += (mpz_dbl_dig_t)*n - x; // will overflow if DIG_SIZE >= MPZ_DBL_DIG_SIZE/2 *n = borrow & DIG_MASK; borrow = (mpz_dbl_dig_signed_t)borrow >> DIG_SIZE; #else // DIG_SIZE == MPZ_DBL_DIG_SIZE / 2 if (x >= *n || *n - x <= borrow) { borrow += x - (mpz_dbl_dig_t)*n; *n = (-borrow) & DIG_MASK; borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up } else { *n = ((mpz_dbl_dig_t)*n - x - borrow) & DIG_MASK; borrow = 0; } #endif } #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2 // Borrow was negative in the above for-loop, make it positive for next if-block. borrow = -borrow; #endif // 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). 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 1 less than borrow (borrow == *num_dig + 1). // 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->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 = -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) { // to catch comparison of -0 with +0 if (z1->len == 0 && z2->len == 0) { return 0; } 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); } 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; } /* 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 (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->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); // check signs and do Python style modulo if (lhs->neg != rhs->neg) { dest_quo->neg = 1; 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 > (~(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 > (~(WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) { // will overflow return false; } val = (val << DIG_SIZE) | *d; } *value = val; return true; } // writes at most len bytes to buf (so buf should be zeroed before calling) 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; } } } } } #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 ================================================ FILE: py/mpz.h ================================================ /* * 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 { 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->len != 0 && 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 ================================================ FILE: py/nativeglue.c ================================================ /* * 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/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); 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_raw_code, 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_make_closure_from_raw_code, mp_arg_check_num_sig, mp_setup_code_state, 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_raise, mp_get_stream_raise, &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, }; #endif // MICROPY_EMIT_NATIVE ================================================ FILE: py/nativeglue.h ================================================ /* * 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_RAW_CODE, 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_MAKE_CLOSURE_FROM_RAW_CODE, 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_raw_code)(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_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 (*make_closure_from_raw_code)(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); void (*arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig); void (*setup_code_state)(mp_code_state_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); void (*get_buffer_raise)(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); const mp_print_t *plat_print; 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; extern const mp_fun_table_t mp_fun_table; #endif // MICROPY_INCLUDED_PY_NATIVEGLUE_H ================================================ FILE: py/nlr.c ================================================ /* * 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_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; } ================================================ FILE: py/nlr.h ================================================ /* * 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_NLR_H #define MICROPY_INCLUDED_PY_NLR_H // non-local return // exception handling, basically a stack of setjmp/longjmp buffers #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_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(__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) #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 here must all be machine word size nlr_buf_t *prev; void *ret_val; // always a concrete object (an exception instance) #if MICROPY_NLR_SETJMP jmp_buf jmpbuf; #else void *regs[MICROPY_NLR_NUM_REGS]; #endif #if MICROPY_ENABLE_PYSTACK void *pystack; #endif }; // 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; \ 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); // 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 #include "mpstate.h" #define nlr_raise(val) \ do { \ /*printf("nlr_raise: nlr_top=%p\n", MP_STATE_THREAD(nlr_top)); \ fflush(stdout);*/ \ 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) /* #define nlr_push(val) \ printf("nlr_push: before: nlr_top=%p, val=%p\n", MP_STATE_THREAD(nlr_top), val),assert(MP_STATE_THREAD(nlr_top) != val),nlr_push(val) */ #endif #endif #endif // MICROPY_INCLUDED_PY_NLR_H ================================================ FILE: py/nlrpowerpc.c ================================================ /* * 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 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) : ); MP_UNREACHABLE; } #endif // MICROPY_NLR_POWERPC ================================================ FILE: py/nlrsetjmp.c ================================================ /* * 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_SETJMP void nlr_jump(void *val) { 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; MP_NLR_RESTORE_PYSTACK(top); *top_ptr = top->prev; longjmp(top->jmpbuf, 1); } #endif ================================================ FILE: py/nlrthumb.c ================================================ /* * 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 : // clobbered registers ); MP_UNREACHABLE } #endif // MICROPY_NLR_THUMB ================================================ FILE: py/nlrx64.c ================================================ /* * 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); unsigned int nlr_push(nlr_buf_t *nlr) { (void)nlr; #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 ( #if defined(__APPLE__) || defined(__MACH__) "pop %rbp \n" // undo function's prelude #endif "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 return 0; // needed to silence compiler warning } 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 %rdr #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 : // clobbered registers ); MP_UNREACHABLE } #endif // MICROPY_NLR_X64 ================================================ FILE: py/nlrx86.c ================================================ /* * 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 : // clobbered registers ); MP_UNREACHABLE } #endif // MICROPY_NLR_X86 ================================================ FILE: py/nlrxtensa.c ================================================ /* * 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 : // clobbered registers ); MP_UNREACHABLE } #endif // MICROPY_NLR_XTENSA ================================================ FILE: py/obj.c ================================================ /* * 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 const mp_obj_type_t *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 (type->print != NULL) { 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 (type->unary_op != NULL) { mp_obj_t result = 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_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 (type->binary_op != NULL && ((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 = type->binary_op(MP_BINARY_OP_NOT_EQUAL, o1, o2); if (r != MP_OBJ_NULL) { return r; } } // Try calling __eq__. mp_obj_t r = 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 conparison 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]. if (arg == mp_const_false) { return 0; } else if (arg == mp_const_true) { return 1; } else if (mp_obj_is_small_int(arg)) { return MP_OBJ_SMALL_INT_VALUE(arg); } else if (mp_obj_is_type(arg, &mp_type_int)) { return mp_obj_int_get_checked(arg); } else { mp_obj_t res = mp_unary_op(MP_UNARY_OP_INT, (mp_obj_t)arg); return mp_obj_int_get_checked(res); } } 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_type(arg, &mp_type_int)) { *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_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 { 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 (arg == mp_const_false) { *real = 0; *imag = 0; } else if (arg == mp_const_true) { *real = 1; *imag = 0; } else if (mp_obj_is_small_int(arg)) { *real = (mp_float_t)MP_OBJ_SMALL_INT_VALUE(arg); *imag = 0; #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE } else if (mp_obj_is_type(arg, &mp_type_int)) { *real = mp_obj_int_as_float_impl(arg); *imag = 0; #endif } else if (mp_obj_is_float(arg)) { *real = mp_obj_float_get(arg); *imag = 0; } else if (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 (type->unary_op != NULL) { return 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 (type->subscr != NULL) { mp_obj_t ret = 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 (type->buffer_p.get_buffer == NULL) { return false; } int ret = type->buffer_p.get_buffer(obj, bufinfo, flags); if (ret != 0) { return false; } return true; } void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) { if (!mp_get_buffer(obj, bufinfo, flags)) { mp_raise_TypeError(MP_ERROR_TEXT("object with buffer protocol required")); } } mp_obj_t mp_generic_unary_op(mp_unary_op_t op, mp_obj_t o_in) { switch (op) { case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT((mp_uint_t)o_in); default: return MP_OBJ_NULL; // op not supported } } ================================================ FILE: py/obj.h ================================================ /* * 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) extern const struct _mp_obj_float_t mp_const_float_e_obj; extern const struct _mp_obj_float_t mp_const_float_pi_obj; #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) extern const struct _mp_obj_float_t mp_const_float_e_obj; extern const struct _mp_obj_float_t mp_const_float_pi_obj; #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 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)) static inline bool mp_obj_is_float(mp_const_obj_t o) { 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))} 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(dict_name, table_name) \ const mp_obj_dict_t dict_name = { \ .base = {&mp_type_dict}, \ .map = { \ .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, \ }, \ } // These macros are used to declare and define constant staticmethond 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} // Declare a module as a builtin, processed by makemoduledefs.py // param module_name: MP_QSTR_ // param obj_module: mp_obj_module_t instance // prarm enabled_define: used as `#if (enabled_define) around entry` #define MP_REGISTER_MODULE(module_name, obj_module, enabled_define) // 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. #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) 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); // 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) typedef struct _mp_buffer_p_t { mp_int_t (*get_buffer)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); } mp_buffer_p_t; bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); 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; // Corresponds to __repr__ and __str__ special methods. mp_print_fun_t print; // Corresponds to __new__ and __init__ special methods, to make an instance of the type. mp_make_new_fun_t make_new; // Corresponds to __call__ special method, ie T(...). mp_call_fun_t call; // Implements unary and binary operations. // Can return MP_OBJ_NULL if the operation is not supported. mp_unary_op_fun_t unary_op; mp_binary_op_fun_t binary_op; // Implements load, store and delete attribute. // // dest[0] = MP_OBJ_NULL means load // return: for fail, do nothing // 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 mp_attr_fun_t 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. mp_subscr_fun_t subscr; // Corresponds to __iter__ special method. // Can use the given mp_obj_iter_buf_t to store iterator object, // otherwise can return a pointer to an object on the heap. mp_getiter_fun_t getiter; // Corresponds to __next__ special method. May return MP_OBJ_STOP_ITERATION // as an optimisation instead of raising StopIteration() with no args. mp_fun_1_t iternext; // Implements the buffer protocol if supported by this type. mp_buffer_p_t buffer_p; // One of disjoint protocols (interfaces), like mp_stream_p_t, etc. const void *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 const void *parent; // A dict mapping qstrs to objects local methods/constants/etc. struct _mp_obj_dict_t *locals_dict; }; // 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_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_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; // 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 // 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. #define mp_obj_is_type(o, t) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type == (t))) // this does not work for checking int, str or fun; use below macros for that #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_type(o, &mp_type_bool) #endif #define mp_obj_is_int(o) (mp_obj_is_small_int(o) || mp_obj_is_type(o, &mp_type_int)) #define mp_obj_is_str(o) (mp_obj_is_qstr(o) || mp_obj_is_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_base_t *)MP_OBJ_TO_PTR(o))->type->binary_op == mp_obj_str_binary_op)) #define mp_obj_is_dict_or_ordereddict(o) (mp_obj_is_obj(o) && ((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->make_new == mp_obj_dict_make_new) #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); mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len); mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, 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, 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_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg); mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args); 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!) #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_fun_bc(mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code, const mp_uint_t *const_table); mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table); mp_obj_t mp_obj_new_fun_asm(size_t n_args, const void *fun_data, mp_uint_t type_sig); 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); static inline bool mp_obj_is_integer(mp_const_obj_t o) { return mp_obj_is_int(o) || mp_obj_is_bool(o); } // returns true if o is bool, small int or long int 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); mp_obj_t mp_generic_unary_op(mp_unary_op_t op, mp_obj_t o_in); // cell mp_obj_t mp_obj_cell_get(mp_obj_t self_in); void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t 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 #define mp_obj_is_native_exception_instance(o) (mp_obj_get_type(o)->make_new == mp_obj_exception_make_new) 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); // 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); qstr mp_obj_code_get_name(const byte *code_info); mp_obj_t mp_identity(mp_obj_t self); MP_DECLARE_CONST_FUN_OBJ_1(mp_identity_obj); mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf); // 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; } // check if given module object is a package bool mp_obj_is_package(mp_obj_t module); // 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 ================================================ FILE: py/objarray.c ================================================ /* * 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 #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 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); self->base.type = &mp_type_memoryview; self->typecode = typecode; self->memview_offset = 0; self->len = nitems; self->items = 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)); // 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)); } } #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 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: { // 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: { 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; } return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len)); } 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 } STATIC MP_DEFINE_CONST_FUN_OBJ_2(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; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(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_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) { 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_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY STATIC const mp_rom_map_elem_t array_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&array_append_obj) }, { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&array_extend_obj) }, #if MICROPY_CPYTHON_COMPAT { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&bytes_decode_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(array_locals_dict, array_locals_dict_table); #endif #if MICROPY_PY_ARRAY const mp_obj_type_t mp_type_array = { { &mp_type_type }, .name = MP_QSTR_array, .print = array_print, .make_new = array_make_new, .getiter = array_iterator_new, .unary_op = array_unary_op, .binary_op = array_binary_op, .subscr = array_subscr, .buffer_p = { .get_buffer = array_get_buffer }, .locals_dict = (mp_obj_dict_t *)&array_locals_dict, }; #endif #if MICROPY_PY_BUILTINS_BYTEARRAY const mp_obj_type_t mp_type_bytearray = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, .name = MP_QSTR_bytearray, .print = array_print, .make_new = bytearray_make_new, .getiter = array_iterator_new, .unary_op = array_unary_op, .binary_op = array_binary_op, .subscr = array_subscr, .buffer_p = { .get_buffer = array_get_buffer }, .locals_dict = (mp_obj_dict_t *)&array_locals_dict, }; #endif #if MICROPY_PY_BUILTINS_MEMORYVIEW const mp_obj_type_t mp_type_memoryview = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, .name = MP_QSTR_memoryview, .make_new = memoryview_make_new, .getiter = array_iterator_new, .unary_op = array_unary_op, .binary_op = array_binary_op, #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE .attr = memoryview_attr, #endif .subscr = array_subscr, .buffer_p = { .get_buffer = array_get_buffer }, }; #endif /* 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, 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 = m_new_obj(mp_obj_array_t); o->base.type = &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 const mp_obj_type_t array_it_type = { { &mp_type_type }, .name = MP_QSTR_iterator, .getiter = mp_identity_getiter, .iternext = 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 = &array_it_type; 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 ================================================ FILE: py/objarray.h ================================================ /* * 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) // 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 : (8 * sizeof(size_t) - 8); 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 #endif // MICROPY_INCLUDED_PY_OBJARRAY_H ================================================ FILE: py/objattrtuple.c ================================================ /* * 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 = m_new_obj_var(mp_obj_tuple_t, mp_obj_t, n + 1); o->base.type = &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); } const mp_obj_type_t mp_type_attrtuple = { { &mp_type_type }, .name = MP_QSTR_tuple, // 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, .getiter = mp_obj_tuple_getiter, }; #endif // MICROPY_PY_ATTRTUPLE ================================================ FILE: py/objbool.c ================================================ /* * 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_UJSON && 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); } const mp_obj_type_t mp_type_bool = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, // can match all numeric types .name = MP_QSTR_bool, .print = bool_print, .make_new = bool_make_new, .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 ================================================ FILE: py/objboundmeth.c ================================================ /* * 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); } #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 STATIC const mp_obj_type_t mp_type_bound_meth = { { &mp_type_type }, .name = MP_QSTR_bound_method, #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED .print = bound_meth_print, #endif .call = bound_meth_call, #if MICROPY_PY_FUNCTION_ATTRS .attr = bound_meth_attr, #endif }; mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self) { mp_obj_bound_meth_t *o = m_new_obj(mp_obj_bound_meth_t); o->base.type = &mp_type_bound_meth; o->meth = meth; o->self = self; return MP_OBJ_FROM_PTR(o); } ================================================ FILE: py/objcell.c ================================================ /* * 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" typedef struct _mp_obj_cell_t { mp_obj_base_t base; mp_obj_t obj; } mp_obj_cell_t; mp_obj_t mp_obj_cell_get(mp_obj_t self_in) { mp_obj_cell_t *self = MP_OBJ_TO_PTR(self_in); return self->obj; } void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj) { mp_obj_cell_t *self = MP_OBJ_TO_PTR(self_in); self->obj = obj; } #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 STATIC const mp_obj_type_t mp_type_cell = { { &mp_type_type }, .name = MP_QSTR_, // cell representation is just value in < > #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED .print = cell_print, #endif }; mp_obj_t mp_obj_new_cell(mp_obj_t obj) { mp_obj_cell_t *o = m_new_obj(mp_obj_cell_t); o->base.type = &mp_type_cell; o->obj = obj; return MP_OBJ_FROM_PTR(o); } ================================================ FILE: py/objclosure.c ================================================ /* * 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 const mp_obj_type_t closure_type = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_closure, #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED .print = closure_print, #endif .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 = m_new_obj_var(mp_obj_closure_t, mp_obj_t, n_closed_over); o->base.type = &closure_type; 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); } ================================================ FILE: py/objcomplex.c ================================================ /* * 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_decimal(s, l, true, true, NULL); } else if (mp_obj_is_type(args[0], &mp_type_complex)) { // a complex, just return it return args[0]; } else { // something else, try to cast it to a complex return mp_obj_new_complex(mp_obj_get_float(args[0]), 0); } 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); } } const mp_obj_type_t mp_type_complex = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, .name = MP_QSTR_complex, .print = complex_print, .make_new = complex_make_new, .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 = m_new_obj(mp_obj_complex_t); o->base.type = &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 ================================================ FILE: py/objdeque.c ================================================ /* * 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 #include "py/mpconfig.h" #if MICROPY_PY_COLLECTIONS_DEQUE #include "py/runtime.h" 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 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); /* Initialization from existing sequence is not supported, so an empty tuple must be passed as such. */ if (args[0] != mp_const_empty_tuple) { mp_raise_ValueError(NULL); } // 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 = m_new_obj(mp_obj_deque_t); o->base.type = 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]); } return MP_OBJ_FROM_PTR(o); } 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: { ssize_t len = self->i_put - self->i_get; if (len < 0) { len += self->alloc; } return MP_OBJ_NEW_SMALL_INT(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 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 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); #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) }, #if 0 { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&deque_clear_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&deque_popleft_obj) }, }; STATIC MP_DEFINE_CONST_DICT(deque_locals_dict, deque_locals_dict_table); const mp_obj_type_t mp_type_deque = { { &mp_type_type }, .name = MP_QSTR_deque, .make_new = deque_make_new, .unary_op = deque_unary_op, .locals_dict = (mp_obj_dict_t *)&deque_locals_dict, }; #endif // MICROPY_PY_COLLECTIONS_DEQUE ================================================ FILE: py/objdict.c ================================================ /* * 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" 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; if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) { kind = PRINT_REPR; } 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, ", "); } first = false; bool add_quote = MICROPY_PY_UJSON && 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, ": "); 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; } } 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) { nlr_raise(mp_obj_new_exception_arg1(&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) { nlr_raise(mp_obj_new_exception_arg1(&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) { nlr_raise(mp_obj_new_exception_arg1(&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 dict_view_type; STATIC const mp_obj_type_t dict_view_it_type; 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, &dict_view_it_type)); 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 const mp_obj_type_t dict_view_it_type = { { &mp_type_type }, .name = MP_QSTR_iterator, .getiter = mp_identity_getiter, .iternext = 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, &dict_view_type)); 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 = &dict_view_it_type; 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, &dict_view_type)); 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_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 const mp_obj_type_t dict_view_type = { { &mp_type_type }, .name = MP_QSTR_dict_view, .print = dict_view_print, .binary_op = dict_view_binary_op, .getiter = 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 = m_new_obj(mp_obj_dict_view_t); o->base.type = &dict_view_type; 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 = &dict_view_it_type; 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); const mp_obj_type_t mp_type_dict = { { &mp_type_type }, .name = MP_QSTR_dict, .print = dict_print, .make_new = mp_obj_dict_make_new, .unary_op = dict_unary_op, .binary_op = dict_binary_op, .subscr = dict_subscr, .getiter = dict_getiter, .locals_dict = (mp_obj_dict_t *)&dict_locals_dict, }; #if MICROPY_PY_COLLECTIONS_ORDEREDDICT const mp_obj_type_t mp_type_ordereddict = { { &mp_type_type }, .name = MP_QSTR_OrderedDict, .print = dict_print, .make_new = mp_obj_dict_make_new, .unary_op = dict_unary_op, .binary_op = dict_binary_op, .subscr = dict_subscr, .getiter = dict_getiter, .parent = &mp_type_dict, .locals_dict = (mp_obj_dict_t *)&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; } ================================================ FILE: py/objenumerate.c ================================================ /* * 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 = m_new_obj(mp_obj_enumerate_t); o->base.type = 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 = m_new_obj(mp_obj_enumerate_t); o->base.type = 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); } const mp_obj_type_t mp_type_enumerate = { { &mp_type_type }, .name = MP_QSTR_enumerate, .make_new = enumerate_make_new, .iternext = enumerate_iternext, .getiter = mp_identity_getiter, }; 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 ================================================ FILE: py/objexcept.c ================================================ /* * 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) 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 STATIC void decompress_error_text_maybe(mp_obj_exception_t *o) { #if MICROPY_ROM_TEXT_COMPRESSION if (o->args->len == 1 && mp_obj_is_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; } else if (o->args->len == 1) { #if MICROPY_PY_UERRNO // try to provide a nice OSError error message if (o->base.type == &mp_type_OSError && 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); return; } } #endif 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 straightaway 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, 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 = MP_OBJ_TO_PTR(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 (self->base.type == &mp_type_StopIteration && attr == MP_QSTR_value) { dest[0] = mp_obj_exception_get_value(self_in); } } const mp_obj_type_t mp_type_BaseException = { { &mp_type_type }, .name = MP_QSTR_BaseException, .print = mp_obj_exception_print, .make_new = mp_obj_exception_make_new, .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(exc_type->make_new == mp_obj_exception_make_new); return mp_obj_exception_make_new(exc_type, 0, 0, NULL); } // "Optimized" version for common(?) case of having 1 exception arg mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { assert(exc_type->make_new == mp_obj_exception_make_new); return mp_obj_exception_make_new(exc_type, 1, 0, &arg); } 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(exc_type->make_new == mp_obj_exception_make_new); return mp_obj_exception_make_new(exc_type, n_args, 0, args); } 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(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(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); } // 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 (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 #define GET_NATIVE_EXCEPTION(self, self_in) \ /* make sure self_in is an exception instance */ \ assert(mp_obj_is_exception_instance(self_in)); \ mp_obj_exception_t *self; \ if (mp_obj_is_native_exception_instance(self_in)) { \ self = MP_OBJ_TO_PTR(self_in); \ } else { \ self = MP_OBJ_TO_PTR(((mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in))->subobj[0]); \ } void mp_obj_exception_clear_traceback(mp_obj_t self_in) { GET_NATIVE_EXCEPTION(self, 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) { GET_NATIVE_EXCEPTION(self, self_in); // append this traceback info to traceback data // if memory allocation fails (eg because gc is locked), just return 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) { GET_NATIVE_EXCEPTION(self, self_in); if (self->traceback_data == NULL) { *n = 0; *values = NULL; } else { *n = self->traceback_len; *values = self->traceback_data; } } ================================================ FILE: py/objexcept.h ================================================ /* * 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) \ const mp_obj_type_t mp_type_##exc_name = { \ { &mp_type_type }, \ .name = MP_QSTR_##exc_name, \ .print = mp_obj_exception_print, \ .make_new = mp_obj_exception_make_new, \ .attr = mp_obj_exception_attr, \ .parent = &mp_type_##base_name, \ }; #endif // MICROPY_INCLUDED_PY_OBJEXCEPT_H ================================================ FILE: py/objfilter.c ================================================ /* * 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 = m_new_obj(mp_obj_filter_t); o->base.type = 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; } const mp_obj_type_t mp_type_filter = { { &mp_type_type }, .name = MP_QSTR_filter, .make_new = filter_make_new, .getiter = mp_identity_getiter, .iternext = filter_iternext, }; #endif // MICROPY_PY_BUILTINS_FILTER ================================================ FILE: py/objfloat.c ================================================ /* * 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}; #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 < 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_decimal(bufinfo.buf, bufinfo.len, false, 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); } const mp_obj_type_t mp_type_float = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, .name = MP_QSTR_float, .print = float_print, .make_new = float_make_new, .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) { mp_obj_float_t *o = m_new(mp_obj_float_t, 1); 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 ================================================ FILE: py/objfun.c ================================================ /* * 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(); } const mp_obj_type_t mp_type_fun_builtin_0 = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_0_call, .unary_op = mp_generic_unary_op, }; 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]); } const mp_obj_type_t mp_type_fun_builtin_1 = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_1_call, .unary_op = mp_generic_unary_op, }; 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]); } const mp_obj_type_t mp_type_fun_builtin_2 = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_2_call, .unary_op = mp_generic_unary_op, }; 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]); } const mp_obj_type_t mp_type_fun_builtin_3 = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_3_call, .unary_op = mp_generic_unary_op, }; 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); } } const mp_obj_type_t mp_type_fun_builtin_var = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_var_call, .unary_op = mp_generic_unary_op, }; /******************************************************************************/ /* byte code functions */ qstr mp_obj_code_get_name(const byte *code_info) { MP_BC_PRELUDE_SIZE_DECODE(code_info); #if MICROPY_PERSISTENT_CODE return code_info[0] | (code_info[1] << 8); #else return mp_decode_uint_value(code_info); #endif } #if MICROPY_EMIT_NATIVE STATIC const mp_obj_type_t mp_type_fun_native; #endif 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); #if MICROPY_EMIT_NATIVE if (fun->base.type == &mp_type_fun_native || fun->base.type == &mp_type_native_gen_wrap) { // TODO native functions don't have name stored return MP_QSTR_; } #endif const byte *bc = fun->bytecode; MP_BC_PRELUDE_SIG_DECODE(bc); return mp_obj_code_get_name(bc); } #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); \ \ /* 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->ip = 0; \ 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, 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->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(sizeof(mp_code_state_t) + state_size); #else if (state_size > VM_MAX_STATE_ON_STACK) { code_state = m_new_obj_var_maybe(mp_code_state_t, 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(sizeof(mp_code_state_t) + 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->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, 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)); } } #endif const mp_obj_type_t mp_type_fun_bc = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, #if MICROPY_CPYTHON_COMPAT .print = fun_bc_print, #endif .call = fun_bc_call, .unary_op = mp_generic_unary_op, #if MICROPY_PY_FUNCTION_ATTRS .attr = mp_obj_fun_bc_attr, #endif }; mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code, const mp_uint_t *const_table) { size_t n_def_args = 0; size_t n_extra_args = 0; mp_obj_tuple_t *def_args = MP_OBJ_TO_PTR(def_args_in); if (def_args_in != MP_OBJ_NULL) { assert(mp_obj_is_type(def_args_in, &mp_type_tuple)); n_def_args = def_args->len; n_extra_args = def_args->len; } if (def_kw_args != MP_OBJ_NULL) { n_extra_args += 1; } mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args); o->base.type = &mp_type_fun_bc; o->globals = mp_globals_get(); o->bytecode = code; o->const_table = const_table; if (def_args != NULL) { memcpy(o->extra_args, def_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 = self_in; mp_call_fun_t fun = MICROPY_MAKE_POINTER_CALLABLE((void *)self->bytecode); return fun(self_in, n_args, n_kw, args); } STATIC const mp_obj_type_t mp_type_fun_native = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, .call = fun_native_call, .unary_op = mp_generic_unary_op, }; mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table) { mp_obj_fun_bc_t *o = mp_obj_new_fun_bc(def_args_in, def_kw_args, (const byte *)fun_data, const_table); o->base.type = &mp_type_fun_native; return o; } #endif // MICROPY_EMIT_NATIVE /******************************************************************************/ /* inline assembler functions */ #if MICROPY_EMIT_INLINE_ASM 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; 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_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 = 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); } STATIC const mp_obj_type_t mp_type_fun_asm = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, .call = fun_asm_call, .unary_op = mp_generic_unary_op, }; 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 = m_new_obj(mp_obj_fun_asm_t); o->base.type = &mp_type_fun_asm; o->n_args = n_args; o->fun_data = fun_data; o->type_sig = type_sig; return o; } #endif // MICROPY_EMIT_INLINE_ASM ================================================ FILE: py/objfun.h ================================================ /* * 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/obj.h" typedef struct _mp_obj_fun_bc_t { mp_obj_base_t base; mp_obj_dict_t *globals; // the context within which this function was defined const byte *bytecode; // bytecode for the function const mp_uint_t *const_table; // constant table #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) // - a single slot for var args tuple (if it takes them) // - a single slot for kw args dict (if it takes them) mp_obj_t extra_args[]; } mp_obj_fun_bc_t; void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); #endif // MICROPY_INCLUDED_PY_OBJFUN_H ================================================ FILE: py/objgenerator.c ================================================ /* * 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 = m_new_obj_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t)); o->base.type = &mp_type_gen_instance; o->pend_exc = mp_const_none; o->code_state.fun_bc = self_fun; o->code_state.ip = 0; 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); } const mp_obj_type_t mp_type_gen_wrap = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_generator, .call = gen_wrap_call, .unary_op = mp_generic_unary_op, #if MICROPY_PY_FUNCTION_ATTRS .attr = mp_obj_fun_bc_attr, #endif }; /******************************************************************************/ // native generator wrapper #if MICROPY_EMIT_NATIVE 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, and extract n_state from it uintptr_t prelude_offset = ((uintptr_t *)self_fun->bytecode)[0]; #if MICROPY_EMIT_NATIVE_PRELUDE_AS_BYTES_OBJ // Prelude is in bytes object in const_table, at index prelude_offset mp_obj_str_t *prelude_bytes = MP_OBJ_TO_PTR(self_fun->const_table[prelude_offset]); prelude_offset = (const byte *)prelude_bytes->data - self_fun->bytecode; #endif const uint8_t *ip = self_fun->bytecode + prelude_offset; size_t n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args; MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args); size_t n_exc_stack = 0; // Allocate the generator object, with room for local stack and exception stack mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t)); o->base.type = &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.ip = (const byte *)prelude_offset; o->code_state.n_state = n_state; mp_setup_code_state(&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 uintptr_t start_offset = ((uintptr_t *)self_fun->bytecode)[1]; o->code_state.ip = MICROPY_MAKE_POINTER_CALLABLE((void *)(self_fun->bytecode + start_offset)); return MP_OBJ_FROM_PTR(o); } const mp_obj_type_t mp_type_native_gen_wrap = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_generator, .call = native_gen_wrap_call, .unary_op = mp_generic_unary_op, #if MICROPY_PY_FUNCTION_ATTRS .attr = mp_obj_fun_bc_attr, #endif }; #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 already stopped generator *ret_val = MP_OBJ_STOP_ITERATION; 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. if (self->code_state.sp == self->code_state.state - 1) { 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->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, with entry point 2 words into the "bytecode" pointer typedef uintptr_t (*mp_fun_native_gen_t)(void *, mp_obj_t); mp_fun_native_gen_t fun = MICROPY_MAKE_POINTER_CALLABLE((const void *)(self->code_state.fun_bc->bytecode + 2 * sizeof(uintptr_t))); 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; *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; *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) { mp_obj_t ret; switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) { case MP_VM_RETURN_NORMAL: default: // Optimize return w/o value in case generator is used in for loop if (ret == mp_const_none || ret == MP_OBJ_STOP_ITERATION) { return MP_OBJ_STOP_ITERATION; } else { nlr_raise(mp_obj_new_exception_arg1(&mp_type_StopIteration, 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); } STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { mp_obj_t ret = gen_resume_and_raise(self_in, send_value, MP_OBJ_NULL); if (ret == MP_OBJ_STOP_ITERATION) { mp_raise_type(&mp_type_StopIteration); } else { return ret; } } 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]; } mp_obj_t ret = gen_resume_and_raise(args[0], mp_const_none, exc); if (ret == MP_OBJ_STOP_ITERATION) { mp_raise_type(&mp_type_StopIteration); } else { return ret; } } 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); const mp_obj_type_t mp_type_gen_instance = { { &mp_type_type }, .name = MP_QSTR_generator, .print = gen_instance_print, .unary_op = mp_generic_unary_op, .getiter = mp_identity_getiter, .iternext = gen_instance_iternext, .locals_dict = (mp_obj_dict_t *)&gen_instance_locals_dict, }; ================================================ FILE: py/objgenerator.h ================================================ /* * 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 ================================================ FILE: py/objgetitemiter.c ================================================ /* * 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 instead of raising return MP_OBJ_STOP_ITERATION; } else { // re-raise exception nlr_jump(nlr.ret_val); } } } STATIC const mp_obj_type_t it_type = { { &mp_type_type }, .name = MP_QSTR_iterator, .getiter = mp_identity_getiter, .iternext = 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 = &it_type; 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); } ================================================ FILE: py/objint.c ================================================ /* * 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: if (mp_obj_is_int(args[0])) { // already an int (small or long), just return it return args[0]; } else if (mp_obj_is_str_or_bytes(args[0])) { // 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, 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 { return mp_unary_op(MP_UNARY_OP_INT, 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 & ~(1 << MP_FLOAT_SIGN_SHIFT_I32)) == 0) { // handle case of -0 (when sign is set but rest of bits are zero) e = 0; } else { e += ((1 << MP_FLOAT_EXP_BITS) - 1) << MP_FLOAT_EXP_SHIFT_I32; } } else { e &= ~((1 << 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) * 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_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_str_from_vstr(&mp_type_bytes, &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); const mp_obj_type_t mp_type_int = { { &mp_type_type }, .name = MP_QSTR_int, .print = mp_obj_int_print, .make_new = mp_obj_int_make_new, .unary_op = mp_obj_int_unary_op, .binary_op = mp_obj_int_binary_op, .locals_dict = (mp_obj_dict_t *)&int_locals_dict, }; ================================================ FILE: py/objint.h ================================================ /* * 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 ================================================ FILE: py/objint_longlong.c ================================================ /* * 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_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); } 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_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_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 = m_new_obj(mp_obj_int_t); o->base.type = &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 = m_new_obj(mp_obj_int_t); o->base.type = &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 = m_new_obj(mp_obj_int_t); o->base.type = &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_type(self_in, &mp_type_int)); mp_obj_int_t *self = self_in; return self->val; } #endif #endif ================================================ FILE: py/objint_mpz.c ================================================ /* * 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 = m_new_obj(mp_obj_int_t); o->base.type = &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_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_type(self_in, &mp_type_int)); mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); memset(buf, 0, len); 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); } 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_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_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; default: { assert(op == 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); } } 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_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 ================================================ FILE: py/objlist.c ================================================ /* * 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); if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) { kind = PRINT_REPR; } 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, 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; } STATIC mp_obj_t 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); const mp_obj_type_t mp_type_list = { { &mp_type_type }, .name = MP_QSTR_list, .print = list_print, .make_new = list_make_new, .unary_op = list_unary_op, .binary_op = list_binary_op, .subscr = list_subscr, .getiter = list_getiter, .locals_dict = (mp_obj_dict_t *)&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); } ================================================ FILE: py/objlist.h ================================================ /* * 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); #endif // MICROPY_INCLUDED_PY_OBJLIST_H ================================================ FILE: py/objmap.c ================================================ /* * 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 = m_new_obj_var(mp_obj_map_t, mp_obj_t, n_args - 1); o->base.type = 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); } const mp_obj_type_t mp_type_map = { { &mp_type_type }, .name = MP_QSTR_map, .make_new = map_make_new, .getiter = mp_identity_getiter, .iternext = map_iternext, }; ================================================ FILE: py/objmodule.c ================================================ /* * 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/objmodule.h" #include "py/runtime.h" #include "py/builtin.h" #include "genhdr/moduledefs.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(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_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)); } #endif } } 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 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 } } const mp_obj_type_t mp_type_module = { { &mp_type_type }, .name = MP_QSTR_module, .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_obj_module_t *o = m_new_obj(mp_obj_module_t); o->base.type = &mp_type_module; o->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->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[] = { { MP_ROM_QSTR(MP_QSTR___main__), MP_ROM_PTR(&mp_module___main__) }, { MP_ROM_QSTR(MP_QSTR_builtins), MP_ROM_PTR(&mp_module_builtins) }, { MP_ROM_QSTR(MP_QSTR_micropython), MP_ROM_PTR(&mp_module_micropython) }, #if MICROPY_PY_IO { MP_ROM_QSTR(MP_QSTR_uio), MP_ROM_PTR(&mp_module_io) }, #endif #if MICROPY_PY_COLLECTIONS { MP_ROM_QSTR(MP_QSTR_ucollections), MP_ROM_PTR(&mp_module_collections) }, #endif #if MICROPY_PY_STRUCT { MP_ROM_QSTR(MP_QSTR_ustruct), MP_ROM_PTR(&mp_module_ustruct) }, #endif #if MICROPY_PY_BUILTINS_FLOAT #if MICROPY_PY_MATH { MP_ROM_QSTR(MP_QSTR_math), MP_ROM_PTR(&mp_module_math) }, #endif #if MICROPY_PY_BUILTINS_COMPLEX && MICROPY_PY_CMATH { MP_ROM_QSTR(MP_QSTR_cmath), MP_ROM_PTR(&mp_module_cmath) }, #endif #endif #if MICROPY_PY_SYS { MP_ROM_QSTR(MP_QSTR_usys), MP_ROM_PTR(&mp_module_sys) }, #endif #if MICROPY_PY_GC && MICROPY_ENABLE_GC { MP_ROM_QSTR(MP_QSTR_gc), MP_ROM_PTR(&mp_module_gc) }, #endif #if MICROPY_PY_THREAD { MP_ROM_QSTR(MP_QSTR__thread), MP_ROM_PTR(&mp_module_thread) }, #endif // extmod modules #if MICROPY_PY_UASYNCIO { MP_ROM_QSTR(MP_QSTR__uasyncio), MP_ROM_PTR(&mp_module_uasyncio) }, #endif #if MICROPY_PY_UERRNO { MP_ROM_QSTR(MP_QSTR_uerrno), MP_ROM_PTR(&mp_module_uerrno) }, #endif #if MICROPY_PY_UCTYPES { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) }, #endif #if MICROPY_PY_UZLIB { MP_ROM_QSTR(MP_QSTR_uzlib), MP_ROM_PTR(&mp_module_uzlib) }, #endif #if MICROPY_PY_UJSON { MP_ROM_QSTR(MP_QSTR_ujson), MP_ROM_PTR(&mp_module_ujson) }, #endif #if MICROPY_PY_URE { MP_ROM_QSTR(MP_QSTR_ure), MP_ROM_PTR(&mp_module_ure) }, #endif #if MICROPY_PY_UHEAPQ { MP_ROM_QSTR(MP_QSTR_uheapq), MP_ROM_PTR(&mp_module_uheapq) }, #endif #if MICROPY_PY_UTIMEQ { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&mp_module_utimeq) }, #endif #if MICROPY_PY_UHASHLIB { MP_ROM_QSTR(MP_QSTR_uhashlib), MP_ROM_PTR(&mp_module_uhashlib) }, #endif #if MICROPY_PY_UCRYPTOLIB { MP_ROM_QSTR(MP_QSTR_ucryptolib), MP_ROM_PTR(&mp_module_ucryptolib) }, #endif #if MICROPY_PY_UBINASCII { MP_ROM_QSTR(MP_QSTR_ubinascii), MP_ROM_PTR(&mp_module_ubinascii) }, #endif #if MICROPY_PY_URANDOM { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&mp_module_urandom) }, #endif #if MICROPY_PY_USELECT { MP_ROM_QSTR(MP_QSTR_uselect), MP_ROM_PTR(&mp_module_uselect) }, #endif #if MICROPY_PY_USSL { MP_ROM_QSTR(MP_QSTR_ussl), MP_ROM_PTR(&mp_module_ussl) }, #endif #if MICROPY_PY_LWIP { MP_ROM_QSTR(MP_QSTR_lwip), MP_ROM_PTR(&mp_module_lwip) }, #endif #if MICROPY_PY_UWEBSOCKET { MP_ROM_QSTR(MP_QSTR_uwebsocket), MP_ROM_PTR(&mp_module_uwebsocket) }, #endif #if MICROPY_PY_WEBREPL { MP_ROM_QSTR(MP_QSTR__webrepl), MP_ROM_PTR(&mp_module_webrepl) }, #endif #if MICROPY_PY_FRAMEBUF { MP_ROM_QSTR(MP_QSTR_framebuf), MP_ROM_PTR(&mp_module_framebuf) }, #endif #if MICROPY_PY_BTREE { MP_ROM_QSTR(MP_QSTR_btree), MP_ROM_PTR(&mp_module_btree) }, #endif #if MICROPY_PY_BLUETOOTH { MP_ROM_QSTR(MP_QSTR_ubluetooth), MP_ROM_PTR(&mp_module_ubluetooth) }, #endif // extra builtin modules as defined by a port MICROPY_PORT_BUILTIN_MODULES #ifdef MICROPY_REGISTERED_MODULES // builtin modules declared with MP_REGISTER_MODULE() MICROPY_REGISTERED_MODULES #endif }; MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table); // returns MP_OBJ_NULL if not found mp_obj_t mp_module_get(qstr module_name) { mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; // lookup module mp_map_elem_t *el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); if (el == NULL) { // module not found, look for builtin module names el = mp_map_lookup((mp_map_t *)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); if (el == NULL) { return MP_OBJ_NULL; } mp_module_call_init(module_name, el->value); } // module found, return it return el->value; } void mp_module_register(qstr qst, mp_obj_t module) { 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(qst), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = module; } #if MICROPY_MODULE_WEAK_LINKS // Search for u"foo" in built-in modules, return MP_OBJ_NULL if not found mp_obj_t mp_module_search_umodule(const char *module_str) { for (size_t i = 0; i < MP_ARRAY_SIZE(mp_builtin_module_table); ++i) { const mp_map_elem_t *entry = (const mp_map_elem_t *)&mp_builtin_module_table[i]; const char *key = qstr_str(MP_OBJ_QSTR_VALUE(entry->key)); if (key[0] == 'u' && strcmp(&key[1], module_str) == 0) { return (mp_obj_t)entry->value; } } return MP_OBJ_NULL; } #endif #if MICROPY_MODULE_BUILTIN_INIT void mp_module_call_init(qstr module_name, mp_obj_t module_obj) { // Look for __init__ and call it if it exists mp_obj_t dest[2]; mp_load_method_maybe(module_obj, MP_QSTR___init__, dest); if (dest[0] != MP_OBJ_NULL) { mp_call_method_n_kw(0, 0, dest); // Register module so __init__ is not called again. // If a module can be referenced by more than one name (eg due to weak links) // then __init__ will still be called for each distinct import, and it's then // up to the particular module to make sure it's __init__ code only runs once. mp_module_register(module_name, module_obj); } } #endif ================================================ FILE: py/objmodule.h ================================================ /* * 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" extern const mp_map_t mp_builtin_module_map; mp_obj_t mp_module_get(qstr module_name); void mp_module_register(qstr qstr, mp_obj_t module); mp_obj_t mp_module_search_umodule(const char *module_str); #if MICROPY_MODULE_BUILTIN_INIT void mp_module_call_init(qstr module_name, mp_obj_t module_obj); #else static inline void mp_module_call_init(qstr module_name, mp_obj_t module_obj) { (void)module_name; (void)module_obj; } #endif #endif // MICROPY_INCLUDED_PY_OBJMODULE_H ================================================ FILE: py/objnamedtuple.c ================================================ /* * 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"), type->base.name, num_fields, n_args + n_kw); #endif } // Create a tuple and set the type to this namedtuple mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_fields, NULL)); tuple->base.type = type_in; // 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_var(mp_obj_namedtuple_type_t, qstr, n_fields); memset(&o->base, 0, sizeof(o->base)); 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); o->base.base.type = &mp_type_type; o->base.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE; // can match tuple o->base.name = name; o->base.print = namedtuple_print; o->base.make_new = namedtuple_make_new; o->base.unary_op = mp_obj_tuple_unary_op; o->base.binary_op = mp_obj_tuple_binary_op; o->base.attr = namedtuple_attr; o->base.subscr = mp_obj_tuple_subscr; o->base.getiter = mp_obj_tuple_getiter; o->base.parent = &mp_type_tuple; 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 ================================================ FILE: py/objnamedtuple.h ================================================ /* * 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 { mp_obj_type_t base; 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 ================================================ FILE: py/objnone.c ================================================ /* * 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_UJSON && kind == PRINT_JSON) { mp_print_str(print, "null"); } else { mp_print_str(print, "None"); } } const mp_obj_type_t mp_type_NoneType = { { &mp_type_type }, .name = MP_QSTR_NoneType, .print = none_print, .unary_op = mp_generic_unary_op, }; #if !MICROPY_OBJ_IMMEDIATE_OBJS const mp_obj_none_t mp_const_none_obj = {{&mp_type_NoneType}}; #endif ================================================ FILE: py/objobject.c ================================================ /* * 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 = m_new_obj(mp_obj_object_t); o->base.type = 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 const mp_obj_type_t mp_type_object = { { &mp_type_type }, .name = MP_QSTR_object, .make_new = object_make_new, #if MICROPY_CPYTHON_COMPAT .locals_dict = (mp_obj_dict_t *)&object_locals_dict, #endif }; ================================================ FILE: py/objpolyiter.c ================================================ /* * 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); } const mp_obj_type_t mp_type_polymorph_iter = { { &mp_type_type }, .name = MP_QSTR_iterator, .getiter = mp_identity_getiter, .iternext = polymorph_it_iternext, }; ================================================ FILE: py/objproperty.c ================================================ /* * 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 = m_new_obj(mp_obj_property_t); o->base.type = 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); const mp_obj_type_t mp_type_property = { { &mp_type_type }, .name = MP_QSTR_property, .make_new = property_make_new, .locals_dict = (mp_obj_dict_t *)&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 ================================================ FILE: py/objrange.c ================================================ /* * 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 const mp_obj_type_t range_it_type = { { &mp_type_type }, .name = MP_QSTR_iterator, .getiter = mp_identity_getiter, .iternext = 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 = &range_it_type; 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 = m_new_obj(mp_obj_range_t); o->base.type = 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 = m_new_obj(mp_obj_range_t); o->base.type = &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 const mp_obj_type_t mp_type_range = { { &mp_type_type }, .name = MP_QSTR_range, .print = range_print, .make_new = range_make_new, .unary_op = range_unary_op, #if MICROPY_PY_BUILTINS_RANGE_BINOP .binary_op = range_binary_op, #endif .subscr = range_subscr, .getiter = range_getiter, #if MICROPY_PY_BUILTINS_RANGE_ATTRS .attr = range_attr, #endif }; ================================================ FILE: py/objreversed.c ================================================ /* * 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 = m_new_obj(mp_obj_reversed_t); o->base.type = 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); } const mp_obj_type_t mp_type_reversed = { { &mp_type_type }, .name = MP_QSTR_reversed, .make_new = reversed_make_new, .getiter = mp_identity_getiter, .iternext = reversed_iternext, }; #endif // MICROPY_PY_BUILTINS_REVERSED ================================================ FILE: py/objset.c ================================================ /* * 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 = m_new_obj(mp_obj_set_t); other->base.type = 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) { nlr_raise(mp_obj_new_exception_arg1(&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); const mp_obj_type_t mp_type_set = { { &mp_type_type }, .name = MP_QSTR_set, .print = set_print, .make_new = set_make_new, .unary_op = set_unary_op, .binary_op = set_binary_op, .getiter = set_getiter, .locals_dict = (mp_obj_dict_t *)&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); const mp_obj_type_t mp_type_frozenset = { { &mp_type_type }, .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, .name = MP_QSTR_frozenset, .print = set_print, .make_new = set_make_new, .unary_op = set_unary_op, .binary_op = set_binary_op, .getiter = set_getiter, .locals_dict = (mp_obj_dict_t *)&frozenset_locals_dict, }; #endif mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) { mp_obj_set_t *o = m_new_obj(mp_obj_set_t); o->base.type = &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 ================================================ FILE: py/objsingleton.c ================================================ /* * 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); } const mp_obj_type_t mp_type_singleton = { { &mp_type_type }, .name = MP_QSTR_, .print = singleton_print, .unary_op = mp_generic_unary_op, }; 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 ================================================ FILE: py/objslice.c ================================================ /* * 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, ")"); } #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_int_get_checked(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 const mp_obj_type_t mp_type_slice = { { &mp_type_type }, .name = MP_QSTR_slice, .print = slice_print, #if MICROPY_PY_BUILTINS_SLICE_ATTRS .attr = slice_attr, #elif MICROPY_PY_BUILTINS_SLICE_INDICES .locals_dict = (mp_obj_dict_t *)&slice_locals_dict, #endif }; mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) { mp_obj_slice_t *o = m_new_obj(mp_obj_slice_t); o->base.type = &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 ================================================ FILE: py/objstr.c ================================================ /* * 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); /******************************************************************************/ /* 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_UJSON 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_UJSON 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)) { mp_printf(print, "%.*s", str_len, str_data); } else { if (is_bytes) { mp_print_str(print, "b"); } 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_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); #if MICROPY_PY_BUILTINS_STR_UNICODE_CHECK if (!utf8_check(bufinfo.buf, bufinfo.len)) { mp_raise_msg(&mp_type_UnicodeError, NULL); } #endif 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) { goto wrong_args; } 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_str_from_vstr(&mp_type_bytes, &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_str_from_vstr(&mp_type_bytes, &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_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) bad_implicit_conversion(rhs_in); } 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_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) { mp_check_self(mp_obj_is_str_or_bytes(self_in)); const mp_obj_type_t *self_type = mp_obj_get_type(self_in); // 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_type_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; for (size_t i = 0; i < seq_len; i++) { if (mp_obj_get_type(seq_items[i]) != 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_from_vstr(self_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 if (mp_obj_get_type(sep) != self_type) { bad_implicit_conversion(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]); mp_check_self(mp_obj_is_str_or_bytes(args[0])); // check argument type if (mp_obj_get_type(args[1]) != self_type) { bad_implicit_conversion(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) { mp_check_self(mp_obj_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 { if (mp_obj_get_type(args[1]) != self_type) { bad_implicit_conversion(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_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) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, field_q)); } arg = key_elem->value; } if (field_name < field_name_top) { mp_raise_NotImplementedError(MP_ERROR_TEXT("attributes not supported yet")); } } 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_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) { 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) { mp_check_self(mp_obj_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_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) { mp_check_self(mp_obj_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 (arg_i != n_args) { mp_raise_TypeError(MP_ERROR_TEXT("format string didn't convert all arguments")); } return mp_obj_new_str_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) { mp_check_self(mp_obj_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]); if (mp_obj_get_type(args[1]) != self_type) { bad_implicit_conversion(args[1]); } if (mp_obj_get_type(args[2]) != self_type) { bad_implicit_conversion(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_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]); mp_check_self(mp_obj_is_str_or_bytes(args[0])); // check argument type if (mp_obj_get_type(args[1]) != self_type) { bad_implicit_conversion(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); } // 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 = utf8_next_char(haystack_ptr); } } 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) { mp_check_self(mp_obj_is_str_or_bytes(self_in)); const mp_obj_type_t *self_type = mp_obj_get_type(self_in); if (self_type != mp_obj_get_type(arg)) { bad_implicit_conversion(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; } 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_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 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; } } STATIC const mp_rom_map_elem_t str8_locals_dict_table[] = { #if MICROPY_CPYTHON_COMPAT { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&bytes_decode_obj) }, #if !MICROPY_PY_BUILTINS_STR_UNICODE // If we have separate unicode type, then here we have methods only // for bytes type, and it should not have encode() methods. Otherwise, // we have non-compliant-but-practical bytestring type, which shares // method table with bytes, so they both have encode() and decode() // methods (which should do type checking at runtime). { MP_ROM_QSTR(MP_QSTR_encode), MP_ROM_PTR(&str_encode_obj) }, #endif #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) }, }; STATIC MP_DEFINE_CONST_DICT(str8_locals_dict, str8_locals_dict_table); #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); const mp_obj_type_t mp_type_str = { { &mp_type_type }, .name = MP_QSTR_str, .print = str_print, .make_new = mp_obj_str_make_new, .binary_op = mp_obj_str_binary_op, .subscr = bytes_subscr, .getiter = mp_obj_new_str_iterator, .buffer_p = { .get_buffer = mp_obj_str_get_buffer }, .locals_dict = (mp_obj_dict_t *)&str8_locals_dict, }; #endif // Reuses most of methods from str const mp_obj_type_t mp_type_bytes = { { &mp_type_type }, .name = MP_QSTR_bytes, .print = str_print, .make_new = bytes_make_new, .binary_op = mp_obj_str_binary_op, .subscr = bytes_subscr, .getiter = mp_obj_new_bytes_iterator, .buffer_p = { .get_buffer = mp_obj_str_get_buffer }, .locals_dict = (mp_obj_dict_t *)&str8_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 = m_new_obj(mp_obj_str_t); o->base.type = 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); } 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. mp_obj_t mp_obj_new_str_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); } } // make a new str/bytes object mp_obj_str_t *o = m_new_obj(mp_obj_str_t); o->base.type = type; o->len = vstr->len; o->hash = qstr_compute_hash((byte *)vstr->buf, vstr->len); if (vstr->len + 1 == vstr->alloc) { o->data = (byte *)vstr->buf; } else { o->data = (byte *)m_renew(char, vstr->buf, vstr->alloc, vstr->len + 1); } ((byte *)o->data)[o->len] = '\0'; // add null byte vstr->buf = NULL; vstr->alloc = 0; return MP_OBJ_FROM_PTR(o); } mp_obj_t mp_obj_new_str(const char *data, size_t len) { 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_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 { *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); } ================================================ FILE: py/objstr.h ================================================ /* * 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" typedef struct _mp_obj_str_t { mp_obj_base_t base; mp_uint_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; #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) \ mp_uint_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 { 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); mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte *data, size_t len); 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); 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); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_encode_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rfind_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_index_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rindex_obj); MP_DECLARE_CONST_FUN_OBJ_2(str_join_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_split_obj); MP_DECLARE_CONST_FUN_OBJ_KW(str_splitlines_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rsplit_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_startswith_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_strip_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_lstrip_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rstrip_obj); MP_DECLARE_CONST_FUN_OBJ_KW(str_format_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_replace_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_count_obj); MP_DECLARE_CONST_FUN_OBJ_2(str_partition_obj); MP_DECLARE_CONST_FUN_OBJ_2(str_rpartition_obj); MP_DECLARE_CONST_FUN_OBJ_2(str_center_obj); MP_DECLARE_CONST_FUN_OBJ_1(str_lower_obj); MP_DECLARE_CONST_FUN_OBJ_1(str_upper_obj); MP_DECLARE_CONST_FUN_OBJ_1(str_isspace_obj); MP_DECLARE_CONST_FUN_OBJ_1(str_isalpha_obj); MP_DECLARE_CONST_FUN_OBJ_1(str_isdigit_obj); MP_DECLARE_CONST_FUN_OBJ_1(str_isupper_obj); MP_DECLARE_CONST_FUN_OBJ_1(str_islower_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(bytes_decode_obj); #endif // MICROPY_INCLUDED_PY_OBJSTR_H ================================================ FILE: py/objstringio.c ================================================ /* * 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_t stringio___exit__(size_t n_args, const mp_obj_t *args) { (void)n_args; return mp_stream_close(args[0]); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stringio___exit___obj, 4, 4, stringio___exit__); STATIC mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) { mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t); o->base.type = 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(&stringio___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, }; const mp_obj_type_t mp_type_stringio = { { &mp_type_type }, .name = MP_QSTR_StringIO, .print = stringio_print, .make_new = stringio_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &stringio_stream_p, .locals_dict = (mp_obj_dict_t *)&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, }; const mp_obj_type_t mp_type_bytesio = { { &mp_type_type }, .name = MP_QSTR_BytesIO, .print = stringio_print, .make_new = stringio_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &bytesio_stream_p, .locals_dict = (mp_obj_dict_t *)&stringio_locals_dict, }; #endif #endif ================================================ FILE: py/objstringio.h ================================================ /* * 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 ================================================ FILE: py/objstrunicode.c ================================================ /* * 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_UJSON if (kind == PRINT_JSON) { mp_str_print_json(print, str_data, str_len); return; } #endif if (kind == PRINT_STR) { mp_printf(print, "%.*s", str_len, str_data); } 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) { // 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 } } STATIC const mp_rom_map_elem_t struni_locals_dict_table[] = { #if MICROPY_CPYTHON_COMPAT { MP_ROM_QSTR(MP_QSTR_encode), MP_ROM_PTR(&str_encode_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) }, }; STATIC MP_DEFINE_CONST_DICT(struni_locals_dict, struni_locals_dict_table); const mp_obj_type_t mp_type_str = { { &mp_type_type }, .name = MP_QSTR_str, .print = uni_print, .make_new = mp_obj_str_make_new, .unary_op = uni_unary_op, .binary_op = mp_obj_str_binary_op, .subscr = str_subscr, .getiter = mp_obj_new_str_iterator, .buffer_p = { .get_buffer = mp_obj_str_get_buffer }, .locals_dict = (mp_obj_dict_t *)&struni_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 ================================================ FILE: py/objtuple.c ================================================ /* * 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_get_type(o)->getiter == 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); if (MICROPY_PY_UJSON && kind == PRINT_JSON) { mp_print_str(print, "["); } else { mp_print_str(print, "("); kind = PRINT_REPR; } for (size_t i = 0; i < o->len; i++) { if (i > 0) { mp_print_str(print, ", "); } mp_obj_print_helper(print, o->items[i], kind); } if (MICROPY_PY_UJSON && 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 (another_type->getiter != 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); const mp_obj_type_t mp_type_tuple = { { &mp_type_type }, .name = MP_QSTR_tuple, .print = mp_obj_tuple_print, .make_new = mp_obj_tuple_make_new, .unary_op = mp_obj_tuple_unary_op, .binary_op = mp_obj_tuple_binary_op, .subscr = mp_obj_tuple_subscr, .getiter = mp_obj_tuple_getiter, .locals_dict = (mp_obj_dict_t *)&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 = m_new_obj_var(mp_obj_tuple_t, mp_obj_t, n); o->base.type = &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, 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); } ================================================ FILE: py/objtuple.h ================================================ /* * 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 ================================================ FILE: py/objtype.c ================================================ /* * 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 (type->parent == NULL) { // No parents so end search here. return count; #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t *)type->parent)->type == &mp_type_tuple) { // Multiple parents, search through them all recursively. const mp_obj_tuple_t *parent_tuple = 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 = 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] = 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 = m_new_obj_var(mp_obj_instance_t, mp_obj_t, num_native_bases); o->base.type = 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->meth_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 meth_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->meth_offset != 0 && mp_obj_is_native_type(type)) { if (*(void **)((char *)type + lookup->meth_offset) != NULL) { DEBUG_printf("mp_obj_class_lookup: Matched special meth slot (off=%d) for %s\n", lookup->meth_offset, qstr_str(lookup->attr)); lookup->dest[0] = MP_OBJ_SENTINEL; return; } } if (type->locals_dict != NULL) { // search locals_dict (the set of methods/attributes) assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(type->locals_dict))); // MicroPython restriction, for now mp_map_t *locals_map = &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 (type->parent == NULL) { DEBUG_printf("mp_obj_class_lookup: No more parents\n"); return; #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t *)type->parent)->type == &mp_type_tuple) { const mp_obj_tuple_t *parent_tuple = 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 = 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, .meth_offset = offsetof(mp_obj_type_t, 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.meth_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); } 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__, .meth_offset = offsetof(mp_obj_type_t, 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.meth_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] = 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. 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] = 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_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, .meth_offset = offsetof(mp_obj_type_t, 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: // 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. 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); retry:; 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, .meth_offset = offsetof(mp_obj_type_t, 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); } else { // If this was an inplace method, fallback to 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_INPLACE_OR - MP_BINARY_OP_OR; goto retry; } 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". As // MicroPython doesn't implement any fallbacks, signal to raise // TypeError right away. 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); 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, .meth_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 straightaway 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, .meth_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, .meth_offset = offsetof(mp_obj_type_t, 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__, .meth_offset = offsetof(mp_obj_type_t, 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__, .meth_offset = offsetof(mp_obj_type_t, getiter), .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 (iter_buf == NULL) { iter_buf = m_new_obj(mp_obj_iter_buf_t); } return type->getiter(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 .meth_offset = offsetof(mp_obj_type_t, buffer_p.get_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 type->buffer_p.get_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 (self->make_new == NULL) { #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 = 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 = 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 = self->parent ? MP_OBJ_FROM_PTR(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, .meth_offset = 0, .dest = dest, .is_type = true, }; mp_obj_class_lookup(&lookup, self); } else { // delete/store attribute if (self->locals_dict != NULL) { assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(self->locals_dict))); // MicroPython restriction, for now mp_map_t *locals_map = &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 } } } } const mp_obj_type_t mp_type_type = { { &mp_type_type }, .name = MP_QSTR_type, .print = type_print, .make_new = type_make_new, .call = type_call, .unary_op = mp_generic_unary_op, .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; 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 (t->make_new == NULL) { #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 } mp_obj_type_t *o = m_new0(mp_obj_type_t, 1); o->base.type = &mp_type_type; o->flags = base_flags; o->name = name; o->print = instance_print; o->make_new = mp_obj_instance_make_new; o->call = mp_obj_instance_call; o->unary_op = instance_unary_op; o->binary_op = instance_binary_op; o->attr = mp_obj_instance_attr; o->subscr = instance_subscr; o->getiter = mp_obj_instance_getiter; // o->iternext = ; not implemented o->buffer_p.get_buffer = instance_get_buffer; if (bases_len > 0) { // 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. o->protocol = ((mp_obj_type_t *)MP_OBJ_TO_PTR(bases_items[0]))->protocol; if (bases_len >= 2) { #if MICROPY_MULTIPLE_INHERITANCE o->parent = MP_OBJ_TO_PTR(bases_tuple); #else mp_raise_NotImplementedError(MP_ERROR_TEXT("multiple inheritance not supported")); #endif } else { o->parent = MP_OBJ_TO_PTR(bases_items[0]); } } o->locals_dict = MP_OBJ_TO_PTR(locals_dict); #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 < o->locals_dict->map.alloc; i++) { if (mp_map_slot_is_filled(&o->locals_dict->map, i)) { const mp_map_elem_t *elem = &o->locals_dict->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 = &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, .meth_offset = 0, .dest = dest, .is_type = false, }; // Allow a call super().__init__() to reach any native base classes if (attr == MP_QSTR___init__) { lookup.meth_offset = offsetof(mp_obj_type_t, make_new); } if (type->parent == NULL) { // no parents, do nothing #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t *)type->parent)->type == &mp_type_tuple) { const mp_obj_tuple_t *parent_tuple = 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 (type->parent != &mp_type_object) { mp_obj_class_lookup(&lookup, 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 meth_offset so we don't look up any native methods in object, // because object never takes up the native base-class slot. lookup.meth_offset = 0; mp_obj_class_lookup(&lookup, &mp_type_object); } const mp_obj_type_t mp_type_super = { { &mp_type_type }, .name = MP_QSTR_super, .print = super_print, .make_new = super_make_new, .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 (self->parent == NULL) { // type has no parents return false; #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t *)self->parent)->type == &mp_type_tuple) { // get the base objects (they should be type objects) const mp_obj_tuple_t *parent_tuple = 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(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); } const mp_obj_type_t mp_type_staticmethod = { { &mp_type_type }, .name = MP_QSTR_staticmethod, .make_new = static_class_method_make_new, }; const mp_obj_type_t mp_type_classmethod = { { &mp_type_type }, .name = MP_QSTR_classmethod, .make_new = static_class_method_make_new, }; ================================================ FILE: py/objtype.h ================================================ /* * 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)->make_new == mp_obj_instance_make_new) #define mp_obj_is_native_type(type) ((type)->make_new != mp_obj_instance_make_new) // this needs to be exposed for the above macros to work correctly mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); // 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 ================================================ FILE: py/objzip.c ================================================ /* * 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 = m_new_obj_var(mp_obj_zip_t, mp_obj_t, n_args); o->base.type = 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); } const mp_obj_type_t mp_type_zip = { { &mp_type_type }, .name = MP_QSTR_zip, .make_new = zip_make_new, .getiter = mp_identity_getiter, .iternext = zip_iternext, }; ================================================ FILE: py/opmethods.c ================================================ /* * 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); return 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); return 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); return 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); return type->binary_op(MP_BINARY_OP_CONTAINS, lhs_in, rhs_in); } MP_DEFINE_CONST_FUN_OBJ_2(mp_op_contains_obj, op_contains); ================================================ FILE: py/pairheap.c ================================================ /* * 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; } ================================================ FILE: py/pairheap.h ================================================ /* * 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 ================================================ FILE: py/parse.c ================================================ /* * 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 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; } 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; } bool mp_parse_node_is_const_false(mp_parse_node_t pn) { return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE) || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0); } bool mp_parse_node_is_const_true(mp_parse_node_t pn) { return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE) || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) != 0); } 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; #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D // nodes are 32-bit pointers, but need to extract 64-bit object *o = (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32); #else *o = (mp_obj_t)pns->nodes[0]; #endif return mp_obj_is_int(*o); } else { return false; } } 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; case MP_PARSE_NODE_BYTES: mp_printf(print, "bytes(%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) { #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D mp_printf(print, "literal const(%016llx)\n", (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32)); #else mp_printf(print, "literal const(%p)\n", (mp_obj_t)pns->nodes[0]); #endif } 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; } STATIC mp_parse_node_t mp_parse_node_new_small_int_checked(parser_t *parser, mp_obj_t o_val) { (void)parser; mp_int_t val = MP_OBJ_SMALL_INT_VALUE(o_val); #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, 0, o_val); } #endif return mp_parse_node_new_small_int(val); } 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) { if (mp_obj_is_small_int(elem->value)) { pn = mp_parse_node_new_small_int_checked(parser, elem->value); } else { pn = make_node_const_object(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); if (mp_obj_is_small_int(o)) { pn = mp_parse_node_new_small_int_checked(parser, o); } else { pn = make_node_const_object(parser, lex->tok_line, o); } } else if (lex->tok_kind == MP_TOKEN_FLOAT_OR_IMAG) { mp_obj_t o = mp_parse_num_decimal(lex->vstr.buf, lex->vstr.len, true, false, lex); pn = make_node_const_object(parser, lex->tok_line, o); } else if (lex->tok_kind == MP_TOKEN_STRING || lex->tok_kind == MP_TOKEN_BYTES) { // Don't automatically intern all strings/bytes. 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(lex->tok_kind == MP_TOKEN_STRING ? MP_PARSE_NODE_STRING : MP_PARSE_NODE_BYTES, qst); } else { // not interned, make a node holding a pointer to the string/bytes object mp_obj_t o = mp_obj_new_str_copy( lex->tok_kind == MP_TOKEN_STRING ? &mp_type_str : &mp_type_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_MODULE_CONST STATIC const mp_rom_map_elem_t mp_constants_table[] = { #if MICROPY_PY_UERRNO { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_module_uerrno) }, #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 STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args); #if MICROPY_COMP_CONST_FOLDING 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]; mp_obj_t value; if (!mp_parse_node_get_int_maybe(pn_value, &value)) { mp_obj_t exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, MP_ERROR_TEXT("constant must be an integer")); mp_obj_exception_add_traceback(exc, parser->lexer->source_name, ((mp_parse_node_struct_t *)pn1)->source_line, MP_QSTRnull); nlr_raise(exc); } // 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); } if (mp_obj_is_small_int(arg0)) { push_result_node(parser, mp_parse_node_new_small_int_checked(parser, arg0)); } else { // TODO reuse memory for parse node struct? push_result_node(parser, make_node_const_object(parser, 0, arg0)); } return true; } #endif STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args) { // optimise away parenthesis around an expression if possible if (rule_id == RULE_atom_paren) { // there should be just 1 arg for this rule 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; } } #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 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); } 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) { // 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")); } 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); // we also free the lexer on behalf of the caller mp_lexer_free(lex); 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; } } #endif // MICROPY_ENABLE_COMPILER ================================================ FILE: py/parse.h ================================================ /* * 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 string of bytes; bits 4 and above are the qstr holding the value // - xx...xx1110: 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_BYTES (0x0a) #define MP_PARSE_NODE_TOKEN (0x0e) 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)); } 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 ================================================ FILE: py/parsenum.c ================================================ /* * 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_vstr(&mp_type_str, &vstr)); raise_exc(exc, lex); #endif } } typedef enum { PARSE_DEC_IN_INTG, PARSE_DEC_IN_FRAC, PARSE_DEC_IN_EXP, } parse_dec_in_t; mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex) { #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 const char *top = str + len; mp_float_t dec_val = 0; bool dec_neg = false; bool imag = false; // 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; 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 (dec_val < DEC_VAL_MAX) { // dec_val won't overflow so keep accumulating dec_val = 10 * dec_val + dig; if (in == PARSE_DEC_IN_FRAC) { --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) { ++exp_extra; } } } } 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 (allow_imag && (dig | 0x20) == 'j') { imag = true; break; } 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; 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); } } // 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) { goto value_error; } // return the object #if MICROPY_PY_BUILTINS_COMPLEX if (imag) { return mp_obj_new_complex(0, dec_val); } else if (force_complex) { return mp_obj_new_complex(dec_val, 0); } #else if (imag || force_complex) { raise_exc(mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("complex values not supported")), lex); } #endif else { 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 } ================================================ FILE: py/parsenum.h ================================================ /* * 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); mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex); #endif // MICROPY_INCLUDED_PY_PARSENUM_H ================================================ FILE: py/parsenumbase.c ================================================ /* * 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; } ================================================ FILE: py/parsenumbase.h ================================================ /* * 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 ================================================ FILE: py/persistentcode.c ================================================ /* * 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" #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 #if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER) // The bytecode will depend on the number of bits in a small-int, and // this function computes that (could make it a fixed constant, but it // would need to be defined in mpconfigport.h). STATIC int mp_small_int_bits(void) { mp_int_t i = MP_SMALL_INT_MAX; int n = 1; while (i != 0) { i >>= 1; ++n; } return n; } #endif #define QSTR_WINDOW_SIZE (32) typedef struct _qstr_window_t { uint16_t idx; // indexes the head of the window uint16_t window[QSTR_WINDOW_SIZE]; } qstr_window_t; // Push a qstr to the head of the window, and the tail qstr is overwritten STATIC void qstr_window_push(qstr_window_t *qw, qstr qst) { qw->idx = (qw->idx + 1) % QSTR_WINDOW_SIZE; qw->window[qw->idx] = qst; } // Pull an existing qstr from within the window to the head of the window STATIC qstr qstr_window_pull(qstr_window_t *qw, size_t idx) { qstr qst = qw->window[idx]; if (idx > qw->idx) { memmove(&qw->window[idx], &qw->window[idx + 1], (QSTR_WINDOW_SIZE - idx - 1) * sizeof(uint16_t)); qw->window[QSTR_WINDOW_SIZE - 1] = qw->window[0]; idx = 0; } memmove(&qw->window[idx], &qw->window[idx + 1], (qw->idx - idx) * sizeof(uint16_t)); qw->window[qw->idx] = qst; return qst; } #if MICROPY_PERSISTENT_CODE_LOAD // Access a qstr at the given index, relative to the head of the window (0=head) STATIC qstr qstr_window_access(qstr_window_t *qw, size_t idx) { return qstr_window_pull(qw, (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE); } #endif #if MICROPY_PERSISTENT_CODE_SAVE // Insert a qstr at the head of the window, either by pulling an existing one or pushing a new one STATIC size_t qstr_window_insert(qstr_window_t *qw, qstr qst) { for (size_t idx = 0; idx < QSTR_WINDOW_SIZE; ++idx) { if (qw->window[idx] == qst) { qstr_window_pull(qw, idx); return (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE; } } qstr_window_push(qw, qst); return QSTR_WINDOW_SIZE; } #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; // ip will point to start of opcodes // return value will point to simple_name, source_file qstrs STATIC byte *extract_prelude(const byte **ip, bytecode_prelude_t *prelude) { 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); byte *ip_info = (byte *)*ip; *ip += n_info; *ip += n_cell; return ip_info; } #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, byte **out); #if MICROPY_EMIT_MACHINE_CODE typedef struct _reloc_info_t { mp_reader_t *reader; mp_uint_t *const_table; } reloc_info_t; #if MICROPY_EMIT_THUMB STATIC void asm_thumb_rewrite_mov(uint8_t *pc, uint16_t val) { // high part *(uint16_t *)pc = (*(uint16_t *)pc & 0xfbf0) | (val >> 1 & 0x0400) | (val >> 12); // low part *(uint16_t *)(pc + 2) = (*(uint16_t *)(pc + 2) & 0x0f00) | (val << 4 & 0x7000) | (val & 0x00ff); } #endif STATIC void arch_link_qstr(uint8_t *pc, bool is_obj, qstr qst) { mp_uint_t val = qst; if (is_obj) { val = (mp_uint_t)MP_OBJ_NEW_QSTR(qst); } #if MICROPY_EMIT_X86 || MICROPY_EMIT_X64 || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN pc[0] = val & 0xff; pc[1] = (val >> 8) & 0xff; pc[2] = (val >> 16) & 0xff; pc[3] = (val >> 24) & 0xff; #elif MICROPY_EMIT_THUMB if (is_obj) { // qstr object, movw and movt asm_thumb_rewrite_mov(pc, val); // movw asm_thumb_rewrite_mov(pc + 4, val >> 16); // movt } else { // qstr number, movw instruction asm_thumb_rewrite_mov(pc, val); // movw } #endif } 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, NULL); 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->const_table[1])[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, NULL); } op >>= 1; if (op == 0) { // Destination is text dest = reloc_text; } else { // Destination is rodata (op=1) or bss (op=1 if no rodata, else op=2) dest = ri->const_table[op]; } } else if (op == 6) { // 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 - 7]; } 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, byte **out) { size_t unum = 0; for (;;) { byte b = reader->readbyte(reader->data); if (out != NULL) { **out = b; ++*out; } unum = (unum << 7) | (b & 0x7f); if ((b & 0x80) == 0) { break; } } return unum; } STATIC qstr load_qstr(mp_reader_t *reader, qstr_window_t *qw) { size_t len = read_uint(reader, NULL); if (len == 0) { // static qstr return read_byte(reader); } if (len & 1) { // qstr in window return qstr_window_access(qw, len >> 1); } len >>= 1; char *str = m_new(char, len); read_bytes(reader, (byte *)str, len); qstr qst = qstr_from_strn(str, len); m_del(char, str, len); qstr_window_push(qw, qst); return qst; } STATIC mp_obj_t load_obj(mp_reader_t *reader) { byte obj_type = read_byte(reader); if (obj_type == 'e') { return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); } else { size_t len = read_uint(reader, NULL); vstr_t vstr; vstr_init_len(&vstr, len); read_bytes(reader, (byte *)vstr.buf, len); if (obj_type == 's' || obj_type == 'b') { return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr); } else if (obj_type == 'i') { return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } else { assert(obj_type == 'f' || obj_type == 'c'); return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == 'c', false, NULL); } } } STATIC void load_prelude_qstrs(mp_reader_t *reader, qstr_window_t *qw, byte *ip) { qstr simple_name = load_qstr(reader, qw); ip[0] = simple_name; ip[1] = simple_name >> 8; qstr source_file = load_qstr(reader, qw); ip[2] = source_file; ip[3] = source_file >> 8; } STATIC void load_prelude(mp_reader_t *reader, qstr_window_t *qw, byte **ip, bytecode_prelude_t *prelude) { // Read in the prelude header byte *ip_read = *ip; read_uint(reader, &ip_read); // read in n_state/etc (is effectively a var-uint) read_uint(reader, &ip_read); // read in n_info/n_cell (is effectively a var-uint) // Prelude header has been read into *ip, now decode and extract values from it extract_prelude((const byte **)ip, prelude); // Load qstrs in prelude load_prelude_qstrs(reader, qw, ip_read); ip_read += 4; // Read remaining code info read_bytes(reader, ip_read, *ip - ip_read); } STATIC void load_bytecode(mp_reader_t *reader, qstr_window_t *qw, byte *ip, byte *ip_top) { while (ip < ip_top) { *ip = read_byte(reader); size_t sz; uint f = mp_opcode_format(ip, &sz, false); ++ip; --sz; if (f == MP_BC_FORMAT_QSTR) { qstr qst = load_qstr(reader, qw); *ip++ = qst; *ip++ = qst >> 8; sz -= 2; } else if (f == MP_BC_FORMAT_VAR_UINT) { while ((*ip++ = read_byte(reader)) & 0x80) { } } read_bytes(reader, ip, sz); ip += sz; } } STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { // Load function kind and data length size_t kind_len = read_uint(reader, NULL); int kind = (kind_len & 3) + MP_CODE_BYTECODE; size_t fun_data_len = kind_len >> 2; #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; bytecode_prelude_t prelude = {0}; #if MICROPY_EMIT_MACHINE_CODE size_t prelude_offset = 0; mp_uint_t type_sig = 0; size_t n_qstr_link = 0; #endif if (kind == MP_CODE_BYTECODE) { // Allocate memory for the bytecode fun_data = m_new(uint8_t, fun_data_len); // Load prelude byte *ip = fun_data; load_prelude(reader, qw, &ip, &prelude); // Load bytecode load_bytecode(reader, qw, ip, 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 || kind == MP_CODE_NATIVE_VIPER) { // Parse qstr link table and link native code n_qstr_link = read_uint(reader, NULL); for (size_t i = 0; i < n_qstr_link; ++i) { size_t off = read_uint(reader, NULL); qstr qst = load_qstr(reader, qw); uint8_t *dest = fun_data + (off >> 2); if ((off & 3) == 0) { // Generic 16-bit link dest[0] = qst & 0xff; dest[1] = (qst >> 8) & 0xff; } else if ((off & 3) == 3) { // Generic, aligned qstr-object link *(mp_obj_t *)dest = MP_OBJ_NEW_QSTR(qst); } else { // Architecture-specific link arch_link_qstr(dest, (off & 3) == 2, qst); } } } if (kind == MP_CODE_NATIVE_PY) { // Extract prelude for later use prelude_offset = read_uint(reader, NULL); const byte *ip = fun_data + prelude_offset; byte *ip_info = extract_prelude(&ip, &prelude); // Load qstrs in prelude load_prelude_qstrs(reader, qw, ip_info); } else { // Load basic scope info for viper and asm prelude.scope_flags = read_uint(reader, NULL); prelude.n_pos_args = 0; prelude.n_kwonly_args = 0; if (kind == MP_CODE_NATIVE_ASM) { prelude.n_pos_args = read_uint(reader, NULL); type_sig = read_uint(reader, NULL); } } #endif } size_t n_obj = 0; size_t n_raw_code = 0; mp_uint_t *const_table = NULL; if (kind != MP_CODE_NATIVE_ASM) { // Load constant table for bytecode, native and viper // Number of entries in constant table n_obj = read_uint(reader, NULL); n_raw_code = read_uint(reader, NULL); // Allocate constant table size_t n_alloc = prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code; #if MICROPY_EMIT_MACHINE_CODE if (kind != MP_CODE_BYTECODE) { ++n_alloc; // additional entry for mp_fun_table if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { ++n_alloc; // additional entry for rodata } if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { ++n_alloc; // additional entry for BSS } } #endif const_table = m_new(mp_uint_t, n_alloc); mp_uint_t *ct = const_table; // Load function argument names (initial entries in const_table) // (viper has n_pos_args=n_kwonly_args=0 so doesn't load any qstrs here) for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader, qw)); } #if MICROPY_EMIT_MACHINE_CODE if (kind != MP_CODE_BYTECODE) { // Populate mp_fun_table entry *ct++ = (mp_uint_t)(uintptr_t)&mp_fun_table; // Allocate and load rodata if needed if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { size_t size = read_uint(reader, NULL); uint8_t *rodata = m_new(uint8_t, size); read_bytes(reader, rodata, size); *ct++ = (uintptr_t)rodata; } // Allocate BSS if needed if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { size_t size = read_uint(reader, NULL); uint8_t *bss = m_new0(uint8_t, size); *ct++ = (uintptr_t)bss; } } #endif // Load constant objects and raw code children for (size_t i = 0; i < n_obj; ++i) { *ct++ = (mp_uint_t)load_obj(reader); } for (size_t i = 0; i < n_raw_code; ++i) { *ct++ = (mp_uint_t)(uintptr_t)load_raw_code(reader, qw); } } // Create raw_code and return it mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); if (kind == MP_CODE_BYTECODE) { // Assign bytecode to raw code object mp_emit_glue_assign_bytecode(rc, fun_data, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS fun_data_len, #endif const_table, #if MICROPY_PERSISTENT_CODE_SAVE n_obj, n_raw_code, #endif prelude.scope_flags); #if MICROPY_EMIT_MACHINE_CODE } else { // Relocate and commit code to executable address space reloc_info_t ri = {reader, const_table}; #if defined(MP_PLAT_COMMIT_EXEC) void *opt_ri = (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); #else if (prelude.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 // Assign native code to raw code object mp_emit_glue_assign_native(rc, kind, fun_data, fun_data_len, const_table, #if MICROPY_PERSISTENT_CODE_SAVE prelude_offset, n_obj, n_raw_code, n_qstr_link, NULL, #endif prelude.n_pos_args, prelude.scope_flags, type_sig); #endif } return rc; } mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { byte header[4]; read_bytes(reader, header, sizeof(header)); if (header[0] != 'M' || header[1] != MPY_VERSION || MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS || header[3] > mp_small_int_bits() || read_uint(reader, NULL) > QSTR_WINDOW_SIZE) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); if (!MPY_FEATURE_ARCH_TEST(arch)) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch")); } } qstr_window_t qw; qw.idx = 0; mp_raw_code_t *rc = load_raw_code(reader, &qw); reader->close(reader->data); return rc; } mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) { mp_reader_t reader; mp_reader_new_mem(&reader, buf, len, 0); return mp_raw_code_load(&reader); } #if MICROPY_HAS_FILE_READER mp_raw_code_t *mp_raw_code_load_file(const char *filename) { mp_reader_t reader; mp_reader_new_file(&reader, filename); return mp_raw_code_load(&reader); } #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 ((BYTES_PER_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_window_t *qw, qstr qst) { if (qst <= QSTR_LAST_STATIC) { // encode static qstr byte buf[2] = {0, qst & 0xff}; mp_print_bytes(print, buf, 2); return; } size_t idx = qstr_window_insert(qw, qst); if (idx < QSTR_WINDOW_SIZE) { // qstr found in window, encode index to it mp_print_uint(print, idx << 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); } STATIC void save_obj(mp_print_t *print, mp_obj_t o) { if (mp_obj_is_str_or_bytes(o)) { byte obj_type; if (mp_obj_is_str(o)) { obj_type = 's'; } else { obj_type = 'b'; } 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); } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) { byte obj_type = 'e'; mp_print_bytes(print, &obj_type, 1); } else { // we save numbers using a simplistic text representation // TODO could be improved byte obj_type; if (mp_obj_is_type(o, &mp_type_int)) { obj_type = 'i'; #if MICROPY_PY_BUILTINS_COMPLEX } else if (mp_obj_is_type(o, &mp_type_complex)) { obj_type = 'c'; #endif } else { assert(mp_obj_is_float(o)); obj_type = 'f'; } 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_prelude_qstrs(mp_print_t *print, qstr_window_t *qw, const byte *ip) { save_qstr(print, qw, ip[0] | (ip[1] << 8)); // simple_name save_qstr(print, qw, ip[2] | (ip[3] << 8)); // source_file } STATIC void save_bytecode(mp_print_t *print, qstr_window_t *qw, const byte *ip, const byte *ip_top) { while (ip < ip_top) { size_t sz; uint f = mp_opcode_format(ip, &sz, true); if (f == MP_BC_FORMAT_QSTR) { mp_print_bytes(print, ip, 1); qstr qst = ip[1] | (ip[2] << 8); save_qstr(print, qw, qst); ip += 3; sz -= 3; } mp_print_bytes(print, ip, sz); ip += sz; } } STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *qstr_window) { // Save function kind and data length mp_print_uint(print, (rc->fun_data_len << 2) | (rc->kind - MP_CODE_BYTECODE)); bytecode_prelude_t prelude; if (rc->kind == MP_CODE_BYTECODE) { // Extract prelude const byte *ip = rc->fun_data; const byte *ip_info = extract_prelude(&ip, &prelude); // Save prelude mp_print_bytes(print, rc->fun_data, ip_info - (const byte *)rc->fun_data); save_prelude_qstrs(print, qstr_window, ip_info); ip_info += 4; mp_print_bytes(print, ip_info, ip - ip_info); // Save bytecode const byte *ip_top = (const byte *)rc->fun_data + rc->fun_data_len; save_bytecode(print, qstr_window, ip, ip_top); #if MICROPY_EMIT_MACHINE_CODE } else { // Save native code mp_print_bytes(print, rc->fun_data, rc->fun_data_len); if (rc->kind == MP_CODE_NATIVE_PY || rc->kind == MP_CODE_NATIVE_VIPER) { // Save qstr link table for native code mp_print_uint(print, rc->n_qstr); for (size_t i = 0; i < rc->n_qstr; ++i) { mp_print_uint(print, rc->qstr_link[i].off); save_qstr(print, qstr_window, rc->qstr_link[i].qst); } } if (rc->kind == MP_CODE_NATIVE_PY) { // Save prelude size mp_print_uint(print, rc->prelude_offset); // Extract prelude and save qstrs in prelude const byte *ip = (const byte *)rc->fun_data + rc->prelude_offset; const byte *ip_info = extract_prelude(&ip, &prelude); save_prelude_qstrs(print, qstr_window, ip_info); } else { // Save basic scope info for viper and asm mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG); prelude.n_pos_args = 0; prelude.n_kwonly_args = 0; if (rc->kind == MP_CODE_NATIVE_ASM) { mp_print_uint(print, rc->n_pos_args); mp_print_uint(print, rc->type_sig); } } #endif } if (rc->kind != MP_CODE_NATIVE_ASM) { // Save constant table for bytecode, native and viper // Number of entries in constant table mp_print_uint(print, rc->n_obj); mp_print_uint(print, rc->n_raw_code); const mp_uint_t *const_table = rc->const_table; // Save function argument names (initial entries in const_table) // (viper has n_pos_args=n_kwonly_args=0 so doesn't save any qstrs here) for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { mp_obj_t o = (mp_obj_t)*const_table++; save_qstr(print, qstr_window, MP_OBJ_QSTR_VALUE(o)); } if (rc->kind != MP_CODE_BYTECODE) { // Skip saving mp_fun_table entry ++const_table; } // Save constant objects and raw code children for (size_t i = 0; i < rc->n_obj; ++i) { save_obj(print, (mp_obj_t)*const_table++); } for (size_t i = 0; i < rc->n_raw_code; ++i) { save_raw_code(print, (mp_raw_code_t *)(uintptr_t)*const_table++, qstr_window); } } } STATIC bool mp_raw_code_has_native(mp_raw_code_t *rc) { if (rc->kind != MP_CODE_BYTECODE) { return true; } const byte *ip = rc->fun_data; bytecode_prelude_t prelude; extract_prelude(&ip, &prelude); const mp_uint_t *const_table = rc->const_table + prelude.n_pos_args + prelude.n_kwonly_args + rc->n_obj; for (size_t i = 0; i < rc->n_raw_code; ++i) { if (mp_raw_code_has_native((mp_raw_code_t *)(uintptr_t)*const_table++)) { return true; } } return false; } void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { // header contains: // byte 'M' // byte version // byte feature flags // byte number of bits in a small int // uint size of qstr window byte header[4] = { 'M', MPY_VERSION, MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS_DYNAMIC), #if MICROPY_DYNAMIC_COMPILER mp_dynamic_compiler.small_int_bits, #else mp_small_int_bits(), #endif }; if (mp_raw_code_has_native(rc)) { header[2] |= MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC); } mp_print_bytes(print, header, sizeof(header)); mp_print_uint(print, QSTR_WINDOW_SIZE); qstr_window_t qw; qw.idx = 0; memset(qw.window, 0, sizeof(qw.window)); save_raw_code(print, rc, &qw); } // here we define mp_raw_code_save_file depending on the port // TODO abstract this away properly #if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) || defined(__unix__) #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_raw_code_t *rc, const char *filename) { MP_THREAD_GIL_EXIT(); int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); MP_THREAD_GIL_ENTER(); mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn}; mp_raw_code_save(rc, &fd_print); MP_THREAD_GIL_EXIT(); close(fd); MP_THREAD_GIL_ENTER(); } #else #error mp_raw_code_save_file not implemented for this platform #endif #endif // MICROPY_PERSISTENT_CODE_SAVE ================================================ FILE: py/persistentcode.h ================================================ /* * 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 #define MPY_VERSION 5 // Macros to encode/decode flags to/from the feature byte #define MPY_FEATURE_ENCODE_FLAGS(flags) (flags) #define MPY_FEATURE_DECODE_FLAGS(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) // The feature flag bits encode the compile-time config options that // affect the generate bytecode. #define MPY_FEATURE_FLAGS ( \ ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \ | ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1) \ ) // This is a version of the flags that can be configured at runtime. #define MPY_FEATURE_FLAGS_DYNAMIC ( \ ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) << 0) \ | ((MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) << 1) \ ) // 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_ARMV7M) #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_FLAGS(MPY_FEATURE_FLAGS) | 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, }; mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader); mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len); mp_raw_code_t *mp_raw_code_load_file(const char *filename); void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print); void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename); void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text); #endif // MICROPY_INCLUDED_PY_PERSISTENTCODE_H ================================================ FILE: py/profile.c ================================================ /* * 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" #if MICROPY_PY_SYS_SETTRACE #define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) 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, 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 = ip + 4; prelude->opcodes = ip + n_info + n_cell; qstr block_name = ip[0] | (ip[1] << 8); qstr source_file = ip[2] | (ip[3] << 8); prelude->qstr_block_name = block_name; prelude->qstr_source_file = source_file; } /******************************************************************************/ // 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, "", prelude->qstr_block_name, o, prelude->qstr_source_file, rc->line_of_definition ); } STATIC mp_obj_tuple_t *code_consts(const mp_raw_code_t *rc) { const mp_bytecode_prelude_t *prelude = &rc->prelude; int start = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj; int stop = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj + rc->n_raw_code; mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(stop - start + 1, NULL)); size_t const_no = 0; for (int i = start; i < stop; ++i) { mp_obj_t code = mp_obj_new_code((const mp_raw_code_t *)MP_OBJ_TO_PTR(rc->const_table[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(rc)); break; case MP_QSTR_co_filename: dest[0] = MP_OBJ_NEW_QSTR(prelude->qstr_source_file); 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(prelude->qstr_block_name); 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; } } const mp_obj_type_t mp_type_code = { { &mp_type_type }, .name = MP_QSTR_code, .print = code_print, .unary_op = mp_generic_unary_op, .attr = code_attr, }; mp_obj_t mp_obj_new_code(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_code; 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, prelude->qstr_source_file, frame->lineno, prelude->qstr_block_name ); } 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->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; } } const mp_obj_type_t mp_type_frame = { { &mp_type_type }, .name = MP_QSTR_frame, .print = frame_print, .unary_op = mp_generic_unary_op, .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->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_VM(mp_pending_exception) != MP_OBJ_NULL) { mp_obj_t obj = MP_STATE_VM(mp_pending_exception); MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; nlr_raise(obj); } 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); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); } 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); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); } 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); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); } 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); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); } 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 ================================================ FILE: py/profile.h ================================================ /* * 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 { mp_obj_base_t base; 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_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 ================================================ FILE: py/py.mk ================================================ # where py object files go (they have a name prefix to prevent filename clashes) PY_BUILD = $(BUILD)/py # where autogenerated header files go HEADER_BUILD = $(BUILD)/genhdr # file containing qstr defs for the core Python bit PY_QSTR_DEFS = $(PY_SRC)/qstrdefs.h # If qstr autogeneration is not disabled we specify the output header # for all collected qstrings. ifneq ($(QSTR_AUTOGEN_DISABLE),1) QSTR_DEFS_COLLECTED = $(HEADER_BUILD)/qstrdefs.collected.h endif # Any files listed by these variables will cause a full regeneration of qstrs # DEPENDENCIES: included in qstr processing; REQUIREMENTS: not included QSTR_GLOBAL_DEPENDENCIES += $(PY_SRC)/mpconfig.h mpconfigport.h QSTR_GLOBAL_REQUIREMENTS += $(HEADER_BUILD)/mpversion.h # some code is performance bottleneck and compiled with other optimization options CSUPEROPT = -O3 # Enable building 32-bit code on 64-bit host. ifeq ($(MICROPY_FORCE_32BIT),1) CC += -m32 CXX += -m32 LD += -m32 endif # External modules written in C. ifneq ($(USER_C_MODULES),) # pre-define USERMOD variables as expanded so that variables are immediate # expanded as they're added to them SRC_USERMOD := SRC_USERMOD_CXX := CFLAGS_USERMOD := CXXFLAGS_USERMOD := LDFLAGS_USERMOD := $(foreach module, $(wildcard $(USER_C_MODULES)/*/micropython.mk), \ $(eval USERMOD_DIR = $(patsubst %/,%,$(dir $(module))))\ $(info Including User C Module from $(USERMOD_DIR))\ $(eval include $(module))\ ) SRC_MOD += $(patsubst $(USER_C_MODULES)/%.c,%.c,$(SRC_USERMOD)) SRC_MOD_CXX += $(patsubst $(USER_C_MODULES)/%.cpp,%.cpp,$(SRC_USERMOD_CXX)) CFLAGS_MOD += $(CFLAGS_USERMOD) CXXFLAGS_MOD += $(CXXFLAGS_USERMOD) LDFLAGS_MOD += $(LDFLAGS_USERMOD) endif # py object files PY_CORE_O_BASENAME = $(addprefix py/,\ mpstate.o \ nlr.o \ nlrx86.o \ nlrx64.o \ nlrthumb.o \ nlrpowerpc.o \ nlrxtensa.o \ nlrsetjmp.o \ malloc.o \ gc.o \ pystack.o \ qstr.o \ vstr.o \ mpprint.o \ unicode.o \ mpz.o \ reader.o \ lexer.o \ parse.o \ scope.o \ compile.o \ emitcommon.o \ emitbc.o \ asmbase.o \ asmx64.o \ emitnx64.o \ asmx86.o \ emitnx86.o \ asmthumb.o \ emitnthumb.o \ emitinlinethumb.o \ asmarm.o \ emitnarm.o \ asmxtensa.o \ emitnxtensa.o \ emitinlinextensa.o \ emitnxtensawin.o \ formatfloat.o \ parsenumbase.o \ parsenum.o \ emitglue.o \ persistentcode.o \ runtime.o \ runtime_utils.o \ scheduler.o \ nativeglue.o \ pairheap.o \ ringbuf.o \ stackctrl.o \ argcheck.o \ warning.o \ profile.o \ map.o \ obj.o \ objarray.o \ objattrtuple.o \ objbool.o \ objboundmeth.o \ objcell.o \ objclosure.o \ objcomplex.o \ objdeque.o \ objdict.o \ objenumerate.o \ objexcept.o \ objfilter.o \ objfloat.o \ objfun.o \ objgenerator.o \ objgetitemiter.o \ objint.o \ objint_longlong.o \ objint_mpz.o \ objlist.o \ objmap.o \ objmodule.o \ objobject.o \ objpolyiter.o \ objproperty.o \ objnone.o \ objnamedtuple.o \ objrange.o \ objreversed.o \ objset.o \ objsingleton.o \ objslice.o \ objstr.o \ objstrunicode.o \ objstringio.o \ objtuple.o \ objtype.o \ objzip.o \ opmethods.o \ sequence.o \ stream.o \ binary.o \ builtinimport.o \ builtinevex.o \ builtinhelp.o \ modarray.o \ modbuiltins.o \ modcollections.o \ modgc.o \ modio.o \ modmath.o \ modcmath.o \ modmicropython.o \ modstruct.o \ modsys.o \ moduerrno.o \ modthread.o \ vm.o \ bc.o \ showbc.o \ repl.o \ smallint.o \ frozenmod.o \ ) PY_EXTMOD_O_BASENAME = \ extmod/moduasyncio.o \ extmod/moductypes.o \ extmod/modujson.o \ extmod/modure.o \ extmod/moduzlib.o \ extmod/moduheapq.o \ extmod/modutimeq.o \ extmod/moduhashlib.o \ extmod/moducryptolib.o \ extmod/modubinascii.o \ extmod/virtpin.o \ extmod/machine_mem.o \ extmod/machine_pinbase.o \ extmod/machine_signal.o \ extmod/machine_pulse.o \ extmod/machine_i2c.o \ extmod/machine_spi.o \ extmod/modbluetooth.o \ extmod/modussl_axtls.o \ extmod/modussl_mbedtls.o \ extmod/modurandom.o \ extmod/moduselect.o \ extmod/moduwebsocket.o \ extmod/modwebrepl.o \ extmod/modframebuf.o \ extmod/vfs.o \ extmod/vfs_blockdev.o \ extmod/vfs_reader.o \ extmod/vfs_posix.o \ extmod/vfs_posix_file.o \ extmod/vfs_fat.o \ extmod/vfs_fat_diskio.o \ extmod/vfs_fat_file.o \ extmod/vfs_lfs.o \ extmod/utime_mphal.o \ extmod/uos_dupterm.o \ lib/embed/abort_.o \ lib/utils/printf.o \ # prepend the build destination prefix to the py object files PY_CORE_O = $(addprefix $(BUILD)/, $(PY_CORE_O_BASENAME)) PY_EXTMOD_O = $(addprefix $(BUILD)/, $(PY_EXTMOD_O_BASENAME)) # this is a convenience variable for ports that want core, extmod and frozen code PY_O = $(PY_CORE_O) $(PY_EXTMOD_O) # object file for frozen code specified via a manifest ifneq ($(FROZEN_MANIFEST),) PY_O += $(BUILD)/$(BUILD)/frozen_content.o endif # object file for frozen files ifneq ($(FROZEN_DIR),) PY_O += $(BUILD)/$(BUILD)/frozen.o endif # object file for frozen bytecode (frozen .mpy files) ifneq ($(FROZEN_MPY_DIR),) PY_O += $(BUILD)/$(BUILD)/frozen_mpy.o endif # Sources that may contain qstrings SRC_QSTR_IGNORE = py/nlr% SRC_QSTR += $(SRC_MOD) $(filter-out $(SRC_QSTR_IGNORE),$(PY_CORE_O_BASENAME:.o=.c)) $(PY_EXTMOD_O_BASENAME:.o=.c) # Anything that depends on FORCE will be considered out-of-date FORCE: .PHONY: FORCE $(HEADER_BUILD)/mpversion.h: FORCE | $(HEADER_BUILD) $(Q)$(PYTHON) $(PY_SRC)/makeversionhdr.py $@ # mpconfigport.mk is optional, but changes to it may drastically change # overall config, so they need to be caught MPCONFIGPORT_MK = $(wildcard mpconfigport.mk) # qstr data # Adding an order only dependency on $(HEADER_BUILD) causes $(HEADER_BUILD) to get # created before we run the script to generate the .h # Note: we need to protect the qstr names from the preprocessor, so we wrap # the lines in "" and then unwrap after the preprocessor is finished. # See more information about this process in docs/develop/qstr.rst. $(HEADER_BUILD)/qstrdefs.generated.h: $(PY_QSTR_DEFS) $(QSTR_DEFS) $(QSTR_DEFS_COLLECTED) $(PY_SRC)/makeqstrdata.py mpconfigport.h $(MPCONFIGPORT_MK) $(PY_SRC)/mpconfig.h | $(HEADER_BUILD) $(ECHO) "GEN $@" $(Q)$(CAT) $(PY_QSTR_DEFS) $(QSTR_DEFS) $(QSTR_DEFS_COLLECTED) | $(SED) 's/^Q(.*)/"&"/' | $(CPP) $(CFLAGS) - | $(SED) 's/^\"\(Q(.*)\)\"/\1/' > $(HEADER_BUILD)/qstrdefs.preprocessed.h $(Q)$(PYTHON) $(PY_SRC)/makeqstrdata.py $(HEADER_BUILD)/qstrdefs.preprocessed.h > $@ $(HEADER_BUILD)/compressed.data.h: $(HEADER_BUILD)/compressed.collected $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makecompresseddata.py $< > $@ # build a list of registered modules for py/objmodule.c. $(HEADER_BUILD)/moduledefs.h: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(HEADER_BUILD)/mpversion.h @$(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makemoduledefs.py --vpath="., $(TOP), $(USER_C_MODULES)" $(SRC_QSTR) > $@ SRC_QSTR += $(HEADER_BUILD)/moduledefs.h # Standard C functions like memset need to be compiled with special flags so # the compiler does not optimise these functions in terms of themselves. CFLAGS_BUILTIN ?= -ffreestanding -fno-builtin -fno-lto $(BUILD)/lib/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) # Force nlr code to always be compiled with space-saving optimisation so # that the function preludes are of a minimal and predictable form. $(PY_BUILD)/nlr%.o: CFLAGS += -Os # optimising gc for speed; 5ms down to 4ms on pybv2 $(PY_BUILD)/gc.o: CFLAGS += $(CSUPEROPT) # optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) $(PY_BUILD)/vm.o: CFLAGS += $(CSUPEROPT) # Optimizing vm.o for modern deeply pipelined CPUs with branch predictors # may require disabling tail jump optimization. This will make sure that # each opcode has its own dispatching jump which will improve branch # branch predictor efficiency. # https://marc.info/?l=lua-l&m=129778596120851 # http://hg.python.org/cpython/file/b127046831e2/Python/ceval.c#l828 # http://www.emulators.com/docs/nx25_nostradamus.htm #-fno-crossjumping # Include rules for extmod related code include $(TOP)/extmod/extmod.mk ================================================ FILE: py/pystack.c ================================================ /* * 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 nlr_raise(mp_obj_new_exception_arg1(&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 ================================================ FILE: py/pystack.h ================================================ /* * 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 ================================================ FILE: py/qstr.c ================================================ /* * 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" // NOTE: we are using linear arrays to store and search for qstr's (unique strings, interned strings) // ultimately we will replace this with a static hash table of some kind // also probably need to include the length in the string data, to allow null bytes in the string #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 contains (hash, length, data): // - hash (configurable number of bytes) // - length (configurable number of bytes) // - data ("length" number of bytes) // - \0 terminated (so they can be printed using printf) #if MICROPY_QSTR_BYTES_IN_HASH == 1 #define Q_HASH_MASK (0xff) #define Q_GET_HASH(q) ((mp_uint_t)(q)[0]) #define Q_SET_HASH(q, hash) do { (q)[0] = (hash); } while (0) #elif MICROPY_QSTR_BYTES_IN_HASH == 2 #define Q_HASH_MASK (0xffff) #define Q_GET_HASH(q) ((mp_uint_t)(q)[0] | ((mp_uint_t)(q)[1] << 8)) #define Q_SET_HASH(q, hash) do { (q)[0] = (hash); (q)[1] = (hash) >> 8; } while (0) #else #error unimplemented qstr hash decoding #endif #define Q_GET_ALLOC(q) (MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + Q_GET_LENGTH(q) + 1) #define Q_GET_DATA(q) ((q) + MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN) #if MICROPY_QSTR_BYTES_IN_LEN == 1 #define Q_GET_LENGTH(q) ((q)[MICROPY_QSTR_BYTES_IN_HASH]) #define Q_SET_LENGTH(q, len) do { (q)[MICROPY_QSTR_BYTES_IN_HASH] = (len); } while (0) #elif MICROPY_QSTR_BYTES_IN_LEN == 2 #define Q_GET_LENGTH(q) ((q)[MICROPY_QSTR_BYTES_IN_HASH] | ((q)[MICROPY_QSTR_BYTES_IN_HASH + 1] << 8)) #define Q_SET_LENGTH(q, len) do { (q)[MICROPY_QSTR_BYTES_IN_HASH] = (len); (q)[MICROPY_QSTR_BYTES_IN_HASH + 1] = (len) >> 8; } while (0) #else #error unimplemented qstr length decoding #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 mp_uint_t qstr_compute_hash(const byte *data, size_t len) { // djb2 algorithm; see http://www.cse.yorku.ca/~oz/hash.html mp_uint_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; } const qstr_pool_t mp_qstr_const_pool = { NULL, // no previous pool 0, // no previous pool MICROPY_ALLOC_QSTR_ENTRIES_INIT, MP_QSTRnumber_of, // corresponds to number of strings in array just below { #ifndef NO_QSTR #define QDEF(id, str) str, #include "genhdr/qstrdefs.generated.h" #undef QDEF #endif }, }; #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 byte *find_qstr(qstr q) { // search pool for this qstr // total_prev_len==0 in the final pool, so the loop will always terminate qstr_pool_t *pool = MP_STATE_VM(last_pool); while (q < pool->total_prev_len) { pool = pool->prev; } return pool->qstrs[q - pool->total_prev_len]; } // qstr_mutex must be taken while in this function STATIC qstr qstr_add(const byte *q_ptr) { DEBUG_printf("QSTR: add hash=%d len=%d data=%.*s\n", Q_GET_HASH(q_ptr), Q_GET_LENGTH(q_ptr), Q_GET_LENGTH(q_ptr), Q_GET_DATA(q_ptr)); // 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 qstr_pool_t *pool = m_new_obj_var_maybe(qstr_pool_t, const char *, new_alloc); if (pool == NULL) { QSTR_EXIT(); m_malloc_fail(new_alloc); } 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_STATE_VM(last_pool)->qstrs[MP_STATE_VM(last_pool)->len++] = q_ptr; // return id for the newly-added qstr return MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len - 1; } qstr qstr_find_strn(const char *str, size_t str_len) { // work out hash of str mp_uint_t str_hash = qstr_compute_hash((const byte *)str, str_len); // search pools for the data for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) { for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { if (Q_GET_HASH(*q) == str_hash && Q_GET_LENGTH(*q) == str_len && memcmp(Q_GET_DATA(*q), str, str_len) == 0) { return pool->total_prev_len + (q - pool->qstrs); } } } // not found; return null qstr return 0; } 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 = MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + 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 byte *new_p = m_renew_maybe(byte, 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(byte, 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(byte, 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(byte, 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 byte *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 mp_uint_t hash = qstr_compute_hash((const byte *)str, len); Q_SET_HASH(q_ptr, hash); Q_SET_LENGTH(q_ptr, len); memcpy(q_ptr + MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN, str, len); q_ptr[MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + len] = '\0'; q = qstr_add(q_ptr); } QSTR_EXIT(); return q; } mp_uint_t qstr_hash(qstr q) { const byte *qd = find_qstr(q); return Q_GET_HASH(qd); } size_t qstr_len(qstr q) { const byte *qd = find_qstr(q); return Q_GET_LENGTH(qd); } const char *qstr_str(qstr q) { const byte *qd = find_qstr(q); return (const char *)Q_GET_DATA(qd); } const byte *qstr_data(qstr q, size_t *len) { const byte *qd = find_qstr(q); *len = Q_GET_LENGTH(qd); return Q_GET_DATA(qd); } 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 (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 (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { *n_str_data_bytes += Q_GET_ALLOC(*q); } #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(qstr) * 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 (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { mp_printf(&mp_plat_print, "Q(%s)\n", Q_GET_DATA(*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 ================================================ FILE: py/qstr.h ================================================ /* * 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_static("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 QDEF(id, str) id, #include "genhdr/qstrdefs.generated.h" #undef QDEF #endif MP_QSTRnumber_of, // no underscore so it can't clash with any of the above }; typedef size_t qstr; typedef struct _qstr_pool_t { struct _qstr_pool_t *prev; size_t total_prev_len; size_t alloc; size_t len; const byte *qstrs[]; } qstr_pool_t; #define QSTR_FROM_STR_STATIC(s) (qstr_from_strn((s), strlen(s))) #define QSTR_TOTAL() (MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len) void qstr_init(void); mp_uint_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, mp_rom_error_text_t src); #define MP_IS_COMPRESSED_ROM_STRING(s) (*(byte *)(s) == 0xff) #endif #endif // MICROPY_INCLUDED_PY_QSTR_H ================================================ FILE: py/qstrdefs.h ================================================ /* * 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_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_ENABLE_PYSTACK Q(pystack exhausted) #endif ================================================ FILE: py/reader.c ================================================ /* * 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 "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, const char *filename) { MP_THREAD_GIL_EXIT(); int fd = open(filename, O_RDONLY, 0644); MP_THREAD_GIL_ENTER(); if (fd < 0) { mp_raise_OSError(errno); } mp_reader_new_file_from_fd(reader, fd, true); } #endif #endif ================================================ FILE: py/reader.h ================================================ /* * 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, const char *filename); void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); #endif // MICROPY_INCLUDED_PY_READER_H ================================================ FILE: py/repl.c ================================================ /* * 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/runtime.h" #include "py/builtin.h" #include "py/repl.h" #if MICROPY_HELPER_REPL 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; } 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; } } size_t nqstr = QSTR_TOTAL(); // 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]; for (;;) { // get next word in string to complete const char *s_start = str; while (str < top && *str != '.') { ++str; } size_t s_len = str - s_start; if (str < top) { // 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; } else { // end of string, do completion on this partial name // look for matches const char *match_str = NULL; size_t match_len = 0; qstr q_first = 0, q_last = 0; for (qstr q = MP_QSTR_ + 1; q < nqstr; ++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) { mp_load_method_protected(obj, q, dest, true); if (dest[0] != MP_OBJ_NULL) { 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; } } } // 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) { static const char import_str[] = "import "; 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 #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) { mp_load_method_protected(obj, q, dest, true); if (dest[0] != MP_OBJ_NULL) { 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"); return (size_t)(-1); // indicate many matches } } } #endif // MICROPY_HELPER_REPL ================================================ FILE: py/repl.h ================================================ /* * 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 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 ================================================ FILE: py/ringbuf.c ================================================ /* * 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 "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; } ================================================ FILE: py/ringbuf.h ================================================ /* * 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); #endif // MICROPY_INCLUDED_PY_RINGBUF_H ================================================ FILE: py/runtime.c ================================================ /* * 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 "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/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), }; void mp_init(void) { qstr_init(); // no pending exceptions to start with MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; #if MICROPY_ENABLE_SCHEDULER MP_STATE_VM(sched_state) = MP_SCHED_IDLE; 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), 3); // 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_ATEXIT MP_STATE_VM(sys_exitfunc) = mp_const_none; #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_BLUETOOTH MP_STATE_VM(bluetooth) = 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 // mp_obj_dict_free(&dict_main); // mp_map_deinit(&MP_STATE_VM(mp_loaded_modules_map)); } mp_obj_t 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 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: 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 (type->unary_op != NULL) { mp_obj_t result = type->unary_op(op, arg); if (result != MP_OBJ_NULL) { return result; } } // With MP_UNARY_OP_INT, mp_unary_op() becomes a fallback for mp_obj_get_int(). // In this case provide a more focused error message to not confuse, e.g. chr(1.0) #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE if (op == MP_UNARY_OP_INT) { mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); } else { mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); } #else if (op == MP_UNARY_OP_INT) { mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("can't convert %s to int"), mp_obj_get_type_str(arg)); } 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 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)BITS_PER_WORD || 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 <<= 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)BITS_PER_WORD) { // Shifting to big amounts is underfined behavior // in C and is CPU-dependent; propagate sign bit. rhs_val = BITS_PER_WORD - 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 (type->binary_op != NULL) { mp_obj_t result = type->binary_op(op, lhs, rhs); if (result != MP_OBJ_NULL) { return result; } } #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 (type->call != NULL) { return 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 } uint n_args = n_args_n_kw & 0xff; uint n_kw = (n_args_n_kw >> 8) & 0xff; mp_obj_t pos_seq = args[n_args + 2 * n_kw]; // may be MP_OBJ_NULL mp_obj_t kw_dict = args[n_args + 2 * n_kw + 1]; // may be MP_OBJ_NULL DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, seq=%p, dict=%p)\n", fun, self, n_args, n_kw, args, pos_seq, kw_dict); // 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; uint args2_alloc; uint args2_len = 0; // Try to get a hint for the size of the kw_dict uint kw_dict_len = 0; if (kw_dict != MP_OBJ_NULL && mp_obj_is_type(kw_dict, &mp_type_dict)) { kw_dict_len = mp_obj_dict_len(kw_dict); } // Extract the pos_seq sequence to the new args array. // Note that it can be arbitrary iterator. if (pos_seq == MP_OBJ_NULL) { // no sequence // 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 if (mp_obj_is_type(pos_seq, &mp_type_tuple) || mp_obj_is_type(pos_seq, &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(pos_seq, &len, &items); // allocate memory for the new array of args args2_alloc = 1 + n_args + 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; } // copy the fixed and variable position args mp_seq_cat(args2 + args2_len, args, n_args, items, len, mp_obj_t); args2_len += n_args + len; } else { // generic iterator // allocate memory for the new array of args args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len) + 3; 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 position args mp_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); args2_len += n_args; // extract the variable position args from the iterator mp_obj_iter_buf_t iter_buf; mp_obj_t iterable = mp_getiter(pos_seq, &iter_buf); mp_obj_t item; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { if (args2_len >= 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; } } // The size of the args2 array now is the number of positional args. uint pos_args_len = args2_len; // Copy the fixed kw args. mp_seq_copy(args2 + args2_len, args + n_args, 2 * n_kw, mp_obj_t); args2_len += 2 * n_kw; // Extract (key,value) pairs from kw_dict dictionary and append to args2. // Note that it can be arbitrary iterator. if (kw_dict == MP_OBJ_NULL) { // pass } else if (mp_obj_is_type(kw_dict, &mp_type_dict)) { // dictionary mp_map_t *map = mp_obj_dict_get_map(kw_dict); assert(args2_len + 2 * map->used <= args2_alloc); // should have enough, since kw_dict_len is in this case hinted correctly above for (size_t i = 0; i < map->alloc; i++) { if (mp_map_slot_is_filled(map, i)) { // the key must be a qstr, so intern it if it's a string mp_obj_t key = map->table[i].key; if (!mp_obj_is_qstr(key)) { key = mp_obj_str_intern_checked(key); } args2[args2_len++] = key; args2[args2_len++] = map->table[i].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_dict, 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) { uint new_alloc = args2_alloc * 2; if (new_alloc < 4) { new_alloc = 4; } 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_dict, 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; } } 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 const mp_obj_type_t mp_type_checked_fun = { { &mp_type_type }, .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, .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 = m_new_obj(mp_obj_checked_fun_t); o->base.type = &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; // 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->iternext != NULL) { dest[0] = MP_OBJ_FROM_PTR(&mp_builtin_next_obj); dest[1] = obj; } else if (type->attr != NULL) { // this type can do its own load, so call it type->attr(obj, attr, dest); } else if (type->locals_dict != NULL) { // generic method lookup // this is a lookup in the object (ie not class or type) assert(type->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now mp_map_t *locals_map = &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); } } } 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 (type->attr != NULL) { mp_obj_t dest[2] = {MP_OBJ_SENTINEL, value}; 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); // Check for native getiter which is the identity. We handle this case explicitly // so we don't unnecessarily allocate any RAM for the iter_buf, which won't be used. if (type->getiter == mp_identity_getiter) { return o_in; } // check for native getiter (corresponds to __iter__) if (type->getiter != NULL) { if (iter_buf == NULL && type->getiter != 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_obj_t iter = type->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 } // 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->iternext != NULL) { return type->iternext(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->iternext != NULL) { return type->iternext(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_OBJ_STOP_ITERATION; } 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 } } } // TODO: Unclear what to do with StopIterarion exception here. 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->iternext != NULL && send_value == mp_const_none) { mp_obj_t ret = type->iternext(self_in); *ret_val = ret; if (ret != MP_OBJ_STOP_ITERATION) { return MP_VM_RETURN_YIELD; } else { // Emulate raise StopIteration() // Special case, handled in vm.c 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 return mp_call_function_n_kw(o, 0, 0, NULL); } else 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 if (!mp_obj_is_package(module)) { 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 mp_obj_dict_t *volatile old_globals = mp_globals_get(); mp_obj_dict_t *volatile old_locals = mp_locals_get(); // set new context mp_globals_set(globals); mp_locals_set(locals); nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { 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); } // finish nlr block, restore context and return value nlr_pop(); mp_globals_set(old_globals); mp_locals_set(old_locals); return ret; } else { // exception; restore context and re-raise same exception mp_globals_set(old_globals); mp_locals_set(old_locals); nlr_jump(nlr.ret_val); } } #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); } 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_OSError(int errno_) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_))); } NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } #if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK NORETURN void mp_raise_recursion_depth(void) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded))); } #endif ================================================ FILE: py/runtime.h ================================================ /* * 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" 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; // 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_keyboard_interrupt(void); void mp_handle_pending(bool raise_exc); void mp_handle_pending_tail(mp_uint_t atomic_state); #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); #endif // 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; } 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); 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); #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); NORETURN void mp_raise_OSError(int errno_); 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); #define mp_sys_path (MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_sys_path_obj))) #define mp_sys_argv (MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_sys_argv_obj))) #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 ================================================ FILE: py/runtime0.h ================================================ /* * 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 // The first four must fit in 8 bits, see emitbc.c // The remaining must fit in 16 bits, see scope.h #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) // 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, // __int__ 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 ================================================ FILE: py/runtime_utils.c ================================================ /* * 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; } } ================================================ FILE: py/scheduler.c ================================================ /* * 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_KBD_EXCEPTION // This function may be called asynchronously at any time so only do the bare minimum. void MICROPY_WRAP_MP_KEYBOARD_INTERRUPT(mp_keyboard_interrupt)(void) { MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); #if MICROPY_ENABLE_SCHEDULER if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } #endif } #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; } // A variant of this is inlined in the VM at the pending exception check void mp_handle_pending(bool raise_exc) { if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); // Re-check state is still pending now that we're in the atomic section. if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { mp_obj_t obj = MP_STATE_VM(mp_pending_exception); if (obj != MP_OBJ_NULL) { MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; if (!mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; } if (raise_exc) { MICROPY_END_ATOMIC_SECTION(atomic_state); nlr_raise(obj); } } mp_handle_pending_tail(atomic_state); } else { MICROPY_END_ATOMIC_SECTION(atomic_state); } } } // This function should only be called by mp_handle_pending, // or by the VM's inlined version of that function. void mp_handle_pending_tail(mp_uint_t atomic_state) { MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; 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); } mp_sched_unlock(); } void mp_sched_lock(void) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); if (MP_STATE_VM(sched_state) < 0) { --MP_STATE_VM(sched_state); } else { 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) { // vm became unlocked if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL || 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; ret = true; } else { // schedule queue is full ret = false; } MICROPY_END_ATOMIC_SECTION(atomic_state); return ret; } #else // MICROPY_ENABLE_SCHEDULER // A variant of this is inlined in the VM at the pending exception check void mp_handle_pending(bool raise_exc) { if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { mp_obj_t obj = MP_STATE_VM(mp_pending_exception); MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; if (raise_exc) { nlr_raise(obj); } } } #endif // MICROPY_ENABLE_SCHEDULER ================================================ FILE: py/scope.c ================================================ /* * 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. 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, qstr source_file, 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; scope->source_file = source_file; 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 ================================================ FILE: py/scope.h ================================================ /* * 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_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 // whet 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; uint16_t source_file; // a qstr 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, qstr source_file, 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 ================================================ FILE: py/sequence.c ================================================ /* * 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; } // Othewise, 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); } ================================================ FILE: py/showbc.c ================================================ /* * 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/bc.h" #if MICROPY_DEBUG_PRINTERS #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) #if MICROPY_PERSISTENT_CODE #define DECODE_QSTR \ qst = ip[0] | ip[1] << 8; \ ip += 2; #define DECODE_PTR \ DECODE_UINT; \ unum = mp_showbc_const_table[unum] #define DECODE_OBJ \ DECODE_UINT; \ unum = mp_showbc_const_table[unum] #else #define DECODE_QSTR { \ qst = 0; \ do { \ qst = (qst << 7) + (*ip & 0x7f); \ } while ((*ip++ & 0x80) != 0); \ } #define DECODE_PTR do { \ ip = (byte *)MP_ALIGN(ip, sizeof(void *)); \ unum = (uintptr_t)*(void **)ip; \ ip += sizeof(void *); \ } while (0) #define DECODE_OBJ do { \ ip = (byte *)MP_ALIGN(ip, sizeof(mp_obj_t)); \ unum = (mp_uint_t)*(mp_obj_t *)ip; \ ip += sizeof(mp_obj_t); \ } while (0) #endif const byte *mp_showbc_code_start; const mp_uint_t *mp_showbc_const_table; void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { mp_showbc_code_start = ip; // Decode prelude MP_BC_PRELUDE_SIG_DECODE(ip); MP_BC_PRELUDE_SIZE_DECODE(ip); const byte *code_info = ip; #if MICROPY_PERSISTENT_CODE qstr block_name = code_info[0] | (code_info[1] << 8); qstr source_file = code_info[2] | (code_info[3] << 8); code_info += 4; #else qstr block_name = mp_decode_uint(&code_info); qstr source_file = mp_decode_uint(&code_info); #endif mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len); // raw bytecode dump size_t prelude_size = ip - mp_showbc_code_start + n_info + n_cell; mp_printf(print, "Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", prelude_size, len - prelude_size); for (mp_uint_t i = 0; i < len; i++) { if (i > 0 && i % 16 == 0) { mp_printf(print, "\n"); } mp_printf(print, " %02x", mp_showbc_code_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++) { mp_printf(print, " %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i]))); } 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; // 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;) { 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, len - prelude_size, const_table); } const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { 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 = (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)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_GLOBAL: DECODE_QSTR; mp_printf(print, "LOAD_GLOBAL %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_ATTR: DECODE_QSTR; mp_printf(print, "LOAD_ATTR %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { mp_printf(print, " (cache=%u)", *ip++); } 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)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { mp_printf(print, " (cache=%u)", *ip++); } 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 - mp_showbc_code_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 - mp_showbc_code_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 - mp_showbc_code_start)); break; case MP_BC_JUMP_IF_TRUE_OR_POP: DECODE_SLABEL; mp_printf(print, "JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_JUMP_IF_FALSE_OR_POP: DECODE_SLABEL; mp_printf(print, "JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_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 - mp_showbc_code_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 - mp_showbc_code_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 - mp_showbc_code_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 - mp_showbc_code_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 - mp_showbc_code_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 - mp_showbc_code_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_printf(print, "UNARY_OP " UINT_FMT, (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; 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, const mp_uint_t *const_table) { mp_showbc_code_start = ip; mp_showbc_const_table = const_table; while (ip < len + mp_showbc_code_start) { mp_printf(print, "%02u ", (uint)(ip - mp_showbc_code_start)); ip = mp_bytecode_print_str(print, ip); mp_printf(print, "\n"); } } #endif // MICROPY_DEBUG_PRINTERS ================================================ FILE: py/smallint.c ================================================ /* * 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; } ================================================ FILE: py/smallint.h ================================================ /* * 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)WORD_MSBIT_HIGH) >> 1)) #define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & WORD_MSBIT_HIGH) == 0) // Mask to truncate mp_int_t to positive value #define MP_SMALL_INT_POSITIVE_MASK ~(WORD_MSBIT_HIGH | (WORD_MSBIT_HIGH >> 1)) #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B #define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)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 ~(WORD_MSBIT_HIGH | (WORD_MSBIT_HIGH >> 1) | (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))) bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y); mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor); mp_int_t mp_small_int_floor_divide(mp_int_t num, mp_int_t denom); #endif // MICROPY_INCLUDED_PY_SMALLINT_H ================================================ FILE: py/stackctrl.c ================================================ /* * 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. */ #include "py/runtime.h" #include "py/stackctrl.h" void mp_stack_ctrl_init(void) { volatile int stack_dummy; MP_STATE_THREAD(stack_top) = (char *)&stack_dummy; } 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 ================================================ FILE: py/stackctrl.h ================================================ /* * 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) #define MP_STACK_CHECK() #endif #endif // MICROPY_INCLUDED_PY_STACKCTRL_H ================================================ FILE: py/stream.c ================================================ /* * 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); #define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) // 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; } 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); const mp_stream_p_t *stream_p = type->protocol; if (stream_p == NULL || ((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)) { // CPython: io.UnsupportedOperation, OSError subclass mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported")); } return stream_p; } 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(&mp_type_str, &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; return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &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; return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &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; } } return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &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 stream_seek(size_t n_args, const mp_obj_t *args) { struct mp_stream_seek_t seek_s; // TODO: Could be uint64 seek_s.offset = mp_obj_get_int(args[1]); seek_s.whence = SEEK_SET; if (n_args == 3) { seek_s.whence = mp_obj_get_int(args[2]); } // In POSIX, it's error to seek before end of stream, we enforce it here. if (seek_s.whence == SEEK_SET && seek_s.offset < 0) { mp_raise_OSError(MP_EINVAL); } const mp_stream_p_t *stream_p = mp_get_stream(args[0]); int error; mp_uint_t res = stream_p->ioctl(args[0], MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error); if (res == MP_STREAM_ERROR) { mp_raise_OSError(error); } // TODO: Could be uint64 return mp_obj_new_int_from_uint(seek_s.offset); } 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 = 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 = 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) { const mp_obj_base_t *o = stream; const mp_stream_p_t *stream_p = o->type->protocol; struct mp_stream_seek_t seek_s; seek_s.offset = offset; seek_s.whence = whence; mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &errno); if (res == MP_STREAM_ERROR) { return -1; } return seek_s.offset; } int mp_stream_posix_fsync(void *stream) { mp_obj_base_t *o = stream; const mp_stream_p_t *stream_p = 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 ================================================ FILE: py/stream.h ================================================ /* * 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 // 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_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 *)((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) 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 ================================================ FILE: py/unicode.c ================================================ /* * 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 ================================================ FILE: py/unicode.h ================================================ /* * 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 ================================================ FILE: py/vm.c ================================================ /* * 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/runtime.h" #include "py/bc0.h" #include "py/bc.h" #include "py/profile.h" // *FORMAT-OFF* #if 0 #define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(&mp_plat_print, ip, 1, code_state->fun_bc->const_table); #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 = (ip[0] | (ip[1] << 8)); ip += 2 #define DECODE_SLABEL size_t slab = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2 #if MICROPY_PERSISTENT_CODE #define DECODE_QSTR \ qstr qst = ip[0] | ip[1] << 8; \ ip += 2; #define DECODE_PTR \ DECODE_UINT; \ void *ptr = (void*)(uintptr_t)code_state->fun_bc->const_table[unum] #define DECODE_OBJ \ DECODE_UINT; \ mp_obj_t obj = (mp_obj_t)code_state->fun_bc->const_table[unum] #else #define DECODE_QSTR qstr qst = 0; \ do { \ qst = (qst << 7) + (*ip & 0x7f); \ } while ((*ip++ & 0x80) != 0) #define DECODE_PTR \ ip = (byte*)MP_ALIGN(ip, sizeof(void*)); \ void *ptr = *(void**)ip; \ ip += sizeof(void*) #define DECODE_OBJ \ ip = (byte*)MP_ALIGN(ip, sizeof(mp_obj_t)); \ mp_obj_t obj = *(mp_obj_t*)ip; \ ip += sizeof(mp_obj_t) #endif #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 #if MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE static inline mp_map_elem_t *mp_map_cached_lookup(mp_map_t *map, qstr qst, uint8_t *idx_cache) { size_t idx = *idx_cache; mp_obj_t key = MP_OBJ_NEW_QSTR(qst); mp_map_elem_t *elem = NULL; if (idx < map->alloc && map->table[idx].key == key) { elem = &map->table[idx]; } else { elem = mp_map_lookup(map, key, MP_MAP_LOOKUP); if (elem != NULL) { *idx_cache = (elem - &map->table[0]) & 0xff; } } return elem; } #endif // 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 mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) { #define SELECTIVE_EXC_IP (0) #if SELECTIVE_EXC_IP #define MARK_EXC_IP_SELECTIVE() { code_state->ip = ip; } /* stores ip 1 byte past last opcode */ #define MARK_EXC_IP_GLOBAL() #else #define MARK_EXC_IP_SELECTIVE() #define MARK_EXC_IP_GLOBAL() { code_state->ip = ip; } /* stores ip pointing to last opcode */ #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; 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_int_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; } #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_LOAD_NAME): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; PUSH(mp_load_name(qst)); DISPATCH(); } #else ENTRY(MP_BC_LOAD_NAME): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_map_elem_t *elem = mp_map_cached_lookup(&mp_locals_get()->map, qst, (uint8_t*)ip); mp_obj_t obj; if (elem != NULL) { obj = elem->value; } else { obj = mp_load_name(qst); } PUSH(obj); ip++; DISPATCH(); } #endif #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_LOAD_GLOBAL): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; PUSH(mp_load_global(qst)); DISPATCH(); } #else ENTRY(MP_BC_LOAD_GLOBAL): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_map_elem_t *elem = mp_map_cached_lookup(&mp_globals_get()->map, qst, (uint8_t*)ip); mp_obj_t obj; if (elem != NULL) { obj = elem->value; } else { obj = mp_load_global(qst); } PUSH(obj); ip++; DISPATCH(); } #endif #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_LOAD_ATTR): { FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; SET_TOP(mp_load_attr(TOP(), qst)); DISPATCH(); } #else ENTRY(MP_BC_LOAD_ATTR): { FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t top = TOP(); 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_cached_lookup(&self->members, qst, (uint8_t*)ip); } mp_obj_t obj; if (elem != NULL) { obj = elem->value; } else { obj = mp_load_attr(top, qst); } SET_TOP(obj); ip++; DISPATCH(); } #endif 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(); } #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_STORE_ATTR): { FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_store_attr(sp[0], qst, sp[-1]); sp -= 2; DISPATCH(); } #else // This caching code works with MICROPY_PY_BUILTINS_PROPERTY and/or // MICROPY_PY_DESCRIPTORS enabled because if the attr exists in // self->members then it can't be a property or have descriptors. A // consequence of this is that we can't use MP_MAP_LOOKUP_ADD_IF_NOT_FOUND // in the fast-path below, because that store could override a property. ENTRY(MP_BC_STORE_ATTR): { FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_map_elem_t *elem = NULL; mp_obj_t top = TOP(); if (mp_obj_is_instance_type(mp_obj_get_type(top)) && sp[-1] != MP_OBJ_NULL) { mp_obj_instance_t *self = MP_OBJ_TO_PTR(top); elem = mp_map_cached_lookup(&self->members, qst, (uint8_t*)ip); } if (elem != NULL) { elem->value = sp[-1]; } else { mp_store_attr(sp[0], qst, sp[-1]); } sp -= 2; ip++; DISPATCH(); } #endif 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_SLABEL; if (mp_obj_is_true(TOP())) { ip += slab; } else { sp--; } DISPATCH_WITH_PEND_EXC_CHECK(); } ENTRY(MP_BC_JUMP_IF_FALSE_OR_POP): { DECODE_SLABEL; if (mp_obj_is_true(TOP())) { sp--; } else { ip += slab; } 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_raw_code(ptr, MP_OBJ_NULL, MP_OBJ_NULL)); DISPATCH(); } ENTRY(MP_BC_MAKE_FUNCTION_DEFARGS): { DECODE_PTR; // Stack layout: def_tuple def_dict <- TOS mp_obj_t def_dict = POP(); SET_TOP(mp_make_function_from_raw_code(ptr, TOP(), def_dict)); 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_raw_code(ptr, 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_raw_code(ptr, 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 ... seq dict <- 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(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 ... seq dict <- TOS sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 3; #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_warning(NULL, "exception chaining not supported"); sp--; // ignore (pop) "from" argument 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(); //#define EXC_MATCH(exc, type) mp_obj_is_type(exc, type) #define EXC_MATCH(exc, type) mp_obj_exception_match(exc, type) #define GENERATOR_EXIT_IF_NEEDED(t) if (t != MP_OBJ_NULL && EXC_MATCH(t, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { mp_obj_t raise_t = mp_make_raise_obj(t); RAISE(raise_t); } 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) { // Pop exhausted gen sp--; if (ret_value == MP_OBJ_STOP_ITERATION) { // Optimize StopIteration // TODO: get StopIteration's value PUSH(mp_const_none); } else { PUSH(ret_value); } // If we injected GeneratorExit downstream, then even // if it was swallowed, we re-raise GeneratorExit GENERATOR_EXIT_IF_NEEDED(t_exc); DISPATCH(); } else { assert(ret_kind == MP_VM_RETURN_EXCEPTION); assert(!EXC_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 { 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: MICROPY_VM_HOOK_LOOP #if MICROPY_ENABLE_SCHEDULER // This is an inlined variant of mp_handle_pending if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); // Re-check state is still pending now that we're in the atomic section. if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { MARK_EXC_IP_SELECTIVE(); mp_obj_t obj = MP_STATE_VM(mp_pending_exception); if (obj != MP_OBJ_NULL) { MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; if (!mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; } MICROPY_END_ATOMIC_SECTION(atomic_state); RAISE(obj); } mp_handle_pending_tail(atomic_state); } else { MICROPY_END_ATOMIC_SECTION(atomic_state); } } #else // This is an inlined variant of mp_handle_pending if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { MARK_EXC_IP_SELECTIVE(); mp_obj_t obj = MP_STATE_VM(mp_pending_exception); MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; RAISE(obj); } #endif #if MICROPY_PY_THREAD_GIL #if MICROPY_PY_THREAD_GIL_VM_DIVISOR 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))) { if (code_state->ip) { // 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 *bytecode_start = ip + n_info + n_cell; #if !MICROPY_PERSISTENT_CODE // so bytecode is aligned bytecode_start = MP_ALIGN(bytecode_start, sizeof(mp_uint_t)); #endif size_t bc = code_state->ip - bytecode_start; #if MICROPY_PERSISTENT_CODE qstr block_name = ip[0] | (ip[1] << 8); qstr source_file = ip[2] | (ip[3] << 8); ip += 4; #else qstr block_name = mp_decode_uint_value(ip); ip = mp_decode_uint_skip(ip); qstr source_file = mp_decode_uint_value(ip); ip = mp_decode_uint_skip(ip); #endif size_t source_line = mp_bytecode_get_source_line(ip, 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; } } } } ================================================ FILE: py/vmentrytable.h ================================================ /* * 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 ================================================ FILE: py/vstr.c ================================================ /* * 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); } ================================================ FILE: py/warning.c ================================================ /* * 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 ================================================ FILE: tools/mpy-cross/hellortt.py ================================================ # import os class HelloRtt: def __repr__(self): self.__call__() return "" def __call__(self): print("hello world!!") print("hello RTT") hello = HelloRtt()