Numworks Epsilon  1.4.1
Graphing Calculator Operating System
persistentcode.c
Go to the documentation of this file.
1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2013-2016 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/emitglue.h"
34 #include "py/persistentcode.h"
35 #include "py/bc.h"
36 
37 #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
38 
39 #include "py/smallint.h"
40 
41 // The current version of .mpy files
42 #define MPY_VERSION (3)
43 
44 // The feature flags byte encodes the compile-time config options that
45 // affect the generate bytecode.
46 #define MPY_FEATURE_FLAGS ( \
47  ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \
48  | ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1) \
49  )
50 // This is a version of the flags that can be configured at runtime.
51 #define MPY_FEATURE_FLAGS_DYNAMIC ( \
52  ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) << 0) \
53  | ((MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) << 1) \
54  )
55 
56 #if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER)
57 // The bytecode will depend on the number of bits in a small-int, and
58 // this function computes that (could make it a fixed constant, but it
59 // would need to be defined in mpconfigport.h).
60 STATIC int mp_small_int_bits(void) {
62  int n = 1;
63  while (i != 0) {
64  i >>= 1;
65  ++n;
66  }
67  return n;
68 }
69 #endif
70 
71 typedef struct _bytecode_prelude_t {
72  uint n_state;
73  uint n_exc_stack;
74  uint scope_flags;
75  uint n_pos_args;
76  uint n_kwonly_args;
77  uint n_def_pos_args;
78  uint code_info_size;
79 } bytecode_prelude_t;
80 
81 // ip will point to start of opcodes
82 // ip2 will point to simple_name, source_file qstrs
83 STATIC void extract_prelude(const byte **ip, const byte **ip2, bytecode_prelude_t *prelude) {
84  prelude->n_state = mp_decode_uint(ip);
85  prelude->n_exc_stack = mp_decode_uint(ip);
86  prelude->scope_flags = *(*ip)++;
87  prelude->n_pos_args = *(*ip)++;
88  prelude->n_kwonly_args = *(*ip)++;
89  prelude->n_def_pos_args = *(*ip)++;
90  *ip2 = *ip;
91  prelude->code_info_size = mp_decode_uint(ip2);
92  *ip += prelude->code_info_size;
93  while (*(*ip)++ != 255) {
94  }
95 }
96 
97 #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
98 
99 #if MICROPY_PERSISTENT_CODE_LOAD
100 
101 #include "py/parsenum.h"
102 
103 STATIC int read_byte(mp_reader_t *reader) {
104  return reader->readbyte(reader->data);
105 }
106 
107 STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) {
108  while (len-- > 0) {
109  *buf++ = reader->readbyte(reader->data);
110  }
111 }
112 
113 STATIC size_t read_uint(mp_reader_t *reader) {
114  size_t unum = 0;
115  for (;;) {
116  byte b = reader->readbyte(reader->data);
117  unum = (unum << 7) | (b & 0x7f);
118  if ((b & 0x80) == 0) {
119  break;
120  }
121  }
122  return unum;
123 }
124 
125 STATIC qstr load_qstr(mp_reader_t *reader) {
126  size_t len = read_uint(reader);
127  char *str = m_new(char, len);
128  read_bytes(reader, (byte*)str, len);
129  qstr qst = qstr_from_strn(str, len);
130  m_del(char, str, len);
131  return qst;
132 }
133 
134 STATIC mp_obj_t load_obj(mp_reader_t *reader) {
135  byte obj_type = read_byte(reader);
136  if (obj_type == 'e') {
138  } else {
139  size_t len = read_uint(reader);
140  vstr_t vstr;
141  vstr_init_len(&vstr, len);
142  read_bytes(reader, (byte*)vstr.buf, len);
143  if (obj_type == 's' || obj_type == 'b') {
144  return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr);
145  } else if (obj_type == 'i') {
146  return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
147  } else {
148  assert(obj_type == 'f' || obj_type == 'c');
149  return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == 'c', false, NULL);
150  }
151  }
152 }
153 
154 STATIC void load_bytecode_qstrs(mp_reader_t *reader, byte *ip, byte *ip_top) {
155  while (ip < ip_top) {
156  size_t sz;
157  uint f = mp_opcode_format(ip, &sz);
158  if (f == MP_OPCODE_QSTR) {
159  qstr qst = load_qstr(reader);
160  ip[1] = qst;
161  ip[2] = qst >> 8;
162  }
163  ip += sz;
164  }
165 }
166 
167 STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) {
168  // load bytecode
169  size_t bc_len = read_uint(reader);
170  byte *bytecode = m_new(byte, bc_len);
171  read_bytes(reader, bytecode, bc_len);
172 
173  // extract prelude
174  const byte *ip = bytecode;
175  const byte *ip2;
176  bytecode_prelude_t prelude;
177  extract_prelude(&ip, &ip2, &prelude);
178 
179  // load qstrs and link global qstr ids into bytecode
180  qstr simple_name = load_qstr(reader);
181  qstr source_file = load_qstr(reader);
182  ((byte*)ip2)[0] = simple_name; ((byte*)ip2)[1] = simple_name >> 8;
183  ((byte*)ip2)[2] = source_file; ((byte*)ip2)[3] = source_file >> 8;
184  load_bytecode_qstrs(reader, (byte*)ip, bytecode + bc_len);
185 
186  // load constant table
187  size_t n_obj = read_uint(reader);
188  size_t n_raw_code = read_uint(reader);
189  mp_uint_t *const_table = m_new(mp_uint_t, prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code);
190  mp_uint_t *ct = const_table;
191  for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) {
192  *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader));
193  }
194  for (size_t i = 0; i < n_obj; ++i) {
195  *ct++ = (mp_uint_t)load_obj(reader);
196  }
197  for (size_t i = 0; i < n_raw_code; ++i) {
198  *ct++ = (mp_uint_t)(uintptr_t)load_raw_code(reader);
199  }
200 
201  // create raw_code and return it
203  mp_emit_glue_assign_bytecode(rc, bytecode, bc_len, const_table,
205  n_obj, n_raw_code,
206  #endif
207  prelude.scope_flags);
208  return rc;
209 }
210 
212  byte header[4];
213  read_bytes(reader, header, sizeof(header));
214  if (header[0] != 'M'
215  || header[1] != MPY_VERSION
216  || header[2] != MPY_FEATURE_FLAGS
217  || header[3] > mp_small_int_bits()) {
218  mp_raise_ValueError("incompatible .mpy file");
219  }
220  mp_raw_code_t *rc = load_raw_code(reader);
221  reader->close(reader->data);
222  return rc;
223 }
224 
225 mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) {
226  mp_reader_t reader;
227  mp_reader_new_mem(&reader, buf, len, 0);
228  return mp_raw_code_load(&reader);
229 }
230 
231 mp_raw_code_t *mp_raw_code_load_file(const char *filename) {
232  mp_reader_t reader;
233  mp_reader_new_file(&reader, filename);
234  return mp_raw_code_load(&reader);
235 }
236 
237 #endif // MICROPY_PERSISTENT_CODE_LOAD
238 
239 #if MICROPY_PERSISTENT_CODE_SAVE
240 
241 #include "py/objstr.h"
242 
243 STATIC void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) {
244  print->print_strn(print->data, (const char*)data, len);
245 }
246 
247 #define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7)
248 STATIC void mp_print_uint(mp_print_t *print, size_t n) {
249  byte buf[BYTES_FOR_INT];
250  byte *p = buf + sizeof(buf);
251  *--p = n & 0x7f;
252  n >>= 7;
253  for (; n != 0; n >>= 7) {
254  *--p = 0x80 | (n & 0x7f);
255  }
256  print->print_strn(print->data, (char*)p, buf + sizeof(buf) - p);
257 }
258 
259 STATIC void save_qstr(mp_print_t *print, qstr qst) {
260  size_t len;
261  const byte *str = qstr_data(qst, &len);
262  mp_print_uint(print, len);
263  mp_print_bytes(print, str, len);
264 }
265 
266 STATIC void save_obj(mp_print_t *print, mp_obj_t o) {
267  if (MP_OBJ_IS_STR_OR_BYTES(o)) {
268  byte obj_type;
269  if (MP_OBJ_IS_STR(o)) {
270  obj_type = 's';
271  } else {
272  obj_type = 'b';
273  }
274  mp_uint_t len;
275  const char *str = mp_obj_str_get_data(o, &len);
276  mp_print_bytes(print, &obj_type, 1);
277  mp_print_uint(print, len);
278  mp_print_bytes(print, (const byte*)str, len);
279  } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) {
280  byte obj_type = 'e';
281  mp_print_bytes(print, &obj_type, 1);
282  } else {
283  // we save numbers using a simplistic text representation
284  // TODO could be improved
285  byte obj_type;
286  if (MP_OBJ_IS_TYPE(o, &mp_type_int)) {
287  obj_type = 'i';
288  #if MICROPY_PY_BUILTINS_COMPLEX
289  } else if (MP_OBJ_IS_TYPE(o, &mp_type_complex)) {
290  obj_type = 'c';
291  #endif
292  } else {
294  obj_type = 'f';
295  }
296  vstr_t vstr;
297  mp_print_t pr;
298  vstr_init_print(&vstr, 10, &pr);
300  mp_print_bytes(print, &obj_type, 1);
301  mp_print_uint(print, vstr.len);
302  mp_print_bytes(print, (const byte*)vstr.buf, vstr.len);
303  vstr_clear(&vstr);
304  }
305 }
306 
307 STATIC void save_bytecode_qstrs(mp_print_t *print, const byte *ip, const byte *ip_top) {
308  while (ip < ip_top) {
309  size_t sz;
310  uint f = mp_opcode_format(ip, &sz);
311  if (f == MP_OPCODE_QSTR) {
312  qstr qst = ip[1] | (ip[2] << 8);
313  save_qstr(print, qst);
314  }
315  ip += sz;
316  }
317 }
318 
319 STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc) {
320  if (rc->kind != MP_CODE_BYTECODE) {
321  mp_raise_ValueError("can only save bytecode");
322  }
323 
324  // save bytecode
325  mp_print_uint(print, rc->data.u_byte.bc_len);
326  mp_print_bytes(print, rc->data.u_byte.bytecode, rc->data.u_byte.bc_len);
327 
328  // extract prelude
329  const byte *ip = rc->data.u_byte.bytecode;
330  const byte *ip2;
331  bytecode_prelude_t prelude;
332  extract_prelude(&ip, &ip2, &prelude);
333 
334  // save qstrs
335  save_qstr(print, ip2[0] | (ip2[1] << 8)); // simple_name
336  save_qstr(print, ip2[2] | (ip2[3] << 8)); // source_file
337  save_bytecode_qstrs(print, ip, rc->data.u_byte.bytecode + rc->data.u_byte.bc_len);
338 
339  // save constant table
340  mp_print_uint(print, rc->data.u_byte.n_obj);
341  mp_print_uint(print, rc->data.u_byte.n_raw_code);
342  const mp_uint_t *const_table = rc->data.u_byte.const_table;
343  for (uint i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) {
344  mp_obj_t o = (mp_obj_t)*const_table++;
345  save_qstr(print, MP_OBJ_QSTR_VALUE(o));
346  }
347  for (uint i = 0; i < rc->data.u_byte.n_obj; ++i) {
348  save_obj(print, (mp_obj_t)*const_table++);
349  }
350  for (uint i = 0; i < rc->data.u_byte.n_raw_code; ++i) {
351  save_raw_code(print, (mp_raw_code_t*)(uintptr_t)*const_table++);
352  }
353 }
354 
355 void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) {
356  // header contains:
357  // byte 'M'
358  // byte version
359  // byte feature flags
360  // byte number of bits in a small int
361  byte header[4] = {'M', MPY_VERSION, MPY_FEATURE_FLAGS_DYNAMIC,
362  #if MICROPY_DYNAMIC_COMPILER
363  mp_dynamic_compiler.small_int_bits,
364  #else
365  mp_small_int_bits(),
366  #endif
367  };
368  mp_print_bytes(print, header, sizeof(header));
369 
370  save_raw_code(print, rc);
371 }
372 
373 // here we define mp_raw_code_save_file depending on the port
374 // TODO abstract this away properly
375 
376 #if defined(__i386__) || defined(__x86_64__) || defined(__unix__)
377 
378 #include <unistd.h>
379 #include <sys/stat.h>
380 #include <fcntl.h>
381 
382 STATIC void fd_print_strn(void *env, const char *str, size_t len) {
383  int fd = (intptr_t)env;
384  ssize_t ret = write(fd, str, len);
385  (void)ret;
386 }
387 
388 void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) {
389  int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
390  mp_print_t fd_print = {(void*)(intptr_t)fd, fd_print_strn};
391  mp_raw_code_save(rc, &fd_print);
392  close(fd);
393 }
394 
395 #else
396 #error mp_raw_code_save_file not implemented for this platform
397 #endif
398 
399 #endif // MICROPY_PERSISTENT_CODE_SAVE
intptr_t mp_int_t
Definition: mpconfigport.h:73
qstr qstr_from_strn(const char *str, size_t len)
Definition: qstr.c:187
uintptr_t mp_uint_t
Definition: mpconfigport.h:74
#define MP_SMALL_INT_MAX
Definition: smallint.h:62
Definition: misc.h:142
union _mp_raw_code_t::@15 data
struct _mp_raw_code_t::@15::@16 u_byte
#define assert(e)
Definition: assert.h:9
def data
Definition: i18n.py:176
mp_raw_code_t * mp_raw_code_load_file(const char *filename)
mp_print_strn_t print_strn
Definition: mpprint.h:52
#define MP_OBJ_IS_TYPE(o, t)
Definition: obj.h:254
#define m_del(type, ptr, num)
Definition: misc.h:77
void vstr_init_len(vstr_t *vstr, size_t len)
Definition: vstr.c:52
char * buf
Definition: misc.h:145
#define MP_OBJ_QSTR_VALUE(o)
Definition: obj.h:91
void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename)
mp_raw_code_t * mp_raw_code_load_mem(const byte *buf, size_t len)
unsigned int uintptr_t
Definition: stdint.h:14
#define MP_OBJ_FROM_PTR(p)
Definition: obj.h:233
#define MP_OBJ_NEW_QSTR(qst)
Definition: obj.h:92
void mp_reader_new_file(mp_reader_t *reader, const char *filename)
const mp_obj_type_t mp_type_bytes
Definition: objstr.c:1964
size_t len
Definition: misc.h:144
#define STATIC
Definition: mpconfig.h:1178
int ssize_t
Definition: stddef.h:6
#define mp_obj_is_float(o)
Definition: obj.h:745
const mp_obj_type_t mp_type_complex
signed int intptr_t
Definition: stdint.h:15
const char * mp_obj_str_get_data(mp_obj_t self_in, size_t *len)
Definition: objstr.c:2105
void * data
Definition: mpprint.h:51
#define NULL
Definition: stddef.h:4
void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind)
Definition: obj.c:59
size_t qstr
Definition: qstr.h:48
const mp_obj_type_t mp_type_str
Definition: objstr.c:1950
mp_raw_code_t * mp_raw_code_load(mp_reader_t *reader)
unsigned char byte
Definition: misc.h:37
mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr)
Definition: objstr.c:1998
mp_uint_t mp_decode_uint(const byte **ptr)
Definition: bc.c:43
mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex)
Definition: parsenum.c:171
const byte * qstr_data(qstr q, size_t *len)
Definition: qstr.c:283
NORETURN void mp_raise_ValueError(const char *msg)
Definition: runtime.c:1456
const struct _mp_obj_singleton_t mp_const_ellipsis_obj
Definition: objsingleton.c:52
mp_raw_code_t * mp_emit_glue_new_raw_code(void)
Definition: emitglue.c:52
#define MP_OBJ_TO_PTR(o)
Definition: obj.h:228
void(* close)(void *data)
Definition: reader.h:39
void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print)
mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, mp_lexer_t *lex)
Definition: parsenum.c:49
void * data
Definition: reader.h:37
uint64_t mp_obj_t
Definition: obj.h:39
mp_uint_t(* readbyte)(void *data)
Definition: reader.h:38
const mp_obj_type_t mp_type_int
Definition: obj.h:544
#define MP_OBJ_IS_STR(o)
Definition: obj.h:256
void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len)
Definition: reader.c:58
void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t len, const mp_uint_t *const_table, mp_uint_t scope_flags)
Definition: emitglue.c:58
#define MP_OBJ_IS_STR_OR_BYTES(o)
Definition: obj.h:257
void vstr_init_print(vstr_t *vstr, size_t alloc, struct _mp_print_t *print)
Definition: vstr.c:64
void vstr_clear(vstr_t *vstr)
Definition: vstr.c:70
#define MICROPY_PERSISTENT_CODE_SAVE
Definition: mpconfig.h:246
#define m_new(type, num)
Definition: misc.h:57
mp_raw_code_kind_t kind
Definition: emitglue.h:43
unsigned int uint
Definition: misc.h:38