// This file is a part of Julia. License is MIT: https://julialang.org/license /* evaluating top-level expressions, loading source files */ #include "platform.h" #include #include #include #include #include #if defined(_OS_WINDOWS_) #include #else #include #endif #include "julia.h" #include "julia_internal.h" #include "julia_assert.h" #include "intrinsics.h" #include "builtin_proto.h" #ifdef __cplusplus extern "C" { #endif // current line number in a file JL_DLLEXPORT int jl_lineno = 0; // need to update jl_critical_error if this is TLS // current file name JL_DLLEXPORT const char *jl_filename = "none"; // need to update jl_critical_error if this is TLS htable_t jl_current_modules; jl_mutex_t jl_modules_mutex; // During incremental compilation, the following gets set JL_DLLEXPORT jl_module_t *jl_precompile_toplevel_module = NULL; // the toplevel module currently being defined JL_DLLEXPORT void jl_add_standard_imports(jl_module_t *m) { jl_module_t *base_module = jl_base_relative_to(m); assert(base_module != NULL); // using Base jl_module_using(m, base_module); } // create a new top-level module void jl_init_main_module(void) { assert(jl_main_module == NULL); jl_main_module = jl_new_module(jl_symbol("Main")); jl_main_module->parent = jl_main_module; jl_set_const(jl_main_module, jl_symbol("Core"), (jl_value_t*)jl_core_module); jl_set_const(jl_core_module, jl_symbol("Main"), (jl_value_t*)jl_main_module); } static jl_function_t *jl_module_get_initializer(jl_module_t *m JL_PROPAGATES_ROOT) { return (jl_function_t*)jl_get_global(m, jl_symbol("__init__")); } void jl_module_run_initializer(jl_module_t *m) { JL_TIMING(INIT_MODULE); jl_function_t *f = jl_module_get_initializer(m); if (f == NULL) return; jl_task_t *ct = jl_current_task; size_t last_age = ct->world_age; JL_TRY { ct->world_age = jl_atomic_load_acquire(&jl_world_counter); jl_apply(&f, 1); ct->world_age = last_age; } JL_CATCH { if (jl_initerror_type == NULL) { jl_rethrow(); } else { jl_rethrow_other(jl_new_struct(jl_initerror_type, m->name, jl_current_exception())); } } } static void jl_register_root_module(jl_module_t *m) { static jl_value_t *register_module_func = NULL; assert(jl_base_module); if (register_module_func == NULL) register_module_func = jl_get_global(jl_base_module, jl_symbol("register_root_module")); assert(register_module_func); jl_value_t *args[2]; args[0] = register_module_func; args[1] = (jl_value_t*)m; jl_apply(args, 2); } jl_array_t *jl_get_loaded_modules(void) { static jl_value_t *loaded_modules_array = NULL; if (loaded_modules_array == NULL && jl_base_module != NULL) loaded_modules_array = jl_get_global(jl_base_module, jl_symbol("loaded_modules_array")); if (loaded_modules_array != NULL) return (jl_array_t*)jl_call0((jl_function_t*)loaded_modules_array); return NULL; } static int jl_is__toplevel__mod(jl_module_t *mod) { return jl_base_module && (jl_value_t*)mod == jl_get_global(jl_base_module, jl_symbol("__toplevel__")); } // TODO: add locks around global state mutation operations static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex) { jl_task_t *ct = jl_current_task; assert(ex->head == jl_module_sym); if (jl_array_len(ex->args) != 3 || !jl_is_expr(jl_exprarg(ex, 2))) { jl_error("syntax: malformed module expression"); } if (((jl_expr_t *)(jl_exprarg(ex, 2)))->head != jl_symbol("block")) { jl_error("syntax: module expression third argument must be a block"); } int std_imports = (jl_exprarg(ex, 0) == jl_true); jl_sym_t *name = (jl_sym_t*)jl_exprarg(ex, 1); if (!jl_is_symbol(name)) { jl_type_error("module", (jl_value_t*)jl_symbol_type, (jl_value_t*)name); } jl_module_t *newm = jl_new_module(name); jl_value_t *form = (jl_value_t*)newm; JL_GC_PUSH1(&form); JL_LOCK(&jl_modules_mutex); ptrhash_put(&jl_current_modules, (void*)newm, (void*)((uintptr_t)HT_NOTFOUND + 1)); JL_UNLOCK(&jl_modules_mutex); jl_module_t *old_toplevel_module = jl_precompile_toplevel_module; // copy parent environment info into submodule newm->uuid = parent_module->uuid; if (jl_is__toplevel__mod(parent_module)) { newm->parent = newm; jl_register_root_module(newm); if (jl_options.incremental) { jl_precompile_toplevel_module = newm; } } else { newm->parent = parent_module; jl_binding_t *b = jl_get_binding_wr(parent_module, name, 1); jl_declare_constant(b); jl_value_t *old = NULL; if (!jl_atomic_cmpswap(&b->value, &old, (jl_value_t*)newm)) { if (!jl_is_module(old)) { jl_errorf("invalid redefinition of constant %s", jl_symbol_name(name)); } if (jl_generating_output()) jl_errorf("cannot replace module %s during compilation", jl_symbol_name(name)); jl_printf(JL_STDERR, "WARNING: replacing module %s.\n", jl_symbol_name(name)); old = jl_atomic_exchange(&b->value, (jl_value_t*)newm); } jl_gc_wb_binding(b, newm); if (old != NULL) { // create a hidden gc root for the old module JL_LOCK(&jl_modules_mutex); uintptr_t *refcnt = (uintptr_t*)ptrhash_bp(&jl_current_modules, (void*)old); *refcnt += 1; JL_UNLOCK(&jl_modules_mutex); } } if (parent_module == jl_main_module && name == jl_symbol("Base")) { // pick up Base module during bootstrap jl_base_module = newm; } size_t last_age = ct->world_age; // add standard imports unless baremodule if (std_imports) { if (jl_base_module != NULL) { jl_add_standard_imports(newm); } // add `eval` function form = jl_call_scm_on_ast("module-default-defs", (jl_value_t*)ex, newm); jl_toplevel_eval_flex(newm, form, 0, 1); form = NULL; } jl_array_t *exprs = ((jl_expr_t*)jl_exprarg(ex, 2))->args; for (int i = 0; i < jl_array_len(exprs); i++) { // process toplevel form ct->world_age = jl_atomic_load_acquire(&jl_world_counter); form = jl_expand_stmt_with_loc(jl_array_ptr_ref(exprs, i), newm, jl_filename, jl_lineno); ct->world_age = jl_atomic_load_acquire(&jl_world_counter); (void)jl_toplevel_eval_flex(newm, form, 1, 1); } newm->primary_world = jl_atomic_load_acquire(&jl_world_counter); ct->world_age = last_age; #if 0 // some optional post-processing steps size_t i; void **table = newm->bindings.table; for(i=1; i < newm->bindings.size; i+=2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; // remove non-exported macros if (jl_symbol_name(b->name)[0]=='@' && !b->exportp && b->owner == newm) b->value = NULL; // error for unassigned exports /* if (b->exportp && b->owner==newm && b->value==NULL) jl_errorf("identifier %s exported from %s is not initialized", jl_symbol_name(b->name), jl_symbol_name(newm->name)); */ } } #endif JL_LOCK(&jl_modules_mutex); uintptr_t *refcnt = (uintptr_t*)ptrhash_bp(&jl_current_modules, (void*)newm); assert(*refcnt > (uintptr_t)HT_NOTFOUND); *refcnt -= 1; // newm should be reachable from somewhere else by now if (jl_module_init_order == NULL) jl_module_init_order = jl_alloc_vec_any(0); jl_array_ptr_1d_push(jl_module_init_order, (jl_value_t*)newm); // defer init of children until parent is done being defined // then initialize all in definition-finished order // at build time, don't run them at all (defer for runtime) form = NULL; if (!jl_generating_output()) { if (!ptrhash_has(&jl_current_modules, (void*)newm->parent)) { size_t i, l = jl_array_len(jl_module_init_order); size_t ns = 0; form = (jl_value_t*)jl_alloc_vec_any(0); for (i = 0; i < l; i++) { jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(jl_module_init_order, i); if (jl_is_submodule(m, newm)) { jl_array_ptr_1d_push((jl_array_t*)form, (jl_value_t*)m); } else if (ns++ != i) { jl_array_ptr_set(jl_module_init_order, ns - 1, (jl_value_t*)m); } } if (ns < l) jl_array_del_end(jl_module_init_order, l - ns); } } JL_UNLOCK(&jl_modules_mutex); if (form) { size_t i, l = jl_array_len(form); for (i = 0; i < l; i++) { jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(form, i); JL_GC_PROMISE_ROOTED(m); jl_module_run_initializer(m); } } jl_precompile_toplevel_module = old_toplevel_module; JL_GC_POP(); return (jl_value_t*)newm; } static jl_value_t *jl_eval_dot_expr(jl_module_t *m, jl_value_t *x, jl_value_t *f, int fast) { jl_task_t *ct = jl_current_task; jl_value_t **args; JL_GC_PUSHARGS(args, 3); args[1] = jl_toplevel_eval_flex(m, x, fast, 0); args[2] = jl_toplevel_eval_flex(m, f, fast, 0); if (jl_is_module(args[1])) { JL_TYPECHK(getfield, symbol, args[2]); args[0] = jl_eval_global_var((jl_module_t*)args[1], (jl_sym_t*)args[2]); } else { args[0] = jl_eval_global_var(jl_base_relative_to(m), jl_symbol("getproperty")); size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); args[0] = jl_apply(args, 3); ct->world_age = last_age; } JL_GC_POP(); return args[0]; } void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) { // create uninitialized mutable binding for "global x" decl size_t i, l = jl_array_len(ex->args); for (i = 0; i < l; i++) { jl_value_t *arg = jl_exprarg(ex, i); jl_module_t *gm; jl_sym_t *gs; if (jl_is_globalref(arg)) { gm = jl_globalref_mod(arg); gs = jl_globalref_name(arg); } else { assert(jl_is_symbol(arg)); gm = m; gs = (jl_sym_t*)arg; } jl_binding_t *b = jl_get_binding_wr(gm, gs, 0); if (set_type && b) { jl_value_t *old_ty = NULL; jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, (jl_value_t*)jl_any_type); } } } // module referenced by (top ...) from within m // this is only needed because of the bootstrapping process: // - initially Base doesn't exist and top === Core // - later, it refers to either old Base or new Base JL_DLLEXPORT jl_module_t *jl_base_relative_to(jl_module_t *m) { for (;;) { if (m->istopmod) return m; if (m == m->parent) break; m = m->parent; } return jl_top_module; } static void expr_attributes(jl_value_t *v, int *has_intrinsics, int *has_defs, int *has_opaque) { if (!jl_is_expr(v)) return; jl_expr_t *e = (jl_expr_t*)v; jl_sym_t *head = e->head; if (head == jl_toplevel_sym || head == jl_thunk_sym) { return; } else if (head == jl_global_sym) { // this could be considered has_defs, but loops that assign to globals // might still need to be optimized. return; } else if (head == jl_const_sym || head == jl_copyast_sym) { // Note: `copyast` is included here since it indicates the presence of // `quote` and probably `eval`. *has_defs = 1; return; } else if (head == jl_method_sym || jl_is_toplevel_only_expr(v)) { *has_defs = 1; } else if (head == jl_cfunction_sym) { *has_intrinsics = 1; return; } else if (head == jl_foreigncall_sym) { *has_intrinsics = 1; return; } else if (head == jl_new_opaque_closure_sym) { *has_opaque = 1; return; } else if (head == jl_call_sym && jl_expr_nargs(e) > 0) { jl_value_t *called = NULL; jl_value_t *f = jl_exprarg(e, 0); if (jl_is_globalref(f)) { jl_module_t *mod = jl_globalref_mod(f); jl_sym_t *name = jl_globalref_name(f); if (jl_binding_resolved_p(mod, name)) { jl_binding_t *b = jl_get_binding(mod, name); if (b && b->value && b->constp) called = b->value; } } else if (jl_is_quotenode(f)) { called = jl_quotenode_value(f); } if (called) { if (jl_is_intrinsic(called) && jl_unbox_int32(called) == (int)llvmcall) { *has_intrinsics = 1; } if (called == jl_builtin__typebody) { *has_defs = 1; } } return; } int i; for (i = 0; i < jl_array_len(e->args); i++) { jl_value_t *a = jl_exprarg(e, i); if (jl_is_expr(a)) expr_attributes(a, has_intrinsics, has_defs, has_opaque); } } int jl_code_requires_compiler(jl_code_info_t *src) { jl_array_t *body = src->code; assert(jl_typeis(body, jl_array_any_type)); size_t i; int has_intrinsics = 0, has_defs = 0, has_opaque = 0; if (jl_has_meta(body, jl_force_compile_sym)) return 1; for(i=0; i < jl_array_len(body); i++) { jl_value_t *stmt = jl_array_ptr_ref(body,i); expr_attributes(stmt, &has_intrinsics, &has_defs, &has_opaque); if (has_intrinsics) return 1; } return 0; } static void body_attributes(jl_array_t *body, int *has_intrinsics, int *has_defs, int *has_loops, int *has_opaque, int *forced_compile) { size_t i; *has_loops = 0; for(i=0; i < jl_array_len(body); i++) { jl_value_t *stmt = jl_array_ptr_ref(body,i); if (!*has_loops) { if (jl_is_gotonode(stmt)) { if (jl_gotonode_label(stmt) <= i) *has_loops = 1; } else if (jl_is_gotoifnot(stmt)) { if (jl_gotoifnot_label(stmt) <= i) *has_loops = 1; } } expr_attributes(stmt, has_intrinsics, has_defs, has_opaque); } *forced_compile = jl_has_meta(body, jl_force_compile_sym); } static jl_module_t *call_require(jl_module_t *mod, jl_sym_t *var) JL_GLOBALLY_ROOTED { static jl_value_t *require_func = NULL; int build_mode = jl_generating_output(); jl_module_t *m = NULL; jl_task_t *ct = jl_current_task; if (require_func == NULL && jl_base_module != NULL) { require_func = jl_get_global(jl_base_module, jl_symbol("require")); } if (require_func != NULL) { size_t last_age = ct->world_age; ct->world_age = (build_mode ? jl_base_module->primary_world : jl_atomic_load_acquire(&jl_world_counter)); jl_value_t *reqargs[3]; reqargs[0] = require_func; reqargs[1] = (jl_value_t*)mod; reqargs[2] = (jl_value_t*)var; m = (jl_module_t*)jl_apply(reqargs, 3); ct->world_age = last_age; } if (m == NULL || !jl_is_module(m)) { jl_errorf("failed to load module %s", jl_symbol_name(var)); } return m; } // either: // - sets *name and returns the module to import *name from // - sets *name to NULL and returns a module to import static jl_module_t *eval_import_path(jl_module_t *where, jl_module_t *from JL_PROPAGATES_ROOT, jl_array_t *args, jl_sym_t **name, const char *keyword) JL_GLOBALLY_ROOTED { if (jl_array_len(args) == 0) jl_errorf("malformed \"%s\" statement", keyword); jl_sym_t *var = (jl_sym_t*)jl_array_ptr_ref(args, 0); size_t i = 1; jl_module_t *m = NULL; *name = NULL; if (!jl_is_symbol(var)) jl_type_error(keyword, (jl_value_t*)jl_symbol_type, (jl_value_t*)var); if (from != NULL) { m = from; i = 0; } else if (var != jl_dot_sym) { // `A.B`: call the loader to obtain the root A in the current environment. if (jl_core_module && var == jl_core_module->name) { m = jl_core_module; } else if (jl_base_module && var == jl_base_module->name) { m = jl_base_module; } else { m = call_require(where, var); } if (i == jl_array_len(args)) return m; } else { // `.A.B.C`: strip off leading dots by following parent links m = where; while (1) { if (i >= jl_array_len(args)) jl_error("invalid module path"); var = (jl_sym_t*)jl_array_ptr_ref(args, i); if (var != jl_dot_sym) break; i++; assert(m); m = m->parent; } } while (1) { var = (jl_sym_t*)jl_array_ptr_ref(args, i); if (!jl_is_symbol(var)) jl_type_error(keyword, (jl_value_t*)jl_symbol_type, (jl_value_t*)var); if (var == jl_dot_sym) jl_errorf("invalid %s path: \".\" in identifier path", keyword); if (i == jl_array_len(args)-1) break; m = (jl_module_t*)jl_eval_global_var(m, var); JL_GC_PROMISE_ROOTED(m); if (!jl_is_module(m)) jl_errorf("invalid %s path: \"%s\" does not name a module", keyword, jl_symbol_name(var)); i++; } *name = var; return m; } int jl_is_toplevel_only_expr(jl_value_t *e) JL_NOTSAFEPOINT { return jl_is_expr(e) && (((jl_expr_t*)e)->head == jl_module_sym || ((jl_expr_t*)e)->head == jl_import_sym || ((jl_expr_t*)e)->head == jl_using_sym || ((jl_expr_t*)e)->head == jl_export_sym || ((jl_expr_t*)e)->head == jl_thunk_sym || ((jl_expr_t*)e)->head == jl_global_sym || ((jl_expr_t*)e)->head == jl_const_sym || ((jl_expr_t*)e)->head == jl_toplevel_sym || ((jl_expr_t*)e)->head == jl_error_sym || ((jl_expr_t*)e)->head == jl_incomplete_sym); } int jl_needs_lowering(jl_value_t *e) JL_NOTSAFEPOINT { if (!jl_is_expr(e)) return 0; jl_expr_t *ex = (jl_expr_t*)e; jl_sym_t *head = ex->head; if (head == jl_module_sym || head == jl_import_sym || head == jl_using_sym || head == jl_export_sym || head == jl_thunk_sym || head == jl_toplevel_sym || head == jl_error_sym || head == jl_incomplete_sym || head == jl_method_sym) { return 0; } if (head == jl_global_sym || head == jl_const_sym) { size_t i, l = jl_array_len(ex->args); for (i = 0; i < l; i++) { jl_value_t *a = jl_exprarg(ex, i); if (!jl_is_symbol(a) && !jl_is_globalref(a)) return 1; } return 0; } return 1; } static jl_method_instance_t *method_instance_for_thunk(jl_code_info_t *src, jl_module_t *module) { jl_method_instance_t *li = jl_new_method_instance_uninit(); li->uninferred = (jl_value_t*)src; li->specTypes = (jl_value_t*)jl_emptytuple_type; li->def.module = module; return li; } static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym_t *asname) { assert(m); jl_sym_t *name = asname ? asname : import->name; jl_binding_t *b; if (jl_binding_resolved_p(m, name)) { b = jl_get_binding(m, name); if ((!b->constp && b->owner != m) || (b->value && b->value != (jl_value_t*)import)) { jl_errorf("importing %s into %s conflicts with an existing identifier", jl_symbol_name(name), jl_symbol_name(m->name)); } } else { b = jl_get_binding_wr(m, name, 1); b->imported = 1; } if (!b->constp) { b->value = (jl_value_t*)import; b->constp = 1; jl_gc_wb(m, (jl_value_t*)import); } } // in `import A.B: x, y, ...`, evaluate the `A.B` part if it exists static jl_module_t *eval_import_from(jl_module_t *m JL_PROPAGATES_ROOT, jl_expr_t *ex, const char *keyword) { if (jl_expr_nargs(ex) == 1 && jl_is_expr(jl_exprarg(ex, 0))) { jl_expr_t *fr = (jl_expr_t*)jl_exprarg(ex, 0); if (fr->head == jl_colon_sym) { if (jl_expr_nargs(fr) > 0 && jl_is_expr(jl_exprarg(fr, 0))) { jl_expr_t *path = (jl_expr_t*)jl_exprarg(fr, 0); if (((jl_expr_t*)path)->head == jl_dot_sym) { jl_sym_t *name = NULL; jl_module_t *from = eval_import_path(m, NULL, path->args, &name, keyword); if (name != NULL) { from = (jl_module_t*)jl_eval_global_var(from, name); if (!jl_is_module(from)) jl_errorf("invalid %s path: \"%s\" does not name a module", keyword, jl_symbol_name(name)); } return from; } } jl_errorf("malformed \"%s:\" statement", keyword); } } return NULL; } static void check_macro_rename(jl_sym_t *from, jl_sym_t *to, const char *keyword) { char *n1 = jl_symbol_name(from), *n2 = jl_symbol_name(to); if (n1[0] == '@' && n2[0] != '@') jl_errorf("cannot rename macro \"%s\" to non-macro \"%s\" in \"%s\"", n1, n2, keyword); if (n1[0] != '@' && n2[0] == '@') jl_errorf("cannot rename non-macro \"%s\" to macro \"%s\" in \"%s\"", n1, n2, keyword); } // Format msg and eval `throw(ErrorException(msg)))` in module `m`. // Used in `jl_toplevel_eval_flex` instead of `jl_errorf` so that the error // location in julia code gets into the backtrace. static void jl_eval_errorf(jl_module_t *m, const char* fmt, ...) { jl_value_t *throw_ex = (jl_value_t*)jl_exprn(jl_call_sym, 2); JL_GC_PUSH1(&throw_ex); jl_exprargset(throw_ex, 0, jl_builtin_throw); va_list args; va_start(args, fmt); jl_exprargset(throw_ex, 1, jl_vexceptionf(jl_errorexception_type, fmt, args)); va_end(args); jl_toplevel_eval_flex(m, throw_ex, 0, 0); JL_GC_POP(); } jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int fast, int expanded) { jl_task_t *ct = jl_current_task; if (!jl_is_expr(e)) { if (jl_is_linenode(e)) { jl_lineno = jl_linenode_line(e); jl_value_t *file = jl_linenode_file(e); if (file != jl_nothing) { assert(jl_is_symbol(file)); jl_filename = jl_symbol_name((jl_sym_t*)file); } return jl_nothing; } if (jl_is_symbol(e)) { char *n = jl_symbol_name((jl_sym_t*)e), *n0 = n; while (*n == '_') ++n; if (*n == 0 && n > n0) jl_eval_errorf(m, "all-underscore identifier used as rvalue"); } return jl_interpret_toplevel_expr_in(m, e, NULL, NULL); } jl_expr_t *ex = (jl_expr_t*)e; if (ex->head == jl_dot_sym && jl_expr_nargs(ex) != 1) { if (jl_expr_nargs(ex) != 2) jl_eval_errorf(m, "syntax: malformed \".\" expression"); jl_value_t *lhs = jl_exprarg(ex, 0); jl_value_t *rhs = jl_exprarg(ex, 1); // only handle `a.b` syntax here, so qualified names can be eval'd in pure contexts if (jl_is_quotenode(rhs) && jl_is_symbol(jl_fieldref(rhs, 0))) { return jl_eval_dot_expr(m, lhs, rhs, fast); } } if (ct->ptls->in_pure_callback) { jl_error("eval cannot be used in a generated function"); } jl_method_instance_t *mfunc = NULL; jl_code_info_t *thk = NULL; JL_GC_PUSH3(&mfunc, &thk, &ex); size_t last_age = ct->world_age; if (!expanded && jl_needs_lowering(e)) { ct->world_age = jl_atomic_load_acquire(&jl_world_counter); ex = (jl_expr_t*)jl_expand_with_loc_warn(e, m, jl_filename, jl_lineno); ct->world_age = last_age; } jl_sym_t *head = jl_is_expr(ex) ? ex->head : NULL; if (head == jl_module_sym) { jl_value_t *val = jl_eval_module_expr(m, ex); JL_GC_POP(); return val; } else if (head == jl_using_sym) { jl_sym_t *name = NULL; jl_module_t *from = eval_import_from(m, ex, "using"); size_t i = 0; if (from) { i = 1; ex = (jl_expr_t*)jl_exprarg(ex, 0); } for (; i < jl_expr_nargs(ex); i++) { jl_value_t *a = jl_exprarg(ex, i); if (jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_dot_sym) { name = NULL; jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)a)->args, &name, "using"); jl_module_t *u = import; if (name != NULL) u = (jl_module_t*)jl_eval_global_var(import, name); if (from) { // `using A: B` syntax jl_module_use(m, import, name); } else { if (!jl_is_module(u)) jl_eval_errorf(m, "invalid using path: \"%s\" does not name a module", jl_symbol_name(name)); // `using A.B` syntax jl_module_using(m, u); if (m == jl_main_module && name == NULL) { // TODO: for now, `using A` in Main also creates an explicit binding for `A` // This will possibly be extended to all modules. import_module(m, u, NULL); } } continue; } else if (from && jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_as_sym && jl_expr_nargs(a) == 2 && jl_is_expr(jl_exprarg(a, 0)) && ((jl_expr_t*)jl_exprarg(a, 0))->head == jl_dot_sym) { jl_sym_t *asname = (jl_sym_t*)jl_exprarg(a, 1); if (jl_is_symbol(asname)) { jl_expr_t *path = (jl_expr_t*)jl_exprarg(a, 0); name = NULL; jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)path)->args, &name, "using"); assert(name); check_macro_rename(name, asname, "using"); // `using A: B as C` syntax jl_module_use_as(m, import, name, asname); continue; } } jl_eval_errorf(m, "syntax: malformed \"using\" statement"); } JL_GC_POP(); return jl_nothing; } else if (head == jl_import_sym) { jl_sym_t *name = NULL; jl_module_t *from = eval_import_from(m, ex, "import"); size_t i = 0; if (from) { i = 1; ex = (jl_expr_t*)jl_exprarg(ex, 0); } for (; i < jl_expr_nargs(ex); i++) { jl_value_t *a = jl_exprarg(ex, i); if (jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_dot_sym) { name = NULL; jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)a)->args, &name, "import"); if (name == NULL) { // `import A` syntax import_module(m, import, NULL); } else { // `import A.B` or `import A: B` syntax jl_module_import(m, import, name); } continue; } else if (jl_is_expr(a) && ((jl_expr_t*)a)->head == jl_as_sym && jl_expr_nargs(a) == 2 && jl_is_expr(jl_exprarg(a, 0)) && ((jl_expr_t*)jl_exprarg(a, 0))->head == jl_dot_sym) { jl_sym_t *asname = (jl_sym_t*)jl_exprarg(a, 1); if (jl_is_symbol(asname)) { jl_expr_t *path = (jl_expr_t*)jl_exprarg(a, 0); name = NULL; jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)path)->args, &name, "import"); if (name == NULL) { // `import A as B` syntax import_module(m, import, asname); } else { check_macro_rename(name, asname, "import"); // `import A.B as C` syntax jl_module_import_as(m, import, name, asname); } continue; } } jl_eval_errorf(m, "syntax: malformed \"import\" statement"); } JL_GC_POP(); return jl_nothing; } else if (head == jl_export_sym) { for (size_t i = 0; i < jl_array_len(ex->args); i++) { jl_sym_t *name = (jl_sym_t*)jl_array_ptr_ref(ex->args, i); if (!jl_is_symbol(name)) jl_eval_errorf(m, "syntax: malformed \"export\" statement"); jl_module_export(m, name); } JL_GC_POP(); return jl_nothing; } else if (head == jl_global_sym) { jl_eval_global_expr(m, ex, 0); JL_GC_POP(); return jl_nothing; } else if (head == jl_const_sym) { jl_sym_t *arg = (jl_sym_t*)jl_exprarg(ex, 0); jl_module_t *gm; jl_sym_t *gs; if (jl_is_globalref(arg)) { gm = jl_globalref_mod(arg); gs = jl_globalref_name(arg); } else { assert(jl_is_symbol(arg)); gm = m; gs = (jl_sym_t*)arg; } jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); jl_declare_constant(b); JL_GC_POP(); return jl_nothing; } else if (head == jl_toplevel_sym) { jl_value_t *res = jl_nothing; int i; for (i = 0; i < jl_array_len(ex->args); i++) { res = jl_toplevel_eval_flex(m, jl_array_ptr_ref(ex->args, i), fast, 0); } JL_GC_POP(); return res; } else if (head == jl_error_sym || head == jl_incomplete_sym) { if (jl_expr_nargs(ex) == 0) jl_eval_errorf(m, "malformed \"%s\" expression", jl_symbol_name(head)); if (jl_is_string(jl_exprarg(ex, 0))) jl_eval_errorf(m, "syntax: %s", jl_string_data(jl_exprarg(ex, 0))); jl_throw(jl_exprarg(ex, 0)); } else if (jl_is_symbol(ex)) { JL_GC_POP(); return jl_eval_global_var(m, (jl_sym_t*)ex); } else if (head == NULL) { JL_GC_POP(); return (jl_value_t*)ex; } int has_intrinsics = 0, has_defs = 0, has_loops = 0, has_opaque = 0, forced_compile = 0; assert(head == jl_thunk_sym); thk = (jl_code_info_t*)jl_exprarg(ex, 0); assert(jl_is_code_info(thk)); assert(jl_typeis(thk->code, jl_array_any_type)); body_attributes((jl_array_t*)thk->code, &has_intrinsics, &has_defs, &has_loops, &has_opaque, &forced_compile); jl_value_t *result; if (forced_compile || has_intrinsics || (!has_defs && fast && has_loops && jl_options.compile_enabled != JL_OPTIONS_COMPILE_OFF && jl_options.compile_enabled != JL_OPTIONS_COMPILE_MIN && jl_get_module_compile(m) != JL_OPTIONS_COMPILE_OFF && jl_get_module_compile(m) != JL_OPTIONS_COMPILE_MIN)) { // use codegen mfunc = method_instance_for_thunk(thk, m); jl_resolve_globals_in_ir((jl_array_t*)thk->code, m, NULL, 0); // Don't infer blocks containing e.g. method definitions, since it's probably not // worthwhile and also unsound (see #24316). // TODO: This is still not correct since an `eval` can happen elsewhere, but it // helps in common cases. size_t world = jl_atomic_load_acquire(&jl_world_counter); ct->world_age = world; if (!has_defs && jl_get_module_infer(m) != 0) { (void)jl_type_infer(mfunc, world, 0); } result = jl_invoke(/*func*/NULL, /*args*/NULL, /*nargs*/0, mfunc); ct->world_age = last_age; } else { // use interpreter assert(thk); if (has_opaque) { jl_resolve_globals_in_ir((jl_array_t*)thk->code, m, NULL, 0); } result = jl_interpret_toplevel_thunk(m, thk); } JL_GC_POP(); return result; } JL_DLLEXPORT jl_value_t *jl_toplevel_eval(jl_module_t *m, jl_value_t *v) { return jl_toplevel_eval_flex(m, v, 1, 0); } // Check module `m` is open for `eval/include`, or throw an error. static void jl_check_open_for(jl_module_t *m, const char* funcname) { if (jl_options.incremental && jl_generating_output()) { if (m != jl_main_module) { // TODO: this was grand-fathered in JL_LOCK(&jl_modules_mutex); int open = ptrhash_has(&jl_current_modules, (void*)m); if (!open && jl_module_init_order != NULL) { size_t i, l = jl_array_len(jl_module_init_order); for (i = 0; i < l; i++) { if (m == (jl_module_t*)jl_array_ptr_ref(jl_module_init_order, i)) { open = 1; break; } } } JL_UNLOCK(&jl_modules_mutex); if (!open && !jl_is__toplevel__mod(m)) { const char* name = jl_symbol_name(m->name); jl_errorf("Evaluation into the closed module `%s` breaks incremental compilation " "because the side effects will not be permanent. " "This is likely due to some other module mutating `%s` with `%s` during " "precompilation - don't do this.", name, name, funcname); } } } } JL_DLLEXPORT void jl_check_top_level_effect(jl_module_t *m, char *fname) { if (jl_current_task->ptls->in_pure_callback) jl_errorf("%s cannot be used in a generated function", fname); jl_check_open_for(m, fname); } JL_DLLEXPORT jl_value_t *jl_toplevel_eval_in(jl_module_t *m, jl_value_t *ex) { jl_task_t *ct = jl_current_task; if (ct->ptls->in_pure_callback) jl_error("eval cannot be used in a generated function"); jl_check_open_for(m, "eval"); jl_value_t *v = NULL; int last_lineno = jl_lineno; const char *last_filename = jl_filename; jl_lineno = 1; jl_filename = "none"; JL_TRY { v = jl_toplevel_eval(m, ex); } JL_CATCH { jl_lineno = last_lineno; jl_filename = last_filename; jl_rethrow(); } jl_lineno = last_lineno; jl_filename = last_filename; assert(v); return v; } JL_DLLEXPORT jl_value_t *jl_infer_thunk(jl_code_info_t *thk, jl_module_t *m) { jl_method_instance_t *li = method_instance_for_thunk(thk, m); JL_GC_PUSH1(&li); jl_resolve_globals_in_ir((jl_array_t*)thk->code, m, NULL, 0); jl_task_t *ct = jl_current_task; jl_code_info_t *src = jl_type_infer(li, ct->world_age, 0); JL_GC_POP(); if (src) return src->rettype; return (jl_value_t*)jl_any_type; } //------------------------------------------------------------------------------ // Code loading: combined parse+eval for include() // Parse julia code from the string `text` at top level, attributing it to // `filename`. This is used during bootstrap, but the real Base.include() is // implemented in Julia code. static jl_value_t *jl_parse_eval_all(jl_module_t *module, jl_value_t *text, jl_value_t *filename) { if (!jl_is_string(text) || !jl_is_string(filename)) { jl_errorf("Expected `String`s for `text` and `filename`"); } jl_task_t *ct = jl_current_task; if (ct->ptls->in_pure_callback) jl_error("cannot use include inside a generated function"); jl_check_open_for(module, "include"); jl_value_t *result = jl_nothing; jl_value_t *ast = NULL; jl_value_t *expression = NULL; JL_GC_PUSH3(&ast, &result, &expression); ast = jl_svecref(jl_parse(jl_string_data(text), jl_string_len(text), filename, 1, 0, (jl_value_t*)jl_all_sym), 0); if (!jl_is_expr(ast) || ((jl_expr_t*)ast)->head != jl_toplevel_sym) { jl_errorf("jl_parse_all() must generate a top level expression"); } int last_lineno = jl_lineno; const char *last_filename = jl_filename; size_t last_age = ct->world_age; int lineno = 0; jl_lineno = 0; jl_filename = jl_string_data(filename); int err = 0; JL_TRY { for (size_t i = 0; i < jl_expr_nargs(ast); i++) { expression = jl_exprarg(ast, i); if (jl_is_linenode(expression)) { // filename is already set above. lineno = jl_linenode_line(expression); jl_lineno = lineno; continue; } expression = jl_expand_with_loc_warn(expression, module, jl_string_data(filename), lineno); ct->world_age = jl_atomic_load_acquire(&jl_world_counter); result = jl_toplevel_eval_flex(module, expression, 1, 1); } } JL_CATCH { result = jl_box_long(jl_lineno); // (ab)use result to root error line err = 1; goto finally; // skip jl_restore_excstack } finally: ct->world_age = last_age; jl_lineno = last_lineno; jl_filename = last_filename; if (err) { if (jl_loaderror_type == NULL) jl_rethrow(); else jl_rethrow_other(jl_new_struct(jl_loaderror_type, filename, result, jl_current_exception())); } JL_GC_POP(); return result; } // Synchronously read content of entire file into a julia String static jl_value_t *jl_file_content_as_string(jl_value_t *filename) { const char *fname = jl_string_data(filename); ios_t f; if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) jl_errorf("File \"%s\" not found", fname); ios_bufmode(&f, bm_none); ios_seek_end(&f); size_t len = ios_pos(&f); jl_value_t *text = jl_alloc_string(len); ios_seek(&f, 0); if (ios_readall(&f, jl_string_data(text), len) != len) jl_errorf("Error reading file \"%s\"", fname); ios_close(&f); return text; } // Load and parse julia code from the file `filename`. Eval the resulting // statements into `module` after applying `mapexpr` to each one. JL_DLLEXPORT jl_value_t *jl_load_(jl_module_t *module, jl_value_t *filename) { jl_value_t *text = jl_file_content_as_string(filename); JL_GC_PUSH1(&text); jl_value_t *result = jl_parse_eval_all(module, text, filename); JL_GC_POP(); return result; } // Code loading - julia.h C API with native C types // Parse julia code from `filename` and eval into `module`. JL_DLLEXPORT jl_value_t *jl_load(jl_module_t *module, const char *filename) { jl_value_t *filename_ = NULL; JL_GC_PUSH1(&filename_); filename_ = jl_cstr_to_string(filename); jl_value_t *result = jl_load_(module, filename_); JL_GC_POP(); return result; } // Parse julia code from the string `text` of length `len`, attributing it to // `filename`. Eval the resulting statements into `module`. JL_DLLEXPORT jl_value_t *jl_load_file_string(const char *text, size_t len, char *filename, jl_module_t *module) { jl_value_t *text_ = NULL; jl_value_t *filename_ = NULL; JL_GC_PUSH2(&text_, &filename_); text_ = jl_pchar_to_string(text, len); filename_ = jl_cstr_to_string(filename); jl_value_t *result = jl_parse_eval_all(module, text_, filename_); JL_GC_POP(); return result; } //-------------------------------------------------- // Code loading helpers for bootstrap JL_DLLEXPORT jl_value_t *jl_prepend_cwd(jl_value_t *str) { size_t sz = 1024; char path[1024]; int c = uv_cwd(path, &sz); if (c < 0) { jl_errorf("could not get current directory"); } path[sz] = '/'; // fix later with normpath if Windows const char *fstr = (const char*)jl_string_data(str); if (strlen(fstr) + sz >= 1024) { jl_errorf("use a bigger buffer for jl_fullpath"); } strcpy(path + sz + 1, fstr); return jl_cstr_to_string(path); } #ifdef __cplusplus } #endif