Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2013-2020 Damien P. George
7 : : *
8 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : * of this software and associated documentation files (the "Software"), to deal
10 : : * in the Software without restriction, including without limitation the rights
11 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : * copies of the Software, and to permit persons to whom the Software is
13 : : * furnished to do so, subject to the following conditions:
14 : : *
15 : : * The above copyright notice and this permission notice shall be included in
16 : : * all copies or substantial portions of the Software.
17 : : *
18 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 : : * THE SOFTWARE.
25 : : */
26 : :
27 : : #include <stdint.h>
28 : : #include <stdio.h>
29 : : #include <string.h>
30 : : #include <assert.h>
31 : :
32 : : #include "py/reader.h"
33 : : #include "py/nativeglue.h"
34 : : #include "py/persistentcode.h"
35 : : #include "py/bc0.h"
36 : : #include "py/objstr.h"
37 : : #include "py/mpthread.h"
38 : :
39 : : #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
40 : :
41 : : #include "py/smallint.h"
42 : :
43 : : // makeqstrdata.py has a fixed list of qstrs at the start that we can assume
44 : : // are available with know indices on all MicroPython implementations, and
45 : : // avoid needing to duplicate the string data in the .mpy file. This is the
46 : : // last one in that list (anything with a qstr less than or equal to this is
47 : : // assumed to be in the list).
48 : : #define QSTR_LAST_STATIC MP_QSTR_zip
49 : :
50 : : #if MICROPY_DYNAMIC_COMPILER
51 : : #define MPY_FEATURE_ARCH_DYNAMIC mp_dynamic_compiler.native_arch
52 : : #else
53 : : #define MPY_FEATURE_ARCH_DYNAMIC MPY_FEATURE_ARCH
54 : : #endif
55 : :
56 : : typedef struct _bytecode_prelude_t {
57 : : uint n_state;
58 : : uint n_exc_stack;
59 : : uint scope_flags;
60 : : uint n_pos_args;
61 : : uint n_kwonly_args;
62 : : uint n_def_pos_args;
63 : : uint code_info_size;
64 : : } bytecode_prelude_t;
65 : :
66 : : #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
67 : :
68 : : #if MICROPY_PERSISTENT_CODE_LOAD
69 : :
70 : : #include "py/parsenum.h"
71 : :
72 : : static int read_byte(mp_reader_t *reader);
73 : : static size_t read_uint(mp_reader_t *reader);
74 : :
75 : : #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA
76 : :
77 : : // An mp_obj_list_t that tracks native text/BSS/rodata to prevent the GC from reclaiming them.
78 : : MP_REGISTER_ROOT_POINTER(mp_obj_t persistent_code_root_pointers);
79 : :
80 : 8 : static void track_root_pointer(void *ptr) {
81 [ + - ]: 8 : if (MP_STATE_PORT(persistent_code_root_pointers) == MP_OBJ_NULL) {
82 : 8 : MP_STATE_PORT(persistent_code_root_pointers) = mp_obj_new_list(0, NULL);
83 : : }
84 : 8 : mp_obj_list_append(MP_STATE_PORT(persistent_code_root_pointers), MP_OBJ_FROM_PTR(ptr));
85 : 8 : }
86 : :
87 : : #endif
88 : :
89 : : #if MICROPY_EMIT_MACHINE_CODE
90 : :
91 : : typedef struct _reloc_info_t {
92 : : mp_reader_t *reader;
93 : : mp_module_context_t *context;
94 : : uint8_t *rodata;
95 : : uint8_t *bss;
96 : : } reloc_info_t;
97 : :
98 : 8 : void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) {
99 : : // Relocate native code
100 : 8 : reloc_info_t *ri = ri_in;
101 : 8 : uint8_t op;
102 : 8 : uintptr_t *addr_to_adjust = NULL;
103 [ + + ]: 48 : while ((op = read_byte(ri->reader)) != 0xff) {
104 [ + - ]: 40 : if (op & 1) {
105 : : // Point to new location to make adjustments
106 : 40 : size_t addr = read_uint(ri->reader);
107 [ + + ]: 40 : if ((addr & 1) == 0) {
108 : : // Point to somewhere in text
109 : 36 : addr_to_adjust = &((uintptr_t *)text)[addr >> 1];
110 : : } else {
111 : : // Point to somewhere in rodata
112 : 4 : addr_to_adjust = &((uintptr_t *)ri->rodata)[addr >> 1];
113 : : }
114 : : }
115 : 40 : op >>= 1;
116 : 40 : uintptr_t dest;
117 : 40 : size_t n = 1;
118 [ + + ]: 40 : if (op <= 5) {
119 [ + + ]: 20 : if (op & 1) {
120 : : // Read in number of adjustments to make
121 : 4 : n = read_uint(ri->reader);
122 : : }
123 : 20 : op >>= 1;
124 [ + + ]: 20 : if (op == 0) {
125 : : // Destination is text
126 : 40 : dest = reloc_text;
127 [ - + ]: 4 : } else if (op == 1) {
128 : : // Destination is rodata
129 : 0 : dest = (uintptr_t)ri->rodata;
130 : : } else {
131 : : // Destination is bss
132 : 4 : dest = (uintptr_t)ri->bss;
133 : : }
134 [ + + ]: 20 : } else if (op == 6) {
135 : : // Destination is qstr_table
136 : 4 : dest = (uintptr_t)ri->context->constants.qstr_table;
137 [ - + ]: 16 : } else if (op == 7) {
138 : : // Destination is obj_table
139 : 0 : dest = (uintptr_t)ri->context->constants.obj_table;
140 [ + + ]: 16 : } else if (op == 8) {
141 : : // Destination is mp_fun_table itself
142 : 4 : dest = (uintptr_t)&mp_fun_table;
143 : : } else {
144 : : // Destination is an entry in mp_fun_table
145 : 12 : dest = ((uintptr_t *)&mp_fun_table)[op - 9];
146 : : }
147 [ + + ]: 76 : while (n--) {
148 : 36 : *addr_to_adjust++ += dest;
149 : : }
150 : : }
151 : 8 : }
152 : :
153 : : #endif
154 : :
155 : 22407 : static int read_byte(mp_reader_t *reader) {
156 : 22407 : return reader->readbyte(reader->data);
157 : : }
158 : :
159 : 22035 : static void read_bytes(mp_reader_t *reader, byte *buf, size_t len) {
160 [ + + ]: 2025098 : while (len-- > 0) {
161 : 2003063 : *buf++ = reader->readbyte(reader->data);
162 : : }
163 : 22035 : }
164 : :
165 : 35200 : static size_t read_uint(mp_reader_t *reader) {
166 : 35200 : size_t unum = 0;
167 : 44340 : for (;;) {
168 : 44340 : byte b = reader->readbyte(reader->data);
169 : 44340 : unum = (unum << 7) | (b & 0x7f);
170 [ + + ]: 44340 : if ((b & 0x80) == 0) {
171 : : break;
172 : : }
173 : : }
174 : 35200 : return unum;
175 : : }
176 : :
177 : 18311 : static qstr load_qstr(mp_reader_t *reader) {
178 : 18311 : size_t len = read_uint(reader);
179 [ + + ]: 18311 : if (len & 1) {
180 : : // static qstr
181 : 7982 : return len >> 1;
182 : : }
183 : 10329 : len >>= 1;
184 : 10329 : char *str = m_new(char, len);
185 : 10329 : read_bytes(reader, (byte *)str, len);
186 : 10329 : read_byte(reader); // read and discard null terminator
187 : 10329 : qstr qst = qstr_from_strn(str, len);
188 : 10329 : m_del(char, str, len);
189 : 10329 : return qst;
190 : : }
191 : :
192 : 7522 : static mp_obj_t load_obj(mp_reader_t *reader) {
193 : 7522 : byte obj_type = read_byte(reader);
194 : : #if MICROPY_EMIT_MACHINE_CODE
195 [ + + + + : 7522 : if (obj_type == MP_PERSISTENT_OBJ_FUN_TABLE) {
+ + ]
196 : : return MP_OBJ_FROM_PTR(&mp_fun_table);
197 : : } else
198 : : #endif
199 : : if (obj_type == MP_PERSISTENT_OBJ_NONE) {
200 : 4 : return mp_const_none;
201 : : } else if (obj_type == MP_PERSISTENT_OBJ_FALSE) {
202 : 8 : return mp_const_false;
203 : : } else if (obj_type == MP_PERSISTENT_OBJ_TRUE) {
204 : 10 : return mp_const_true;
205 : : } else if (obj_type == MP_PERSISTENT_OBJ_ELLIPSIS) {
206 : 6 : return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj);
207 : : } else {
208 : 6779 : size_t len = read_uint(reader);
209 [ + + ]: 6779 : if (len == 0 && obj_type == MP_PERSISTENT_OBJ_BYTES) {
210 : 40 : read_byte(reader); // skip null terminator
211 : 40 : return mp_const_empty_bytes;
212 [ + + ]: 6739 : } else if (obj_type == MP_PERSISTENT_OBJ_TUPLE) {
213 : 453 : mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(len, NULL));
214 [ + + ]: 1707 : for (size_t i = 0; i < len; ++i) {
215 : 1254 : tuple->items[i] = load_obj(reader);
216 : : }
217 : : return MP_OBJ_FROM_PTR(tuple);
218 : : }
219 : 6286 : vstr_t vstr;
220 : 6286 : vstr_init_len(&vstr, len);
221 : 6286 : read_bytes(reader, (byte *)vstr.buf, len);
222 [ + + ]: 6286 : if (obj_type == MP_PERSISTENT_OBJ_STR || obj_type == MP_PERSISTENT_OBJ_BYTES) {
223 : 4468 : read_byte(reader); // skip null terminator
224 [ + + ]: 4468 : if (obj_type == MP_PERSISTENT_OBJ_STR) {
225 : 3850 : return mp_obj_new_str_from_utf8_vstr(&vstr);
226 : : } else {
227 : 618 : return mp_obj_new_bytes_from_vstr(&vstr);
228 : : }
229 [ + + ]: 1818 : } else if (obj_type == MP_PERSISTENT_OBJ_INT) {
230 : 1242 : return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
231 : : } else {
232 [ - + ]: 576 : assert(obj_type == MP_PERSISTENT_OBJ_FLOAT || obj_type == MP_PERSISTENT_OBJ_COMPLEX);
233 : 576 : return mp_parse_num_float(vstr.buf, vstr.len, obj_type == MP_PERSISTENT_OBJ_COMPLEX, NULL);
234 : : }
235 : : }
236 : : }
237 : :
238 : 4025 : static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *context) {
239 : : // Load function kind and data length
240 : 4025 : size_t kind_len = read_uint(reader);
241 : 4025 : int kind = (kind_len & 3) + MP_CODE_BYTECODE;
242 : 4025 : bool has_children = !!(kind_len & 4);
243 : 4025 : size_t fun_data_len = kind_len >> 3;
244 : :
245 : : #if !MICROPY_EMIT_MACHINE_CODE
246 : : if (kind != MP_CODE_BYTECODE) {
247 : : mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
248 : : }
249 : : #endif
250 : :
251 : 4025 : uint8_t *fun_data = NULL;
252 : : #if MICROPY_EMIT_MACHINE_CODE
253 : 4025 : size_t prelude_offset = 0;
254 : 4025 : mp_uint_t native_scope_flags = 0;
255 : 4025 : mp_uint_t native_n_pos_args = 0;
256 : 4025 : mp_uint_t native_type_sig = 0;
257 : : #endif
258 : :
259 [ + + ]: 4025 : if (kind == MP_CODE_BYTECODE) {
260 : : // Allocate memory for the bytecode
261 : 1912 : fun_data = m_new(uint8_t, fun_data_len);
262 : : // Load bytecode
263 : 1912 : read_bytes(reader, fun_data, fun_data_len);
264 : :
265 : : #if MICROPY_EMIT_MACHINE_CODE
266 : : } else {
267 : : // Allocate memory for native data and load it
268 : 2113 : size_t fun_alloc;
269 : 2113 : MP_PLAT_ALLOC_EXEC(fun_data_len, (void **)&fun_data, &fun_alloc);
270 : 2113 : read_bytes(reader, fun_data, fun_data_len);
271 : :
272 [ + + ]: 2113 : if (kind == MP_CODE_NATIVE_PY) {
273 : : // Read prelude offset within fun_data, and extract scope flags.
274 : 1891 : prelude_offset = read_uint(reader);
275 : 1891 : const byte *ip = fun_data + prelude_offset;
276 [ + + ]: 2219 : MP_BC_PRELUDE_SIG_DECODE(ip);
277 : 2113 : native_scope_flags = scope_flags;
278 : : } else {
279 : : // Load basic scope info for viper and asm.
280 : 222 : native_scope_flags = read_uint(reader);
281 [ + + ]: 222 : if (kind == MP_CODE_NATIVE_ASM) {
282 : 4 : native_n_pos_args = read_uint(reader);
283 : 4 : native_type_sig = read_uint(reader);
284 : : }
285 : : }
286 : : #endif
287 : : }
288 : :
289 : 4025 : size_t n_children = 0;
290 : 4025 : mp_raw_code_t **children = NULL;
291 : :
292 : : #if MICROPY_EMIT_MACHINE_CODE
293 : : // Load optional BSS/rodata for viper.
294 : 4025 : uint8_t *rodata = NULL;
295 : 4025 : uint8_t *bss = NULL;
296 [ + + ]: 4025 : if (kind == MP_CODE_NATIVE_VIPER) {
297 : 218 : size_t rodata_size = 0;
298 [ + + ]: 218 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
299 : 4 : rodata_size = read_uint(reader);
300 : : }
301 : :
302 : 218 : size_t bss_size = 0;
303 [ + + ]: 218 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERBSS) {
304 : 8 : bss_size = read_uint(reader);
305 : : }
306 : :
307 [ + + ]: 218 : if (rodata_size + bss_size != 0) {
308 : 8 : bss_size = (uintptr_t)MP_ALIGN(bss_size, sizeof(uintptr_t));
309 : 8 : uint8_t *data = m_new0(uint8_t, bss_size + rodata_size);
310 : 8 : bss = data;
311 : 8 : rodata = bss + bss_size;
312 [ + + ]: 8 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
313 : 4 : read_bytes(reader, rodata, rodata_size);
314 : : }
315 : :
316 : : #if MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA
317 : : // Track the BSS/rodata memory so it's not reclaimed by the GC.
318 : 8 : track_root_pointer(data);
319 : : #endif
320 : : }
321 : : }
322 : : #endif
323 : :
324 : : // Load children if any.
325 [ + + ]: 4025 : if (has_children) {
326 : 1162 : n_children = read_uint(reader);
327 : 1162 : children = m_new(mp_raw_code_t *, n_children + (kind == MP_CODE_NATIVE_PY));
328 [ + + ]: 3814 : for (size_t i = 0; i < n_children; ++i) {
329 : 2652 : children[i] = load_raw_code(reader, context);
330 : : }
331 : : }
332 : :
333 : : // Create raw_code and return it
334 : 4025 : mp_raw_code_t *rc = mp_emit_glue_new_raw_code();
335 [ + + ]: 4025 : if (kind == MP_CODE_BYTECODE) {
336 : 1912 : const byte *ip = fun_data;
337 [ + + ]: 2314 : MP_BC_PRELUDE_SIG_DECODE(ip);
338 : : // Assign bytecode to raw code object
339 : 1912 : mp_emit_glue_assign_bytecode(rc, fun_data,
340 : : children,
341 : : #if MICROPY_PERSISTENT_CODE_SAVE
342 : : fun_data_len,
343 : : n_children,
344 : : #endif
345 : : scope_flags);
346 : :
347 : : #if MICROPY_EMIT_MACHINE_CODE
348 : : } else {
349 : 2113 : const uint8_t *prelude_ptr;
350 : : #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE
351 : : if (kind == MP_CODE_NATIVE_PY) {
352 : : // Executable code cannot be accessed byte-wise on this architecture, so copy
353 : : // the prelude to a separate memory region that is byte-wise readable.
354 : : void *buf = fun_data + prelude_offset;
355 : : size_t n = fun_data_len - prelude_offset;
356 : : prelude_ptr = memcpy(m_new(uint8_t, n), buf, n);
357 : : }
358 : : #endif
359 : :
360 : : // Relocate and commit code to executable address space
361 : 2113 : reloc_info_t ri = {reader, context, rodata, bss};
362 : : #if defined(MP_PLAT_COMMIT_EXEC)
363 : : void *opt_ri = (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL;
364 : : fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri);
365 : : #else
366 [ + + ]: 2113 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) {
367 : : #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA
368 : : // Track the function data memory so it's not reclaimed by the GC.
369 : : track_root_pointer(fun_data);
370 : : #endif
371 : : // Do the relocations.
372 : 8 : mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data);
373 : : }
374 : : #endif
375 : :
376 [ + + ]: 2113 : if (kind == MP_CODE_NATIVE_PY) {
377 : : #if !MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE
378 : 1891 : prelude_ptr = fun_data + prelude_offset;
379 : : #endif
380 [ + + ]: 1891 : if (n_children == 0) {
381 : : children = (void *)prelude_ptr;
382 : : } else {
383 : 570 : children[n_children] = (void *)prelude_ptr;
384 : : }
385 : : }
386 : :
387 : : // Assign native code to raw code object
388 : 2113 : mp_emit_glue_assign_native(rc, kind,
389 : : fun_data, fun_data_len,
390 : : children,
391 : : #if MICROPY_PERSISTENT_CODE_SAVE
392 : : n_children,
393 : : prelude_offset,
394 : : #endif
395 : : native_scope_flags, native_n_pos_args, native_type_sig
396 : : );
397 : : #endif
398 : : }
399 : 4025 : return rc;
400 : : }
401 : :
402 : 1391 : void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) {
403 : : // Set exception handler to close the reader if an exception is raised.
404 : 1391 : MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, reader->close, reader->data);
405 : 1391 : nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback);
406 : :
407 : 1391 : byte header[4];
408 : 1391 : read_bytes(reader, header, sizeof(header));
409 : 1391 : byte arch = MPY_FEATURE_DECODE_ARCH(header[2]);
410 [ + + ]: 1391 : if (header[0] != 'M'
411 [ + + ]: 1385 : || header[1] != MPY_VERSION
412 [ + + + - ]: 1377 : || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION)
413 [ - + ]: 1377 : || header[3] > MP_SMALL_INT_BITS) {
414 : 14 : mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
415 : : }
416 [ + + ]: 1377 : if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) {
417 [ + + ]: 731 : if (!MPY_FEATURE_ARCH_TEST(arch)) {
418 : 4 : if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) {
419 : : // On supported ports this can be resolved by enabling feature, eg
420 : : // mpconfigboard.h: MICROPY_EMIT_THUMB (1)
421 : : mp_raise_ValueError(MP_ERROR_TEXT("native code in .mpy unsupported"));
422 : : } else {
423 : 4 : mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch"));
424 : : }
425 : : }
426 : : }
427 : :
428 : 1373 : size_t n_qstr = read_uint(reader);
429 : 1373 : size_t n_obj = read_uint(reader);
430 : 1373 : mp_module_context_alloc_tables(cm->context, n_qstr, n_obj);
431 : :
432 : : // Load qstrs.
433 [ + + ]: 19684 : for (size_t i = 0; i < n_qstr; ++i) {
434 : 18311 : cm->context->constants.qstr_table[i] = load_qstr(reader);
435 : : }
436 : :
437 : : // Load constant objects.
438 [ + + ]: 7641 : for (size_t i = 0; i < n_obj; ++i) {
439 : 6268 : cm->context->constants.obj_table[i] = load_obj(reader);
440 : : }
441 : :
442 : : // Load top-level module.
443 : 1373 : cm->rc = load_raw_code(reader, cm->context);
444 : :
445 : : #if MICROPY_PERSISTENT_CODE_SAVE
446 : : cm->has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE;
447 : : cm->n_qstr = n_qstr;
448 : : cm->n_obj = n_obj;
449 : : #endif
450 : :
451 : : // Deregister exception handler and close the reader.
452 : 1373 : nlr_pop_jump_callback(true);
453 : 1373 : }
454 : :
455 : 0 : void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *context) {
456 : 0 : mp_reader_t reader;
457 : 0 : mp_reader_new_mem(&reader, buf, len, 0);
458 : 0 : mp_raw_code_load(&reader, context);
459 : 0 : }
460 : :
461 : : #if MICROPY_HAS_FILE_READER
462 : :
463 : 1391 : void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *context) {
464 : 1391 : mp_reader_t reader;
465 : 1391 : mp_reader_new_file(&reader, filename);
466 : 1391 : mp_raw_code_load(&reader, context);
467 : 1373 : }
468 : :
469 : : #endif // MICROPY_HAS_FILE_READER
470 : :
471 : : #endif // MICROPY_PERSISTENT_CODE_LOAD
472 : :
473 : : #if MICROPY_PERSISTENT_CODE_SAVE
474 : :
475 : : #include "py/objstr.h"
476 : :
477 : : static void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) {
478 : : print->print_strn(print->data, (const char *)data, len);
479 : : }
480 : :
481 : : #define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7)
482 : : static void mp_print_uint(mp_print_t *print, size_t n) {
483 : : byte buf[BYTES_FOR_INT];
484 : : byte *p = buf + sizeof(buf);
485 : : *--p = n & 0x7f;
486 : : n >>= 7;
487 : : for (; n != 0; n >>= 7) {
488 : : *--p = 0x80 | (n & 0x7f);
489 : : }
490 : : print->print_strn(print->data, (char *)p, buf + sizeof(buf) - p);
491 : : }
492 : :
493 : : static void save_qstr(mp_print_t *print, qstr qst) {
494 : : if (qst <= QSTR_LAST_STATIC) {
495 : : // encode static qstr
496 : : mp_print_uint(print, qst << 1 | 1);
497 : : return;
498 : : }
499 : : size_t len;
500 : : const byte *str = qstr_data(qst, &len);
501 : : mp_print_uint(print, len << 1);
502 : : mp_print_bytes(print, str, len + 1); // +1 to store null terminator
503 : : }
504 : :
505 : : static void save_obj(mp_print_t *print, mp_obj_t o) {
506 : : #if MICROPY_EMIT_MACHINE_CODE
507 : : if (o == MP_OBJ_FROM_PTR(&mp_fun_table)) {
508 : : byte obj_type = MP_PERSISTENT_OBJ_FUN_TABLE;
509 : : mp_print_bytes(print, &obj_type, 1);
510 : : } else
511 : : #endif
512 : : if (mp_obj_is_str_or_bytes(o)) {
513 : : byte obj_type;
514 : : if (mp_obj_is_str(o)) {
515 : : obj_type = MP_PERSISTENT_OBJ_STR;
516 : : } else {
517 : : obj_type = MP_PERSISTENT_OBJ_BYTES;
518 : : }
519 : : size_t len;
520 : : const char *str = mp_obj_str_get_data(o, &len);
521 : : mp_print_bytes(print, &obj_type, 1);
522 : : mp_print_uint(print, len);
523 : : mp_print_bytes(print, (const byte *)str, len + 1); // +1 to store null terminator
524 : : } else if (o == mp_const_none) {
525 : : byte obj_type = MP_PERSISTENT_OBJ_NONE;
526 : : mp_print_bytes(print, &obj_type, 1);
527 : : } else if (o == mp_const_false) {
528 : : byte obj_type = MP_PERSISTENT_OBJ_FALSE;
529 : : mp_print_bytes(print, &obj_type, 1);
530 : : } else if (o == mp_const_true) {
531 : : byte obj_type = MP_PERSISTENT_OBJ_TRUE;
532 : : mp_print_bytes(print, &obj_type, 1);
533 : : } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) {
534 : : byte obj_type = MP_PERSISTENT_OBJ_ELLIPSIS;
535 : : mp_print_bytes(print, &obj_type, 1);
536 : : } else if (mp_obj_is_type(o, &mp_type_tuple)) {
537 : : size_t len;
538 : : mp_obj_t *items;
539 : : mp_obj_tuple_get(o, &len, &items);
540 : : byte obj_type = MP_PERSISTENT_OBJ_TUPLE;
541 : : mp_print_bytes(print, &obj_type, 1);
542 : : mp_print_uint(print, len);
543 : : for (size_t i = 0; i < len; ++i) {
544 : : save_obj(print, items[i]);
545 : : }
546 : : } else {
547 : : // we save numbers using a simplistic text representation
548 : : // TODO could be improved
549 : : byte obj_type;
550 : : if (mp_obj_is_int(o)) {
551 : : obj_type = MP_PERSISTENT_OBJ_INT;
552 : : #if MICROPY_PY_BUILTINS_COMPLEX
553 : : } else if (mp_obj_is_type(o, &mp_type_complex)) {
554 : : obj_type = MP_PERSISTENT_OBJ_COMPLEX;
555 : : #endif
556 : : } else {
557 : : assert(mp_obj_is_float(o));
558 : : obj_type = MP_PERSISTENT_OBJ_FLOAT;
559 : : }
560 : : vstr_t vstr;
561 : : mp_print_t pr;
562 : : vstr_init_print(&vstr, 10, &pr);
563 : : mp_obj_print_helper(&pr, o, PRINT_REPR);
564 : : mp_print_bytes(print, &obj_type, 1);
565 : : mp_print_uint(print, vstr.len);
566 : : mp_print_bytes(print, (const byte *)vstr.buf, vstr.len);
567 : : vstr_clear(&vstr);
568 : : }
569 : : }
570 : :
571 : : static void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) {
572 : : // Save function kind and data length
573 : : mp_print_uint(print, (rc->fun_data_len << 3) | ((rc->n_children != 0) << 2) | (rc->kind - MP_CODE_BYTECODE));
574 : :
575 : : // Save function code.
576 : : mp_print_bytes(print, rc->fun_data, rc->fun_data_len);
577 : :
578 : : #if MICROPY_EMIT_MACHINE_CODE
579 : : if (rc->kind == MP_CODE_NATIVE_PY) {
580 : : // Save prelude size
581 : : mp_print_uint(print, rc->prelude_offset);
582 : : } else if (rc->kind == MP_CODE_NATIVE_VIPER || rc->kind == MP_CODE_NATIVE_ASM) {
583 : : // Save basic scope info for viper and asm
584 : : // Viper/asm functions don't support generator, variable args, or default keyword args
585 : : // so (scope_flags & MP_SCOPE_FLAG_ALL_SIG) for these functions is always 0.
586 : : mp_print_uint(print, 0);
587 : : #if MICROPY_EMIT_INLINE_ASM
588 : : if (rc->kind == MP_CODE_NATIVE_ASM) {
589 : : mp_print_uint(print, rc->asm_n_pos_args);
590 : : mp_print_uint(print, rc->asm_type_sig);
591 : : }
592 : : #endif
593 : : }
594 : : #endif
595 : :
596 : : if (rc->n_children) {
597 : : mp_print_uint(print, rc->n_children);
598 : : for (size_t i = 0; i < rc->n_children; ++i) {
599 : : save_raw_code(print, rc->children[i]);
600 : : }
601 : : }
602 : : }
603 : :
604 : : void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) {
605 : : // header contains:
606 : : // byte 'M'
607 : : // byte version
608 : : // byte native arch (and sub-version if native)
609 : : // byte number of bits in a small int
610 : : byte header[4] = {
611 : : 'M',
612 : : MPY_VERSION,
613 : : cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0,
614 : : #if MICROPY_DYNAMIC_COMPILER
615 : : mp_dynamic_compiler.small_int_bits,
616 : : #else
617 : : MP_SMALL_INT_BITS,
618 : : #endif
619 : : };
620 : : mp_print_bytes(print, header, sizeof(header));
621 : :
622 : : // Number of entries in constant table.
623 : : mp_print_uint(print, cm->n_qstr);
624 : : mp_print_uint(print, cm->n_obj);
625 : :
626 : : // Save qstrs.
627 : : for (size_t i = 0; i < cm->n_qstr; ++i) {
628 : : save_qstr(print, cm->context->constants.qstr_table[i]);
629 : : }
630 : :
631 : : // Save constant objects.
632 : : for (size_t i = 0; i < cm->n_obj; ++i) {
633 : : save_obj(print, (mp_obj_t)cm->context->constants.obj_table[i]);
634 : : }
635 : :
636 : : // Save outer raw code, which will save all its child raw codes.
637 : : save_raw_code(print, cm->rc);
638 : : }
639 : :
640 : : #if MICROPY_PERSISTENT_CODE_SAVE_FILE
641 : :
642 : : #include <unistd.h>
643 : : #include <sys/stat.h>
644 : : #include <fcntl.h>
645 : :
646 : : static void fd_print_strn(void *env, const char *str, size_t len) {
647 : : int fd = (intptr_t)env;
648 : : MP_THREAD_GIL_EXIT();
649 : : ssize_t ret = write(fd, str, len);
650 : : MP_THREAD_GIL_ENTER();
651 : : (void)ret;
652 : : }
653 : :
654 : : void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename) {
655 : : MP_THREAD_GIL_EXIT();
656 : : int fd = open(qstr_str(filename), O_WRONLY | O_CREAT | O_TRUNC, 0644);
657 : : MP_THREAD_GIL_ENTER();
658 : : if (fd < 0) {
659 : : mp_raise_OSError_with_filename(errno, qstr_str(filename));
660 : : }
661 : : mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn};
662 : : mp_raw_code_save(cm, &fd_print);
663 : : MP_THREAD_GIL_EXIT();
664 : : close(fd);
665 : : MP_THREAD_GIL_ENTER();
666 : : }
667 : :
668 : : #endif // MICROPY_PERSISTENT_CODE_SAVE_FILE
669 : :
670 : : #endif // MICROPY_PERSISTENT_CODE_SAVE
|