d94141e147
The intent is to allow us to make breaking changes to the native ABI (e.g. changes to dynruntime.h) without needing the bytecode version to increment. With this commit the two bits previously used for the feature flags (but now unused as of .mpy version 6) encode a sub-version. A bytecode-only .mpy file can be loaded as long as MPY_VERSION matches, but a native .mpy (i.e. one with an arch set) must also match MPY_SUB_VERSION. This allows 3 additional updates to the native ABI per bytecode revision. The sub-version is set to 1 because the previous commits that changed the layout of mp_obj_type_t have changed the native ABI. Signed-off-by: Jim Mussared <jim.mussared@gmail.com> Signed-off-by: Damien George <damien@micropython.org>
128 lines
3.3 KiB
Python
128 lines
3.3 KiB
Python
# test importing of .mpy files with native code
|
|
|
|
try:
|
|
import usys, uio, uos
|
|
|
|
usys.implementation._mpy
|
|
uio.IOBase
|
|
uos.mount
|
|
except (ImportError, AttributeError):
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
mpy_arch = usys.implementation._mpy >> 8
|
|
if mpy_arch >> 2 == 0:
|
|
# This system does not support .mpy files containing native code
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
|
|
class UserFile(uio.IOBase):
|
|
def __init__(self, data):
|
|
self.data = memoryview(data)
|
|
self.pos = 0
|
|
|
|
def readinto(self, buf):
|
|
n = min(len(buf), len(self.data) - self.pos)
|
|
buf[:n] = self.data[self.pos : self.pos + n]
|
|
self.pos += n
|
|
return n
|
|
|
|
def ioctl(self, req, arg):
|
|
return 0
|
|
|
|
|
|
class UserFS:
|
|
def __init__(self, files):
|
|
self.files = files
|
|
|
|
def mount(self, readonly, mksfs):
|
|
pass
|
|
|
|
def umount(self):
|
|
pass
|
|
|
|
def stat(self, path):
|
|
if path in self.files:
|
|
return (32768, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
|
raise OSError
|
|
|
|
def open(self, path, mode):
|
|
return UserFile(self.files[path])
|
|
|
|
|
|
# these are the test .mpy files
|
|
valid_header = bytes([77, 6, mpy_arch, 31])
|
|
# fmt: off
|
|
user_files = {
|
|
# bad architecture (mpy_arch needed for sub-version)
|
|
'/mod0.mpy': bytes([77, 6, 0xfc | mpy_arch, 31]),
|
|
|
|
# test loading of viper and asm
|
|
'/mod1.mpy': valid_header + (
|
|
b'\x02' # n_qstr
|
|
b'\x00' # n_obj
|
|
|
|
b'\x0emod1.py\x00' # qstr0 = "mod1.py"
|
|
b'\x0aouter\x00' # qstr1 = "outer"
|
|
|
|
b'\x2c' # 5 bytes, have children, bytecode
|
|
b'\x00\x02' # prelude
|
|
b'\x01' # simple name (qstr index)
|
|
b'\x51' # LOAD_CONST_NONE
|
|
b'\x63' # RETURN_VALUE
|
|
|
|
b'\x02' # 2 children
|
|
|
|
b'\x42' # 8 bytes, no children, viper code
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x00' # dummy machine code
|
|
b'\x00' # scope_flags
|
|
|
|
b'\x43' # 8 bytes, no children, asm code
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x00' # dummy machine code
|
|
b'\x00\x00\x00' # scope_flags, n_pos_args, type_sig
|
|
),
|
|
|
|
# test loading viper with additional scope flags and relocation
|
|
'/mod2.mpy': valid_header + (
|
|
b'\x02' # n_qstr
|
|
b'\x00' # n_obj
|
|
|
|
b'\x0emod2.py\x00' # qstr0 = "mod2.py"
|
|
b'\x0aouter\x00' # qstr1 = "outer"
|
|
|
|
b'\x2c' # 5 bytes, have children, bytecode
|
|
b'\x00\x02' # prelude
|
|
b'\x01' # simple name (qstr index)
|
|
b'\x51' # LOAD_CONST_NONE
|
|
b'\x63' # RETURN_VALUE
|
|
|
|
b'\x01' # 1 child
|
|
|
|
b'\x22' # 4 bytes, no children, viper code
|
|
b'\x00\x00\x00\x00' # dummy machine code
|
|
b'\x70' # scope_flags: VIPERBSS | VIPERRODATA | VIPERRELOC
|
|
b'\x06\x04' # rodata=6 bytes, bss=4 bytes
|
|
b'rodata' # rodata content
|
|
b'\x03\x01\x00' # dummy relocation of rodata
|
|
),
|
|
}
|
|
# fmt: on
|
|
|
|
# create and mount a user filesystem
|
|
uos.mount(UserFS(user_files), "/userfs")
|
|
usys.path.append("/userfs")
|
|
|
|
# import .mpy files from the user filesystem
|
|
for i in range(len(user_files)):
|
|
mod = "mod%u" % i
|
|
try:
|
|
__import__(mod)
|
|
print(mod, "OK")
|
|
except ValueError as er:
|
|
print(mod, "ValueError", er)
|
|
|
|
# unmount and undo path addition
|
|
uos.umount("/userfs")
|
|
usys.path.pop()
|