Initialization of the Julia runtime

julia -e 'println("Hello World!")' の実行方法は次の通りです。

main()

実行は main() in cli/loader_exe.c で開始され、cli/loader_lib.cjl_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.cmain() によって呼び出され、_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_taskjl_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() iteratesjl_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) を呼び出します。

sysimg

もしsysimgファイルが存在する場合、それはCoreおよびMainモジュールの事前に調理されたイメージ(およびboot.jlによって作成されたその他のもの)を含んでいます。Building the Julia system imageを参照してください。

jl_restore_system_image() は、保存された sysimg を現在の Julia ランタイム環境にデシリアライズし、以下の jl_init_box_caches() の後に初期化が続行されます...

注意: jl_restore_system_image() (and staticdata.c in general)Legacy ios.c library を使用しています。

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._startBase.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 frameSource codeNotes
jl_uv_write()jl_uv.ccalled though ccall
julia_write_282942stream.jlfunction write!(s::IO, a::Array{T}) where T
julia_print_284639ascii.jlprint(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.cBase.print(Base.TTY, String)
jl_apply()julia.h
jl_trampoline()builtins.c
jl_apply()julia.h
jl_apply_generic()gf.cBase.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.cBase.println(Base.TTY, String, String...)
jl_apply()julia.h
jl_trampoline()builtins.c
jl_apply()julia.h
jl_apply_generic()gf.cBase.println(String,)
jl_apply()julia.h
do_call()interpreter.c
eval_body()interpreter.c
jl_interpret_toplevel_thunkinterpreter.c
jl_toplevel_eval_flextoplevel.c
jl_toplevel_eval_intoplevel.c
Core.evalboot.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()を参照してください。