/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/toplevel.c
1 001 строка
37 KB
Keno Fischer
Turn Expr(:method) into a builtin (#62320)
29 июл 2026, 07:54
Не верифицирован
29 июл 2026, 07:54
8dc3ef6
Код
Авторство
О чём код?
// 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 <stdlib.h> #include <string.h> #include <setjmp.h> #include <sys/types.h> #include <errno.h> #if defined(_OS_WINDOWS_) #include <malloc.h> #else #include <unistd.h> #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 _Atomic(int) jl_lineno = 0; // need to update jl_fprint_critical_error if this is TLS // current file name JL_DLLEXPORT _Atomic(const char *) jl_filename = "none"; // need to update jl_fprint_critical_error if this is TLS 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) JL_CANSAFEPOINT; htable_t jl_current_modules; jl_mutex_t jl_modules_mutex; // During incremental compilation, the following gets set jl_module_t *jl_precompile_toplevel_module = NULL; // the first toplevel module being defined jl_module_t *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_initial_using(m, base_module); return 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"), NULL, 0, 1); // baremodule Main; end jl_set_initial_const(jl_core_module, jl_symbol("Main"), (jl_value_t*)jl_main_module, 0); // const Main.Core = Core jl_set_initial_const(jl_main_module, jl_symbol("Core"), (jl_value_t*)jl_core_module, 0); // const Core.Main = Main } void jl_module_run_initializer(jl_module_t *m) { JL_TIMING(INIT_MODULE, INIT_MODULE); jl_timing_show_module(m, JL_TIMING_DEFAULT_BLOCK); 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_value_t *f = jl_get_global_value(m, jl_symbol("__init__"), ct->world_age); if (f != NULL) { JL_GC_PUSH1(&f); jl_apply(&f, 1); JL_GC_POP(); } 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(ct))); } } } static void jl_register_root_module(jl_module_t *m) JL_CANSAFEPOINT { jl_value_t *register_module_func = NULL; assert(jl_base_module); 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_task_t *ct = jl_current_task; size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); jl_apply(args, 2); ct->world_age = last_age; } jl_array_t *jl_get_loaded_modules(void) { jl_value_t *loaded_modules_array = NULL; if (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_value_t*)loaded_modules_array); return NULL; } static int jl_is__toplevel__mod(jl_module_t *mod, jl_task_t *ct) JL_CANSAFEPOINT { return jl_base_module && (jl_value_t*)mod == jl_get_global_value(jl_base_module, jl_symbol("__toplevel__"), ct->world_age); } JL_DLLEXPORT void jl_setup_new_module(jl_module_t *m, jl_value_t *syntax_version) JL_CANSAFEPOINT { jl_task_t *ct = jl_current_task; size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); jl_value_t *f = jl_get_global_value(jl_base_module, jl_symbol("_setup_module!"), ct->world_age); if (f != NULL) { jl_value_t **fargs; JL_GC_PUSHARGS(fargs, 3); fargs[0] = f; fargs[1] = (jl_value_t*)m; fargs[2] = syntax_version; jl_apply(fargs, 3); JL_GC_POP(); } ct->world_age = last_age; } JL_DLLEXPORT jl_module_t *jl_begin_new_module(jl_module_t *parent_module, jl_sym_t *name, jl_value_t *syntax_version, int std_imports, const char *filename, int lineno) JL_CANSAFEPOINT { jl_task_t *ct = jl_current_task; int is_parent__toplevel__ = jl_is__toplevel__mod(parent_module, ct); // If we have `Base`, don't also try to import `Core` - the `Base` exports are a superset. // While we allow multiple imports of the same binding from different modules, various error printing // performs reflection on which module a binding came from and we'd prefer users see "Base" here. jl_module_t *newm = jl_new_module_(name, is_parent__toplevel__ ? NULL : parent_module, std_imports && jl_base_module != NULL ? 0 : 1, 1); 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); // copy parent environment info into submodule newm->uuid = parent_module->uuid; jl_gc_write(newm, newm->file, jl_sym_t, jl_symbol(filename)); newm->line = lineno; // add standard imports unless baremodule if (std_imports && jl_base_module != NULL) { jl_setup_new_module(newm, syntax_version); } if (parent_module == jl_main_module && name == jl_symbol("Base") && jl_base_module == NULL) { // pick up Base module during bootstrap jl_base_module = newm; } if (is_parent__toplevel__) { jl_register_root_module(newm); if (jl_options.incremental && jl_precompile_toplevel_module == NULL) { jl_precompile_toplevel_module = newm; } } else { jl_declare_constant_val(NULL, parent_module, name, (jl_value_t*)newm); } JL_GC_POP(); return newm; } JL_DLLEXPORT void jl_end_new_module(jl_module_t *newm) JL_CANSAFEPOINT { jl_value_t *form = NULL; JL_GC_PUSH1(&form); 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 direct 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 if (!jl_generating_output()) { if (!ptrhash_has(&jl_current_modules, (void*)newm->parent)) { size_t i, l = jl_array_nrows(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_nrows(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_GC_POP(); } static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex, const char **toplevel_filename, int *toplevel_lineno) JL_CANSAFEPOINT { assert(ex->head == jl_module_sym); jl_value_t *syntax_version = jl_nothing; int idx = 0; if (!jl_is_bool(jl_exprarg(ex, idx))) { syntax_version = jl_exprarg(ex, idx++); } if (jl_array_nrows(ex->args) != idx+3 || !jl_is_expr(jl_exprarg(ex, idx+2))) { jl_error("syntax: malformed module expression"); } int std_imports = (jl_exprarg(ex, idx++) == jl_true); jl_sym_t *name = (jl_sym_t*)jl_exprarg(ex, idx++); if (!jl_is_symbol(name)) { jl_type_error("module", (jl_value_t*)jl_symbol_type, (jl_value_t*)name); } if (((jl_expr_t *)(jl_exprarg(ex, idx)))->head != jl_symbol("block")) { jl_error("syntax: module expression third argument must be a block"); } jl_array_t *stmts = ((jl_expr_t*)jl_exprarg(ex, idx))->args; int lineno = 0; const char *filename = "none"; if (jl_array_nrows(stmts) > 0) { jl_value_t *lineex = jl_array_ptr_ref(stmts, 0); if (jl_is_linenode(lineex)) { lineno = jl_linenode_line(lineex); jl_value_t *file = jl_linenode_file(lineex); if (jl_is_symbol(file)) filename = jl_symbol_name((jl_sym_t*)file); } } jl_module_t *newm = jl_begin_new_module(parent_module, name, syntax_version, std_imports, filename, lineno); JL_GC_PROMISE_ROOTED(newm); // Rooted in jl_current_modules jl_eval_toplevel_stmts(newm, stmts, 1, 0, toplevel_filename, toplevel_lineno); jl_end_new_module(newm); return (jl_value_t*)newm; } static jl_value_t *jl_eval_dot_expr(jl_task_t *ct, jl_module_t *m, jl_value_t *x, jl_value_t *f, int fast, const char **toplevel_filename, int *toplevel_lineno) JL_CANSAFEPOINT { jl_value_t **args; JL_GC_PUSHARGS(args, 3); args[1] = jl_toplevel_eval_flex(m, x, fast, 0, toplevel_filename, toplevel_lineno); args[2] = jl_toplevel_eval_flex(m, f, fast, 0, toplevel_filename, toplevel_lineno); if (jl_is_module(args[1])) { JL_TYPECHK(getglobal, symbol, args[2]); args[0] = jl_eval_global_var((jl_module_t*)args[1], (jl_sym_t*)args[2], ct->world_age); } else { args[0] = jl_eval_global_var(jl_base_relative_to(m), jl_symbol("getproperty"), ct->world_age); 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_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, int strong) { // create uninitialized mutable binding for "global x" decl sometimes or probably jl_module_t *gm; jl_sym_t *gs; assert(!jl_is_expr(arg)); // Should have been resolved before this 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_LOCK(&world_counter_lock); size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; jl_binding_t *b = jl_get_module_binding(gm, gs, 1); jl_binding_partition_t *bpart = NULL; if (!strong && set_type) jl_error("Weak global definitions cannot have types"); enum jl_partition_kind new_kind = strong ? PARTITION_KIND_GLOBAL : PARTITION_KIND_DECLARED; jl_value_t *global_type = set_type; if (strong && !global_type) global_type = (jl_value_t*)jl_any_type; while (1) { bpart = jl_get_binding_partition(b, new_world); enum jl_partition_kind kind = jl_binding_kind(bpart); if (kind != PARTITION_KIND_GLOBAL) { if (jl_bkind_is_some_implicit(kind) || kind == PARTITION_KIND_DECLARED) { if (kind == new_kind) { if (!set_type) goto done; goto check_type; } check_safe_newbinding(gm, gs); if (jl_atomic_load_relaxed(&bpart->min_world) == new_world) { bpart->kind = new_kind | (bpart->kind & PARTITION_MASK_FLAG); jl_gc_write(bpart, bpart->restriction, jl_value_t, global_type); continue; } else { jl_replace_binding_locked(b, bpart, global_type, new_kind, new_world); } break; } else if (set_type) { if (jl_bkind_is_some_constant(kind)) { jl_errorf("cannot set type for constant %s.%s.", jl_symbol_name(gm->name), jl_symbol_name(gs)); } else { jl_errorf("cannot set type for imported binding %s.%s.", jl_symbol_name(gm->name), jl_symbol_name(gs)); } } } if (set_type) { check_type: ; jl_value_t *old_ty = bpart->restriction; JL_GC_PROMISE_ROOTED(old_ty); if (!jl_types_equal(set_type, old_ty)) { jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", jl_symbol_name(gm->name), jl_symbol_name(gs)); } } goto done; } jl_atomic_store_release(&jl_world_counter, new_world); done: JL_UNLOCK(&world_counter_lock); } // 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, jl_array_t *body, int *has_ccall, int *has_defs, int *has_opaque) JL_CANSAFEPOINT { 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_copyast_sym) { // Note: `copyast` is included here since it indicates the presence of // `quote` and probably `eval`. *has_defs = 1; return; } else if (jl_is_toplevel_only_expr(v)) { *has_defs = 1; } else if (head == jl_cfunction_sym) { *has_ccall = 1; return; } else if (head == jl_foreigncall_sym) { *has_ccall = 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_ssavalue(f)) { f = jl_array_ptr_ref(body, ((jl_ssavalue_t*)f)->id - 1); } if (jl_is_globalref(f)) { jl_module_t *mod = jl_globalref_mod(f); jl_sym_t *name = jl_globalref_name(f); jl_binding_t *b = jl_get_binding(mod, name); called = jl_get_latest_binding_value_if_const(b); } else if (jl_is_quotenode(f)) { called = jl_quotenode_value(f); } if (called != NULL) { if (jl_is_intrinsic(called) && jl_unbox_int32(called) == (int)llvmcall) { *has_ccall = 1; } // TODO: rely on latestworld instead of function callee detection here (or add it to jl_is_toplevel_only_expr) if (called == BUILTIN(_typebody) || called == BUILTIN(declare_const) || called == BUILTIN(define_method)) { *has_defs = 1; } } return; } int i; for (i = 0; i < jl_array_nrows(e->args); i++) { jl_value_t *a = jl_exprarg(e, i); if (jl_is_expr(a)) expr_attributes(a, body, has_ccall, has_defs, has_opaque); } } int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile) { jl_array_t *body = src->code; assert(jl_typetagis(body, jl_array_any_type)); size_t i; int has_ccall = 0, has_defs = 0, has_opaque = 0; if (include_force_compile && jl_has_meta(body, jl_force_compile_sym)) return 1; for(i=0; i < jl_array_nrows(body); i++) { jl_value_t *stmt = jl_array_ptr_ref(body,i); expr_attributes(stmt, body, &has_ccall, &has_defs, &has_opaque); if (has_ccall) return 1; } return 0; } static void body_attributes(jl_array_t *body, int *has_ccall, int *has_defs, int *has_loops, int *has_opaque, int *forced_compile) JL_CANSAFEPOINT { size_t i; *has_loops = 0; for(i=0; i < jl_array_nrows(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, body, has_ccall, has_defs, has_opaque); } *forced_compile = jl_has_meta(body, jl_force_compile_sym); } 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_export_sym || ((jl_expr_t*)e)->head == jl_public_sym || ((jl_expr_t*)e)->head == jl_thunk_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); } static 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_export_sym || head == jl_public_sym || head == jl_thunk_sym || head == jl_toplevel_sym || head == jl_error_sym || head == jl_incomplete_sym) { return 0; } return 1; } JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst_for_uninferred(jl_method_instance_t *mi, jl_code_info_t *src) { jl_svec_t *edges = jl_emptysvec; if (src->edges && jl_is_svec(src->edges)) edges = (jl_svec_t*)src->edges; // Do not compress this, we expect it to be shortlived. jl_code_instance_t *ci = jl_new_codeinst(mi, (jl_value_t*)jl_uninferred_sym, (jl_value_t*)jl_any_type, (jl_value_t*)jl_any_type, jl_nothing, (jl_value_t*)src, 0, src->min_world, src->max_world, 0, NULL, NULL, edges); return ci; } JL_DLLEXPORT jl_method_instance_t *jl_method_instance_for_thunk(jl_code_info_t *src, jl_module_t *module) JL_CANSAFEPOINT { jl_method_instance_t *mi = jl_new_method_instance_uninit(); mi->specTypes = (jl_value_t*)jl_emptytuple_type; mi->def.module = module; JL_GC_PUSH1(&mi); jl_code_instance_t *ci = jl_new_codeinst_for_uninferred(mi, src); jl_gc_write_atomic(mi, mi->cache, jl_code_instance_t, ci, relaxed); JL_GC_POP(); return mi; } // Eval `throw(exc)` in module `m`. // Used in `jl_toplevel_eval_flex` instead of `jl_throw` so that the error // location in julia code gets into the backtrace. static void jl_eval_throw(jl_module_t *m, jl_value_t *exc, const char *filename, int lineno) JL_CANSAFEPOINT { jl_value_t *throw_ex = (jl_value_t*)jl_exprn(jl_call_sym, 2); JL_GC_PUSH1(&throw_ex); jl_exprargset(throw_ex, 0, BUILTIN(throw)); jl_exprargset(throw_ex, 1, exc); jl_toplevel_eval_flex(m, throw_ex, 0, 0, &filename, &lineno); JL_GC_POP(); } // Format error message and call jl_eval static void jl_eval_errorf(jl_module_t *m, const char *filename, int lineno, const char* fmt, ...) JL_CANSAFEPOINT { va_list args; va_start(args, fmt); jl_value_t *exc = jl_vexceptionf(jl_errorexception_type, fmt, args); va_end(args); JL_GC_PUSH1(&exc); jl_eval_throw(m, exc, filename, lineno); JL_GC_POP(); } JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2( jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, enum jl_partition_kind constant_kind) { JL_GC_PUSH1(&val); JL_LOCK(&world_counter_lock); size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; jl_binding_partition_t *bpart = jl_declare_constant_val3(b, mod, var, val, constant_kind, new_world); if (jl_atomic_load_relaxed(&bpart->min_world) == new_world) jl_atomic_store_release(&jl_world_counter, new_world); JL_UNLOCK(&world_counter_lock); JL_GC_POP(); return bpart; } JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val) { return jl_declare_constant_val2(b, mod, var, val, val ? PARTITION_KIND_CONST : PARTITION_KIND_UNDEF_CONST); } 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) { jl_task_t *ct = jl_current_task; size_t last_age = ct->world_age; jl_value_t *root = NULL; JL_GC_PUSH1(&root); jl_value_t *res = jl_nothing; int i; for (i = 0; i < jl_array_nrows(stmts); i++) { root = jl_array_ptr_ref(stmts, i); if (jl_needs_lowering(root)) { root = jl_svecref(jl_lower(root, m, *toplevel_filename, *toplevel_lineno, ~(size_t)0, need_value), 0); } ct->world_age = jl_atomic_load_acquire(&jl_world_counter); res = jl_toplevel_eval_flex(m, root, fast, 1, toplevel_filename, toplevel_lineno); } ct->world_age = last_age; JL_GC_POP(); return res; } 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) { jl_task_t *ct = jl_current_task; if (jl_is_globalref(e)) { return jl_eval_globalref((jl_globalref_t*)e, ct->world_age); } 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, *toplevel_filename, *toplevel_lineno, "all-underscore identifiers are write-only and their values cannot be used in expressions"); return jl_eval_global_var(m, (jl_sym_t*)e, ct->world_age); } if (!jl_is_expr(e)) { if (jl_is_linenode(e)) { *toplevel_lineno = jl_linenode_line(e); jl_value_t *file = jl_linenode_file(e); if (file != jl_nothing) { assert(jl_is_symbol(file)); *toplevel_filename = jl_symbol_name((jl_sym_t*)file); } // Not thread safe. For debugging and last resort error messages (jl_fprint_critical_error) only. jl_atomic_store_relaxed(&jl_filename, *toplevel_filename); jl_atomic_store_relaxed(&jl_lineno, *toplevel_lineno); return jl_nothing; } 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, *toplevel_filename, *toplevel_lineno, "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(ct, m, lhs, rhs, fast, toplevel_filename, toplevel_lineno); } } if (ct->ptls->in_pure_callback) { jl_error("eval cannot be used in a generated function"); } jl_code_info_t *thk = NULL; jl_value_t *root = NULL; JL_GC_PUSH3(&thk, &ex, &root); if (!expanded && (jl_needs_lowering(e))) { ex = (jl_expr_t*)jl_svecref(jl_lower(e, m, *toplevel_filename, *toplevel_lineno, ~(size_t)0, 1), 0); } 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, toplevel_filename, toplevel_lineno); JL_GC_POP(); return val; } else if (head == jl_export_sym || head == jl_public_sym) { int exported = head == jl_export_sym; for (size_t i = 0; i < jl_array_nrows(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, *toplevel_filename, *toplevel_lineno, exported ? "syntax: malformed \"export\" statement" : "syntax: malformed \"public\" statement"); } jl_module_public(m, jl_array_data(ex->args, jl_value_t*), jl_array_len(ex->args), exported); JL_GC_POP(); return jl_nothing; } else if (head == jl_toplevel_sym) { jl_value_t *res = jl_eval_toplevel_stmts(m, ex->args, fast, 1, toplevel_filename, toplevel_lineno); 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, *toplevel_filename, *toplevel_lineno, "malformed \"%s\" expression", jl_symbol_name(head)); if (jl_is_string(jl_exprarg(ex, 0))) jl_eval_errorf(m, *toplevel_filename, *toplevel_lineno, "syntax: %s", jl_string_data(jl_exprarg(ex, 0))); jl_eval_throw(m, jl_exprarg(ex, 0), *toplevel_filename, *toplevel_lineno); } else if (jl_is_symbol(ex)) { JL_GC_POP(); return jl_eval_global_var(m, (jl_sym_t*)ex, ct->world_age); } else if (head == NULL) { JL_GC_POP(); return (jl_value_t*)ex; } assert(head == jl_thunk_sym); thk = (jl_code_info_t*)jl_exprarg(ex, 0); if (!jl_is_code_info(thk) || !jl_typetagis(thk->code, jl_array_any_type)) { jl_eval_errorf(m, *toplevel_filename, *toplevel_lineno, "malformed \"thunk\" statement"); } jl_value_t *result = jl_eval_thunk(m, thk, fast); JL_GC_POP(); return result; } // Evaluate lowered IR thunk `thk` at top level in module `m` in the latest world JL_DLLEXPORT jl_value_t *jl_eval_thunk(jl_module_t *JL_NONNULL m, jl_code_info_t *thk, int fast) { jl_task_t *ct = jl_current_task; JL_TYPECHK(jl_eval_thunk, code_info, (jl_value_t*)thk); JL_TYPECHK(jl_eval_thunk, array_any, (jl_value_t*)thk->code); // Set global jl_lineno/jl_filename to file and line info for the original // start of this block. int last_lineno = jl_atomic_load_relaxed(&jl_lineno); const char *last_filename = jl_atomic_load_relaxed(&jl_filename); int toplevel_lineno = jl_cdi_firstline_all(thk->debuginfo); const char *toplevel_filename = jl_cdi_file(thk->debuginfo); jl_atomic_store_relaxed(&jl_lineno, toplevel_lineno); jl_atomic_store_relaxed(&jl_filename, toplevel_filename); size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); int has_ccall = 0, has_defs = 0, has_loops = 0, has_opaque = 0, forced_compile = 0; body_attributes((jl_array_t*)thk->code, &has_ccall, &has_defs, &has_loops, &has_opaque, &forced_compile); jl_method_instance_t *mfunc = NULL; jl_value_t *result; JL_GC_PUSH1(&mfunc); if (has_ccall || ((forced_compile || (!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 = jl_method_instance_for_thunk(thk, m); jl_resolve_definition_effects_in_ir((jl_array_t *)thk->code, m, NULL, NULL, 0); // Update world again as resolving definition effects (eg ccall return type) may have side effects size_t world = jl_atomic_load_acquire(&jl_world_counter); ct->world_age = world; // Don't infer blocks containing e.g. method definitions, since it's probably // not worthwhile. if (!has_defs && jl_get_module_infer(m) != 0) { (void)jl_type_infer(mfunc, world, SOURCE_MODE_ABI, jl_options.trim); } result = jl_invoke_oneshot(/*func*/ NULL, /*args*/ NULL, /*nargs*/ 0, mfunc); } else { // use interpreter assert(thk); if (has_opaque) { jl_resolve_definition_effects_in_ir((jl_array_t*)thk->code, m, NULL, NULL, 0); } result = jl_interpret_toplevel_thunk(m, thk); } ct->world_age = last_age; jl_atomic_store_relaxed(&jl_lineno, last_lineno); jl_atomic_store_relaxed(&jl_filename, last_filename); JL_GC_POP(); return result; } JL_DLLEXPORT jl_value_t *jl_toplevel_eval(jl_module_t *m, jl_value_t *v) { const char *filename = jl_atomic_load_relaxed(&jl_filename); int lineno = jl_atomic_load_relaxed(&jl_lineno); return jl_toplevel_eval_flex(m, v, 1, 0, &filename, &lineno); } // Check module `m` is open for `eval/include`, or throw an error. JL_DLLEXPORT void jl_check_top_level_effect(jl_module_t *m, const char *fname) JL_CANSAFEPOINT { if (jl_current_task->ptls->in_pure_callback) jl_errorf("%s cannot be used in a generated function", fname); 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_nrows(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, jl_current_task)) { 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, fname); } } } } JL_DLLEXPORT jl_value_t *jl_toplevel_eval_in(jl_module_t *m, jl_value_t *ex) { jl_check_top_level_effect(m, "eval"); jl_value_t *v = NULL; int last_lineno = jl_atomic_load_relaxed(&jl_lineno); const char *last_filename = jl_atomic_load_relaxed(&jl_filename); jl_task_t *ct = jl_current_task; jl_atomic_store_relaxed(&jl_lineno, 1); jl_atomic_store_relaxed(&jl_filename, "none"); size_t last_age = ct->world_age; JL_TRY { ct->world_age = jl_atomic_load_acquire(&jl_world_counter); v = jl_toplevel_eval(m, ex); } JL_CATCH { jl_atomic_store_relaxed(&jl_lineno, last_lineno); jl_atomic_store_relaxed(&jl_filename, last_filename); jl_rethrow(); } jl_atomic_store_relaxed(&jl_lineno, last_lineno); jl_atomic_store_relaxed(&jl_filename, last_filename); ct->world_age = last_age; assert(v); return v; } //------------------------------------------------------------------------------ // 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) JL_CANSAFEPOINT { if (!jl_is_string(text) || !jl_is_string(filename)) { jl_errorf("Expected `String`s for `text` and `filename`"); } jl_check_top_level_effect(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, module), 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"); } jl_task_t *ct = jl_current_task; int last_lineno = jl_atomic_load_relaxed(&jl_lineno); const char *last_filename = jl_atomic_load_relaxed(&jl_filename); int lineno = 0; jl_atomic_store_relaxed(&jl_lineno, 0); const char *filename_str = jl_string_data(filename); jl_atomic_store_relaxed(&jl_filename, filename_str); JL_TRY { size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); 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_atomic_store_relaxed(&jl_lineno, lineno); continue; } expression = jl_svecref(jl_lower(expression, module, jl_string_data(filename), lineno, ~(size_t)0, 1), 0); ct->world_age = jl_atomic_load_acquire(&jl_world_counter); result = jl_toplevel_eval_flex(module, expression, 1, 1, &filename_str, &lineno); } ct->world_age = last_age; } JL_CATCH { result = jl_box_long(lineno); // (ab)use result to root error line jl_atomic_store_relaxed(&jl_lineno, last_lineno); jl_atomic_store_relaxed(&jl_filename, last_filename); if (jl_loaderror_type == NULL) jl_rethrow(); else jl_rethrow_other(jl_new_struct(jl_loaderror_type, filename, result, jl_current_exception(ct))); } jl_atomic_store_relaxed(&jl_lineno, last_lineno); jl_atomic_store_relaxed(&jl_filename, last_filename); 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) JL_CANSAFEPOINT { 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_CANSAFEPOINT { 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) JL_CANSAFEPOINT { 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); } JL_DLLEXPORT jl_value_t *jl_prepend_string(jl_value_t *prefix, jl_value_t *str) JL_CANSAFEPOINT { char path[1024]; const char *pstr = (const char*)jl_string_data(prefix); size_t sz = strlen(pstr); const char *fstr = (const char*)jl_string_data(str); if (strlen(fstr) + sz >= sizeof(path)) { jl_errorf("use a bigger buffer for jl_fullpath"); } strcpy(path, pstr); strcpy(path + sz, fstr); return jl_cstr_to_string(path); } #ifdef __cplusplus } #endif