Initialization of the Julia runtime
julia -e 'println("Hello World!")'
の実行方法は次の通りです。
main()
実行は main()
in cli/loader_exe.c
で開始され、cli/loader_lib.c
で jl_load_repl()
を呼び出し、いくつかのライブラリをロードし、最終的に 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()
を呼び出します(2回目は何もしません)。
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()
は、組み込みの jl_value_t
値のための 8 ビットシリアル化タグを初期化します。
もし sysimg
ファイル (!jl_options.image_file
) が存在しない場合、Core
および Main
モジュールが作成され、boot.jl
が評価されます:
jl_core_module = jl_new_module(jl_symbol("Core"))
は、Juliaの Core
モジュールを作成します。
jl_init_intrinsic_functions()
は、新しいJuliaモジュール Intrinsics
を作成し、定数 jl_intrinsic_type
シンボルを含みます。これらは、各 intrinsic function に対する整数コードを定義します。 emit_intrinsic()
は、コード生成中にこれらのシンボルをLLVM命令に変換します。
jl_init_primitives()
は、C関数をJulia関数シンボルにフックします。例えば、シンボル Core.:(===)()
は、add_builtin_func("===", jl_f_is)
を呼び出すことによってC関数ポインタ 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
のエイリアスであるため、上記の jl_new_main_module()
によって設定された current_module
は上書きされます。
jl_load("boot.jl", sizeof("boot.jl"))
は jl_parse_eval_all
を繰り返し呼び出し、 jl_toplevel_eval_flex()
を実行して boot.jl
を実行します。 <!– TODO – evalを詳しく調べる? –>
jl_get_builtin_hooks()
は、boot.jl
で定義された Julia グローバルに対するグローバル C ポインタを初期化します。
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_core_module
に設定される前のように jl_root_task->current_module = jl_main_module
に戻ります。
プラットフォーム固有のシグナルハンドラが SIGSEGV
(OSX、Linux) および SIGFPE
(Windows) のために初期化されます。
他のシグナル(SIGINFO, SIGBUS, SIGILL, SIGTERM, SIGABRT, SIGQUIT, SIGSYS
および SIGPIPE
)は sigdie_handler()
に接続されており、バックトレースを出力します。
jl_init_restored_module()
は、各デシリアライズされたモジュールに対して __init__()
関数を実行するために jl_module_run_initializer()
を呼び出します。
最後に 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)
を使用して Main
のモジュールコンテキストで解析された式 ex
を実行します。
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()
を呼び出して JL_STDOUT
に "Hello World!" を書き込みます。 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 |
私たちの例には関数呼び出しが1つだけあり、それが「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()
を参照してください。