Initialization of the Julia runtime
كيف ينفذ وقت تشغيل جوليا julia -e 'println("Hello World!")'
؟
main()
تبدأ التنفيذ في main()
in cli/loader_exe.c
، والذي يستدعي jl_load_repl()
في cli/loader_lib.c
الذي يقوم بتحميل بعض المكتبات، وفي النهاية يستدعي jl_repl_entrypoint()
in src/jlapi.c
.
jl_repl_entrypoint()
يستدعي libsupport_init()
لتعيين إعدادات اللغة لمكتبة C وتهيئة مكتبة "ios" (انظر ios_init_stdstreams()
و Legacy ios.c
library).
التالي jl_parse_opts()
يتم استدعاؤه لمعالجة خيارات سطر الأوامر. لاحظ أن jl_parse_opts()
تتعامل فقط مع الخيارات التي تؤثر على توليد الشيفرة أو التهيئة المبكرة. يتم التعامل مع الخيارات الأخرى لاحقًا بواسطة exec_options()
in base/client.jl
.
jl_parse_opts()
يخزن خيارات سطر الأوامر في global jl_options
struct.
julia_init()
julia_init()
in init.c
يتم استدعاؤه بواسطة main()
ويستدعي _julia_init()
in init.c
.
_julia_init()
يبدأ باستدعاء libsupport_init()
مرة أخرى (لا يفعل شيئًا في المرة الثانية).
restore_signals()
يُطلق لتصفير قناع معالج الإشارة.
jl_resolve_sysimg_location()
يبحث في المسارات المكونة عن صورة النظام الأساسية. انظر Building the Julia system image.
jl_gc_init()
يقوم بإعداد تجمعات التخصيص والقوائم للمرجع الضعيف، والقيم المحفوظة، والتصفية النهائية.
jl_init_frontend()
يقوم بتحميل وت初始化 صورة femtolisp مسبقة التجميع تحتوي على الماسح/المحلل.
jl_init_types()
ينشئ كائنات وصف نوع jl_datatype_t
لـ built-in types defined in julia.h
. على سبيل المثال.
jl_any_type = jl_new_abstracttype(jl_symbol("Any"), core, NULL, jl_emptysvec);
jl_any_type->super = jl_any_type;
jl_type_type = jl_new_abstracttype(jl_symbol("Type"), core, jl_any_type, jl_emptysvec);
jl_int32_type = jl_new_primitivetype(jl_symbol("Int32"), core,
jl_any_type, jl_emptysvec, 32);
jl_init_tasks()
ينشئ كائن jl_datatype_t* jl_task_type
؛ ويهيئ الهيكل العالمي jl_root_task
؛ ويضبط jl_current_task
على المهمة الجذرية.
jl_init_codegen()
يقوم بتهيئة LLVM library.
jl_init_serializer()
يقوم بتهيئة علامات التسلسل الثمانية بتنسيق 8 بت لقيم jl_value_t
المدمجة.
إذا لم يكن هناك ملف sysimg (!jl_options.image_file
) ، فسيتم إنشاء وحدات Core
و Main
ويتم تقييم boot.jl
:
jl_core_module = jl_new_module(jl_symbol("Core"))
ينشئ وحدة Core
في جوليا.
jl_init_intrinsic_functions()
ينشئ وحدة جوليا جديدة Intrinsics
تحتوي على رموز ثابتة jl_intrinsic_type
. هذه الرموز تعرف رمزًا عدديًا لكل intrinsic function. emit_intrinsic()
يترجم هذه الرموز إلى تعليمات LLVM أثناء توليد الشيفرة.
jl_init_primitives()
يربط دوال C برموز دوال جوليا. على سبيل المثال، الرمز Core.:(===)()
مرتبط بمؤشر دالة C jl_f_is()
عن طريق استدعاء add_builtin_func("===", jl_f_is)
.
jl_new_main_module()
ينشئ الوحدة العالمية "Main" ويضبط jl_current_task->current_module = jl_main_module
.
ملاحظة: _julia_init()
then sets jl_root_task->current_module = jl_core_module
. jl_root_task
هو اسم مستعار لـ jl_current_task
في هذه النقطة، لذا يتم الكتابة فوق current_module
الذي تم تعيينه بواسطة jl_new_main_module()
أعلاه.
jl_load("boot.jl", sizeof("boot.jl"))
يستدعي jl_parse_eval_all
الذي يستدعي بشكل متكرر jl_toplevel_eval_flex()
لتنفيذ boot.jl
. <!– TODO – drill down into eval? –>
jl_get_builtin_hooks()
يقوم بتهيئة مؤشرات C العالمية إلى المتغيرات العالمية في جوليا المعرفة في boot.jl
.
jl_init_box_caches()
يخصص مسبقًا كائنات قيمة صحيحة مغلفة عالمية للقيم حتى 1024. هذا يسرع تخصيص الأعداد الصحيحة المغلفة لاحقًا. على سبيل المثال:
jl_value_t *jl_box_uint8(uint32_t x)
{
return boxed_uint8_cache[(uint8_t)x];
}
_julia_init()
iterates فوق jl_core_module->bindings.table
بحثًا عن قيم jl_datatype_t
ويضبط بادئة اسم النوع إلى jl_core_module
.
jl_add_standard_imports(jl_main_module)
does "using Base" in the "Main" module.
ملاحظة: _julia_init()
الآن يعود إلى jl_root_task->current_module = jl_main_module
كما كان قبل أن يتم تعيينه إلى jl_core_module
أعلاه.
يتم تهيئة معالجات الإشارات الخاصة بالمنصة لـ SIGSEGV
(OSX، Linux) و SIGFPE
(Windows).
إشارات أخرى (SIGINFO, SIGBUS, SIGILL, SIGTERM, SIGABRT, SIGQUIT, SIGSYS
و SIGPIPE
) متصلة بـ sigdie_handler()
الذي يطبع تتبعًا للعودة.
jl_init_restored_module()
يستدعي jl_module_run_initializer()
لكل وحدة تم فك تسلسلها لتشغيل دالة __init__()
.
أخيرًا sigint_handler()
متصل بـ SIGINT
ويستدعي jl_throw(jl_interrupt_exception)
.
_julia_init()
ثم تعيد back to main()
in cli/loader_exe.c
و main()
تستدعي repl_entrypoint(argc, (char**)argv)
.
repl_entrypoint()
repl_entrypoint()
يقوم بتحميل محتويات argv[]
إلى Base.ARGS
.
إذا تم توفير ملف ".jl" "برنامج" في سطر الأوامر، فإن exec_program()
يستدعي jl_load(program,len)
الذي يستدعي jl_parse_eval_all
الذي يستدعي مرارًا jl_toplevel_eval_flex()
لتنفيذ البرنامج.
ومع ذلك، في مثالنا (julia -e 'println("Hello World!")'
)، jl_get_global(jl_base_module, jl_symbol("_start"))
يبحث عن Base._start
و jl_apply()
ينفذها.
Base._start
Base._start
يستدعي Base.exec_options
الذي يستدعي jl_parse_input_line("println("Hello World!")")
لإنشاء كائن تعبير و Core.eval(Main, ex)
لتنفيذ التعبير المحلل ex
في سياق وحدة Main
.
Core.eval
Core.eval(Main, ex)
يستدعي jl_toplevel_eval_in(m, ex)
، الذي يستدعي jl_toplevel_eval_flex
. jl_toplevel_eval_flex
ينفذ خوارزمية بسيطة لتحديد ما إذا كان يجب تجميع كود معين أو تشغيله بواسطة المفسر. عند إعطائه println("Hello World!")
، عادةً ما يقرر تشغيل الكود بواسطة المفسر، وفي هذه الحالة يستدعي jl_interpret_toplevel_thunk
، الذي يستدعي بعد ذلك eval_body
.
تظهر تفريغ المكدس أدناه كيف يعمل المفسر من خلال طرق مختلفة لـ Base.println()
و Base.print()
قبل الوصول إلى write(s::IO, a::Array{T}) where T
الذي يقوم بـ ccall(jl_uv_write())
.
jl_uv_write()
يستدعي uv_write()
لكتابة "Hello World!" إلى JL_STDOUT
. انظر Libuv wrappers for stdio.
Hello World!
Stack frame | Source code | Notes |
---|---|---|
jl_uv_write() | jl_uv.c | called though ccall |
julia_write_282942 | stream.jl | function write!(s::IO, a::Array{T}) where T |
julia_print_284639 | ascii.jl | print(io::IO, s::String) = (write(io, s); nothing) |
jlcall_print_284639 | ||
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.print(Base.TTY, String) |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.print(Base.TTY, String, Char, Char...) |
jl_apply() | julia.h | |
jl_f_apply() | builtins.c | |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.println(Base.TTY, String, String...) |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.println(String,) |
jl_apply() | julia.h | |
do_call() | interpreter.c | |
eval_body() | interpreter.c | |
jl_interpret_toplevel_thunk | interpreter.c | |
jl_toplevel_eval_flex | toplevel.c | |
jl_toplevel_eval_in | toplevel.c | |
Core.eval | boot.jl |
نظرًا لأن مثالنا يحتوي على استدعاء دالة واحدة فقط، والتي قامت بعملها في طباعة "Hello World!"، فإن المكدس الآن يتراجع بسرعة إلى main()
.
jl_atexit_hook()
main()
يستدعي jl_atexit_hook()
. هذا يستدعي Base._atexit
، ثم يستدعي jl_gc_run_all_finalizers()
وينظف مقبض libuv.
julia_save()
أخيرًا، main()
تستدعي julia_save()
، والتي إذا تم طلبها من سطر الأوامر، تحفظ حالة وقت التشغيل إلى صورة نظام جديدة. انظر jl_compile_all()
و jl_save_system_image()
.