Skip to content

Commit 6ede1b2

Browse files
authored
C API for module creation (#59604)
Provide a way to create new modules without resorting to passing an `Expr(:module, ...)` to `eval()` by splitting up `jl_eval_module_expr()` into three pieces: Two module creation functions which are exported: * `jl_begin_new_module()` - Creates the module - Situates it within the module hierarchy (creating a binding within the parent module, setting the module parent and inheriting uuid, registering root modules) - Deals with global rooting during initialization and state for detecting whether the module is open for eval. - Creates standard imports and bindings for module-local eval and include * `jl_end_new_module()` cleans up some of this state and calls `__init__()` for the module and its children when necessary. The `Expr`-related machinery is left in the internal function `jl_eval_module_expr()`. Something along these lines is required to avoid calling `eval(Expr(:module, ...))` in the JuliaLowering implementation of module evaluation where we try never to construct `Expr`. There's clearly further cleanup which could be done here. It would be nice to use less global state (`jl_current_modules` and `jl_module_init_order`) and to not to have the `Base.__toplevel__` hack. However I wanted to keep this PR small so I've resisted the temptation to change anything more for now. Similar to #57965 in the sense that it splits more functionality out of `eval()`.
1 parent 0635285 commit 6ede1b2

2 files changed

Lines changed: 77 additions & 54 deletions

File tree

src/jl_exported_funcs.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@
330330
XX(jl_new_method_table) \
331331
XX(jl_new_method_uninit) \
332332
XX(jl_new_module) \
333+
XX(jl_begin_new_module) \
334+
XX(jl_end_new_module) \
333335
XX(jl_new_opaque_closure_from_code_info) \
334336
XX(jl_new_opaque_closure_from_code_info_in_world) \
335337
XX(jl_new_primitivetype) \

src/toplevel.c

Lines changed: 75 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ JL_DLLEXPORT _Atomic(int) jl_lineno = 0; // need to update jl_fprint_critical_er
3030
// current file name
3131
JL_DLLEXPORT _Atomic(const char *) jl_filename = "none"; // need to update jl_fprint_critical_error if this is TLS
3232

33+
static jl_value_t *jl_eval_toplevel_stmts(jl_module_t *JL_NONNULL m, jl_array_t *stmts, int fast, int need_value, const char **toplevel_filename, int *toplevel_lineno);
34+
3335
htable_t jl_current_modules;
3436
jl_mutex_t jl_modules_mutex;
3537

@@ -109,25 +111,10 @@ static int jl_is__toplevel__mod(jl_module_t *mod, jl_task_t *ct)
109111
(jl_value_t*)mod == jl_get_global_value(jl_base_module, jl_symbol("__toplevel__"), ct->world_age);
110112
}
111113

112-
// TODO: add locks around global state mutation operations
113-
static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex)
114+
JL_DLLEXPORT jl_module_t *jl_begin_new_module(jl_module_t *parent_module, jl_sym_t *name,
115+
int std_imports, const char *filename, int lineno)
114116
{
115117
jl_task_t *ct = jl_current_task;
116-
assert(ex->head == jl_module_sym);
117-
if (jl_array_nrows(ex->args) != 3 || !jl_is_expr(jl_exprarg(ex, 2))) {
118-
jl_error("syntax: malformed module expression");
119-
}
120-
121-
if (((jl_expr_t *)(jl_exprarg(ex, 2)))->head != jl_symbol("block")) {
122-
jl_error("syntax: module expression third argument must be a block");
123-
}
124-
125-
int std_imports = (jl_exprarg(ex, 0) == jl_true);
126-
jl_sym_t *name = (jl_sym_t*)jl_exprarg(ex, 1);
127-
if (!jl_is_symbol(name)) {
128-
jl_type_error("module", (jl_value_t*)jl_symbol_type, (jl_value_t*)name);
129-
}
130-
131118
int is_parent__toplevel__ = jl_is__toplevel__mod(parent_module, ct);
132119
// If we have `Base`, don't also try to import `Core` - the `Base` exports are a superset.
133120
// While we allow multiple imports of the same binding from different modules, various error printing
@@ -140,18 +127,6 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex
140127
JL_UNLOCK(&jl_modules_mutex);
141128
// copy parent environment info into submodule
142129
newm->uuid = parent_module->uuid;
143-
jl_array_t *exprs = ((jl_expr_t*)jl_exprarg(ex, 2))->args;
144-
int lineno = 0;
145-
const char *filename = "none";
146-
if (jl_array_nrows(exprs) > 0) {
147-
jl_value_t *lineex = jl_array_ptr_ref(exprs, 0);
148-
if (jl_is_linenode(lineex)) {
149-
lineno = jl_linenode_line(lineex);
150-
jl_value_t *file = jl_linenode_file(lineex);
151-
if (jl_is_symbol(file))
152-
filename = jl_symbol_name((jl_sym_t*)file);
153-
}
154-
}
155130
newm->file = jl_symbol(filename);
156131
jl_gc_wb_knownold(newm, newm->file);
157132
newm->line = lineno;
@@ -171,8 +146,6 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex
171146
}
172147
}
173148

174-
size_t last_age = ct->world_age;
175-
176149
if (parent_module == jl_main_module && name == jl_symbol("Base") && jl_base_module == NULL) {
177150
// pick up Base module during bootstrap
178151
jl_base_module = newm;
@@ -187,15 +160,14 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex
187160
else {
188161
jl_declare_constant_val(NULL, parent_module, name, (jl_value_t*)newm);
189162
}
163+
JL_GC_POP();
190164

191-
for (int i = 0; i < jl_array_nrows(exprs); i++) {
192-
// process toplevel form
193-
form = jl_svecref(jl_lower(jl_array_ptr_ref(exprs, i), newm, filename, lineno, ~(size_t)0, 0), 0);
194-
ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
195-
(void)jl_toplevel_eval_flex(newm, form, 1, 1, &filename, &lineno);
196-
}
197-
ct->world_age = last_age;
165+
return newm;
166+
}
198167

168+
JL_DLLEXPORT void jl_end_new_module(jl_module_t *newm) {
169+
jl_value_t *form = NULL;
170+
JL_GC_PUSH1(&form);
199171
JL_LOCK(&jl_modules_mutex);
200172
uintptr_t *refcnt = (uintptr_t*)ptrhash_bp(&jl_current_modules, (void*)newm);
201173
assert(*refcnt > (uintptr_t)HT_NOTFOUND);
@@ -206,10 +178,9 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex
206178
jl_module_init_order = jl_alloc_vec_any(0);
207179
jl_array_ptr_1d_push(jl_module_init_order, (jl_value_t*)newm);
208180

209-
// defer init of children until parent is done being defined
210-
// then initialize all in definition-finished order
211-
// at build time, don't run them at all (defer for runtime)
212-
form = NULL;
181+
// Defer init of direct children until parent is done being defined then
182+
// initialize all in definition-finished order.
183+
// At build time, don't run them at all - defer for runtime
213184
if (!jl_generating_output()) {
214185
if (!ptrhash_has(&jl_current_modules, (void*)newm->parent)) {
215186
size_t i, l = jl_array_nrows(jl_module_init_order);
@@ -240,6 +211,43 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex
240211
}
241212

242213
JL_GC_POP();
214+
}
215+
216+
static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex, const char **toplevel_filename, int *toplevel_lineno)
217+
{
218+
assert(ex->head == jl_module_sym);
219+
if (jl_array_nrows(ex->args) != 3 || !jl_is_expr(jl_exprarg(ex, 2))) {
220+
jl_error("syntax: malformed module expression");
221+
}
222+
223+
if (((jl_expr_t *)(jl_exprarg(ex, 2)))->head != jl_symbol("block")) {
224+
jl_error("syntax: module expression third argument must be a block");
225+
}
226+
jl_array_t *stmts = ((jl_expr_t*)jl_exprarg(ex, 2))->args;
227+
228+
int std_imports = (jl_exprarg(ex, 0) == jl_true);
229+
jl_sym_t *name = (jl_sym_t*)jl_exprarg(ex, 1);
230+
if (!jl_is_symbol(name)) {
231+
jl_type_error("module", (jl_value_t*)jl_symbol_type, (jl_value_t*)name);
232+
}
233+
234+
int lineno = 0;
235+
const char *filename = "none";
236+
if (jl_array_nrows(stmts) > 0) {
237+
jl_value_t *lineex = jl_array_ptr_ref(stmts, 0);
238+
if (jl_is_linenode(lineex)) {
239+
lineno = jl_linenode_line(lineex);
240+
jl_value_t *file = jl_linenode_file(lineex);
241+
if (jl_is_symbol(file))
242+
filename = jl_symbol_name((jl_sym_t*)file);
243+
}
244+
}
245+
246+
jl_module_t *newm = jl_begin_new_module(parent_module, name, std_imports, filename, lineno);
247+
JL_GC_PROMISE_ROOTED(newm); // Rooted in jl_current_modules
248+
jl_eval_toplevel_stmts(newm, stmts, 1, 0, toplevel_filename, toplevel_lineno);
249+
jl_end_new_module(newm);
250+
243251
return (jl_value_t*)newm;
244252
}
245253

@@ -596,6 +604,28 @@ JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t
596604
jl_declare_constant_val(b, gm, gs, val);
597605
}
598606

607+
static jl_value_t *jl_eval_toplevel_stmts(jl_module_t *JL_NONNULL m, jl_array_t *stmts, int fast, int need_value, const char **toplevel_filename, int *toplevel_lineno)
608+
{
609+
jl_task_t *ct = jl_current_task;
610+
size_t last_age = ct->world_age;
611+
jl_value_t *root = NULL;
612+
JL_GC_PUSH1(&root);
613+
jl_value_t *res = jl_nothing;
614+
int i;
615+
for (i = 0; i < jl_array_nrows(stmts); i++) {
616+
root = jl_array_ptr_ref(stmts, i);
617+
if (jl_needs_lowering(root)) {
618+
root = jl_svecref(jl_lower(root, m, *toplevel_filename, *toplevel_lineno, ~(size_t)0,
619+
need_value), 0);
620+
}
621+
ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
622+
res = jl_toplevel_eval_flex(m, root, fast, 1, toplevel_filename, toplevel_lineno);
623+
}
624+
ct->world_age = last_age;
625+
JL_GC_POP();
626+
return res;
627+
}
628+
599629
JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno)
600630
{
601631
jl_task_t *ct = jl_current_task;
@@ -654,7 +684,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val
654684
jl_sym_t *head = jl_is_expr(ex) ? ex->head : NULL;
655685

656686
if (head == jl_module_sym) {
657-
jl_value_t *val = jl_eval_module_expr(m, ex);
687+
jl_value_t *val = jl_eval_module_expr(m, ex, toplevel_filename, toplevel_lineno);
658688
JL_GC_POP();
659689
return val;
660690
}
@@ -701,17 +731,8 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val
701731
return jl_nothing;
702732
}
703733
else if (head == jl_toplevel_sym) {
704-
jl_value_t *res = jl_nothing;
705-
int i;
706-
for (i = 0; i < jl_array_nrows(ex->args); i++) {
707-
root = jl_array_ptr_ref(ex->args, i);
708-
if (jl_needs_lowering(root)) {
709-
root = jl_svecref(jl_lower(root, m, *toplevel_filename, *toplevel_lineno, ~(size_t)0, 1), 0);
710-
}
711-
ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
712-
res = jl_toplevel_eval_flex(m, root, fast, 1, toplevel_filename, toplevel_lineno);
713-
}
714-
ct->world_age = last_age;
734+
jl_value_t *res = jl_eval_toplevel_stmts(m, ex->args, fast, 1,
735+
toplevel_filename, toplevel_lineno);
715736
JL_GC_POP();
716737
return res;
717738
}

0 commit comments

Comments
 (0)