Skip to content

Commit 66028ab

Browse files
committed
Basic implementation of import.
import works for simple cases. Still work to do on finding the right script, and setting globals/locals correctly when running an imported function.
1 parent aae7847 commit 66028ab

20 files changed

Lines changed: 207 additions & 44 deletions

py/builtin.c

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,20 @@
1616

1717
mp_obj_t mp_builtin___build_class__(mp_obj_t o_class_fun, mp_obj_t o_class_name) {
1818
// we differ from CPython: we set the new __locals__ object here
19-
mp_map_t *old_locals = rt_get_map_locals();
19+
mp_map_t *old_locals = rt_locals_get();
2020
mp_map_t *class_locals = mp_map_new(MP_MAP_QSTR, 0);
21-
rt_set_map_locals(class_locals);
21+
rt_locals_set(class_locals);
2222

2323
// call the class code
2424
rt_call_function_1(o_class_fun, (mp_obj_t)0xdeadbeef);
2525

2626
// restore old __locals__ object
27-
rt_set_map_locals(old_locals);
27+
rt_locals_set(old_locals);
2828

2929
// create and return the new class
3030
return mp_obj_new_class(class_locals);
3131
}
3232

33-
mp_obj_t mp_builtin___import__(int n, mp_obj_t *args) {
34-
printf("import:\n");
35-
for (int i = 0; i < n; i++) {
36-
printf(" ");
37-
mp_obj_print(args[i]);
38-
printf("\n");
39-
}
40-
return mp_const_none;
41-
}
42-
4333
mp_obj_t mp_builtin___repl_print__(mp_obj_t o) {
4434
if (o != mp_const_none) {
4535
mp_obj_print(o);

py/builtinimport.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <stdint.h>
2+
#include <stdlib.h>
3+
#include <stdio.h>
4+
#include <stdarg.h>
5+
#include <string.h>
6+
#include <assert.h>
7+
8+
#include "nlr.h"
9+
#include "misc.h"
10+
#include "mpconfig.h"
11+
#include "lexer.h"
12+
#include "lexerunix.h"
13+
#include "parse.h"
14+
#include "compile.h"
15+
#include "obj.h"
16+
#include "runtime0.h"
17+
#include "runtime.h"
18+
#include "map.h"
19+
#include "builtin.h"
20+
21+
mp_obj_t mp_builtin___import__(int n, mp_obj_t *args) {
22+
/*
23+
printf("import:\n");
24+
for (int i = 0; i < n; i++) {
25+
printf(" ");
26+
mp_obj_print(args[i]);
27+
printf("\n");
28+
}
29+
*/
30+
31+
// find the file to import
32+
qstr mod_name = mp_obj_get_qstr(args[0]);
33+
mp_lexer_t *lex = mp_import_open_file(mod_name);
34+
if (lex == NULL) {
35+
// TODO handle lexer error correctly
36+
return mp_const_none;
37+
}
38+
39+
// create a new module object
40+
mp_obj_t module_obj = mp_obj_new_module(mp_obj_get_qstr(args[0]));
41+
42+
// save the old context
43+
mp_map_t *old_locals = rt_locals_get();
44+
mp_map_t *old_globals = rt_globals_get();
45+
46+
// set the new context
47+
rt_locals_set(mp_obj_module_get_globals(module_obj));
48+
rt_globals_set(mp_obj_module_get_globals(module_obj));
49+
50+
// parse the imported script
51+
mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT);
52+
mp_lexer_free(lex);
53+
54+
if (pn == MP_PARSE_NODE_NULL) {
55+
// TODO handle parse error correctly
56+
rt_locals_set(old_locals);
57+
rt_globals_set(old_globals);
58+
return mp_const_none;
59+
}
60+
61+
if (!mp_compile(pn, false)) {
62+
// TODO handle compile error correctly
63+
rt_locals_set(old_locals);
64+
rt_globals_set(old_globals);
65+
return mp_const_none;
66+
}
67+
68+
// complied successfully, execute it
69+
mp_obj_t module_fun = rt_make_function_from_id(1); // TODO we should return from mp_compile the unique_code_id for the module
70+
nlr_buf_t nlr;
71+
if (nlr_push(&nlr) == 0) {
72+
rt_call_function_0(module_fun);
73+
nlr_pop();
74+
} else {
75+
// exception; restore context and re-raise same exception
76+
rt_locals_set(old_locals);
77+
rt_globals_set(old_globals);
78+
nlr_jump(nlr.ret_val);
79+
}
80+
rt_locals_set(old_locals);
81+
rt_globals_set(old_globals);
82+
83+
return module_obj;
84+
}

py/lexer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,6 @@ bool mp_lexer_opt_str(mp_lexer_t *lex, const char *str);
138138
*/
139139
bool mp_lexer_show_error(mp_lexer_t *lex, const char *msg);
140140
bool mp_lexer_show_error_pythonic(mp_lexer_t *lex, const char *msg);
141+
142+
// used to import a module; must be implemented for a specific port
143+
mp_lexer_t *mp_import_open_file(qstr mod_name);

py/lexerunix.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,23 @@ mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
5858

5959
return mp_lexer_new_from_str_len(filename, data, size, true);
6060
}
61+
62+
/******************************************************************************/
63+
/* unix implementation of import */
64+
65+
// TODO properly!
66+
67+
static const char *import_base_dir = NULL;
68+
69+
void mp_import_set_directory(const char *dir) {
70+
import_base_dir = dir;
71+
}
72+
73+
mp_lexer_t *mp_import_open_file(qstr mod_name) {
74+
vstr_t *vstr = vstr_new();
75+
if (import_base_dir != NULL) {
76+
vstr_printf(vstr, "%s/", import_base_dir);
77+
}
78+
vstr_printf(vstr, "%s.py", qstr_str(mod_name));
79+
return mp_lexer_new_from_file(vstr_str(vstr)); // TODO does lexer need to copy the string? can we free it here?
80+
}

py/lexerunix.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str);
22
mp_lexer_t *mp_lexer_new_from_file(const char *filename);
3+
4+
void mp_import_set_directory(const char *dir);

py/map.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ typedef struct _mp_set_t {
2323
mp_obj_t *table;
2424
} mp_set_t;
2525

26-
// these are defined in runtime.c
27-
mp_map_t *rt_get_map_locals(void);
28-
void rt_set_map_locals(mp_map_t *m);
29-
3026
int get_doubling_prime_greater_or_equal_to(int x);
3127
void mp_map_init(mp_map_t *map, mp_map_kind_t kind, int n);
3228
mp_map_t *mp_map_new(mp_map_kind_t kind, int n);

py/obj.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,14 @@ mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value);
215215
void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item);
216216

217217
// functions
218-
typedef struct _mp_obj_fun_native_t { // need this so we can define static objects
218+
typedef struct _mp_obj_fun_native_t { // need this so we can define const objects (to go in ROM)
219219
mp_obj_base_t base;
220220
machine_uint_t n_args_min; // inclusive
221221
machine_uint_t n_args_max; // inclusive
222222
void *fun;
223+
// TODO add mp_map_t *globals
224+
// for const function objects, make an empty, const map
225+
// such functions won't be able to access the global scope, but that's probably okay
223226
} mp_obj_fun_native_t;
224227
extern const mp_obj_type_t fun_native_type;
225228
extern const mp_obj_type_t fun_bc_type;

py/objfun.c

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "misc.h"
88
#include "mpconfig.h"
99
#include "obj.h"
10+
#include "map.h"
1011
#include "runtime.h"
1112
#include "bc.h"
1213

@@ -129,9 +130,10 @@ mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var
129130

130131
typedef struct _mp_obj_fun_bc_t {
131132
mp_obj_base_t base;
132-
int n_args;
133-
uint n_state;
134-
const byte *code;
133+
mp_map_t *globals; // the context within which this function was defined
134+
int n_args; // number of arguments this function takes
135+
uint n_state; // total state size for the executing function (incl args, locals, stack)
136+
const byte *bytecode; // bytecode for the function
135137
} mp_obj_fun_bc_t;
136138

137139
// args are in reverse order in the array
@@ -142,15 +144,17 @@ mp_obj_t fun_bc_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
142144
nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args, (const char*)(machine_int_t)n_args));
143145
}
144146

145-
return mp_execute_byte_code(self->code, args, n_args, self->n_state);
146-
}
147-
148-
void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
149-
assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
150-
mp_obj_fun_bc_t *self = self_in;
151-
*n_args = self->n_args;
152-
*n_state = self->n_state;
153-
*code = self->code;
147+
// optimisation: allow the compiler to optimise this tail call for
148+
// the common case when the globals don't need to be changed
149+
mp_map_t *old_globals = rt_globals_get();
150+
if (self->globals == old_globals) {
151+
return mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
152+
} else {
153+
rt_globals_set(self->globals);
154+
mp_obj_t result = mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
155+
rt_globals_set(old_globals);
156+
return result;
157+
}
154158
}
155159

156160
const mp_obj_type_t fun_bc_type = {
@@ -170,12 +174,21 @@ const mp_obj_type_t fun_bc_type = {
170174
mp_obj_t mp_obj_new_fun_bc(int n_args, uint n_state, const byte *code) {
171175
mp_obj_fun_bc_t *o = m_new_obj(mp_obj_fun_bc_t);
172176
o->base.type = &fun_bc_type;
177+
o->globals = rt_globals_get();
173178
o->n_args = n_args;
174179
o->n_state = n_state;
175-
o->code = code;
180+
o->bytecode = code;
176181
return o;
177182
}
178183

184+
void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
185+
assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
186+
mp_obj_fun_bc_t *self = self_in;
187+
*n_args = self->n_args;
188+
*n_state = self->n_state;
189+
*code = self->bytecode;
190+
}
191+
179192
/******************************************************************************/
180193
/* inline assembler functions */
181194

py/runtime.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,6 @@ void rt_assign_inline_asm_code(int unique_code_id, void *fun, uint len, int n_ar
281281
#endif
282282
}
283283

284-
mp_map_t *rt_get_map_locals(void) {
285-
return map_locals;
286-
}
287-
288-
void rt_set_map_locals(mp_map_t *m) {
289-
map_locals = m;
290-
}
291-
292284
static bool fit_small_int(mp_small_int_t o) {
293285
return true;
294286
}
@@ -786,6 +778,7 @@ mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
786778
} else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
787779
return mp_obj_instance_load_attr(base, attr);
788780
} else if (MP_OBJ_IS_TYPE(base, &module_type)) {
781+
DEBUG_OP_printf("lookup module map %p\n", mp_obj_module_get_globals(base));
789782
mp_map_elem_t *elem = mp_qstr_map_lookup(mp_obj_module_get_globals(base), attr, false);
790783
if (elem == NULL) {
791784
// TODO what about generic method lookup?
@@ -913,6 +906,24 @@ mp_obj_t rt_import_from(mp_obj_t module, qstr name) {
913906
return x;
914907
}
915908

909+
mp_map_t *rt_locals_get(void) {
910+
return map_locals;
911+
}
912+
913+
void rt_locals_set(mp_map_t *m) {
914+
DEBUG_OP_printf("rt_locals_set(%p)\n", m);
915+
map_locals = m;
916+
}
917+
918+
mp_map_t *rt_globals_get(void) {
919+
return map_globals;
920+
}
921+
922+
void rt_globals_set(mp_map_t *m) {
923+
DEBUG_OP_printf("rt_globals_set(%p)\n", m);
924+
map_globals = m;
925+
}
926+
916927
// these must correspond to the respective enum
917928
void *const rt_fun_table[RT_F_NUMBER_OF] = {
918929
rt_load_const_dec,

py/runtime.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,9 @@ mp_obj_t rt_getiter(mp_obj_t o);
5757
mp_obj_t rt_iternext(mp_obj_t o);
5858
mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level);
5959
mp_obj_t rt_import_from(mp_obj_t module, qstr name);
60+
61+
struct _mp_map_t;
62+
struct _mp_map_t *rt_locals_get(void);
63+
void rt_locals_set(struct _mp_map_t *m);
64+
struct _mp_map_t *rt_globals_get(void);
65+
void rt_globals_set(struct _mp_map_t *m);

0 commit comments

Comments
 (0)