>トップ
konoha のソースコードを読んでみてのメモです。内容の正確さは保証しません
コメント等ありましたらプロファイルにあるメイルアドレスのほうまでお願いします
include/konoha/konoha_t.h の中で定義
853 #define KONOHA_MAGIC 314159 854 855 typedef struct konoha_t { 856 knh_uintptr_t magic; 857 Ctx *ctx; 858 } konoha_t ;
konoha_t を引数にとる関数はいくつかある
Linux LKM 用のソース( obsolete/lkm.c )の中で struct konohadev_t のメンバになっている
31 struct konohadev_t { 32 dev_t id; 33 struct cdev cdev; 34 konoha_t konoha; 35 char* buffer; 36 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) 37 struct semaphore sem; 38 #endif 39 };
通常のインタプリタでは、main( src/konoha.c )が konoha_open(4096) ( src/main/runtime.c )を呼んで、konoha_t が作られる( konoha_t を返す関数は konoha_open のみ)。konoha_open は konoha_t を構造体の値返しで返す。main 内では konoha という変数名で、値渡しで、呼び出す関数に渡している
konoha_t を引数にとる関数は全体から見るとわずかで、その中身の Ctx * を引数にとる関数が多い
konoha_t を引数にとる関数は konoha_* 、Ctx * を引数にとる関数は knh_* という命名規則があるようだ
src/konoha.c
34 int main(int argc, char **argv) 35 { 36 #if defined(K_USING_BTRON) 37 char **args = knh_tcstoeucs(argc, argv); 38 #else 39 char** args = (char**) argv; 40 #endif 41 konoha_t konoha = konoha_open(4096);
src/main/runtime.c
576 KNHAPI(konoha_t) konoha_open(size_t stacksize) 577 { 578 konoha_t k = {KONOHA_MAGIC}; 579 konoha_init(); 580 if(stacksize < K_STACKSIZE) stacksize = K_STACKSIZE; 581 { 582 Ctx *ctx = new_RootContext(); 583 k.ctx = ctx; 584 } 585 return k; 586 }
konoha_open の引数はスタックサイズとなっているが、参照しているバージョンでは使われていない
Ctx についてはここでは深追いしない
KONOHA_MAGIC はなんのためにあるのか
include/konoha/konoha_t.h に以下のようなマクロがある
862 #define KONOHA_CHECK_(konoha) \ 863 if(konoha.magic != KONOHA_MAGIC) { \ 864 KNH_SYSLOG(NULL, LOG_ERR, "this is not a Konoha Scripting Engine"); \ 865 return; \ 866 }\ 867 868 #define KONOHA_CHECK(konoha, value) \ 869 if(konoha.magic != KONOHA_MAGIC) { \ 870 KNH_SYSLOG(NULL, LOG_ERR, "this is not a Konoha Scripting Engine"); \ 871 return value; \ 872 }\
konoha_t を引数にとって、マジックが一致するかを見て、一致しなければ関数を終了させるマクロである
konoha_close( src/main/runtime.c )、konoha_load, konoha_eval(コメントアウトされている)( src/langc/script.c )、その他 src/main/konoha_api.c にあるいくつかの、konoha_t を引数にとる関数の先頭で、引数として渡された konoha_t のチェックに利用している
src/konoha.c にある main の頭から追ってみる
36 #if defined(K_USING_BTRON) 37 char **args = knh_tcstoeucs(argc, argv); 38 #else 39 char** args = (char**) argv; 40 #endif
BTRON (T-Shell) だったら引数をTRONコードからEUCに変換している。knh_tcstoeucs は T-Shell の? tstring.h にある tcstoeucs のラッパ。obsolete/tb.c にある
41 konoha_t konoha = konoha_open(4096);
konoha_open を読んでいく
src/main/runtime.c
576 KNHAPI(konoha_t) konoha_open(size_t stacksize) 577 { 578 konoha_t k = {KONOHA_MAGIC}; 579 konoha_init(); 580 if(stacksize < K_STACKSIZE) stacksize = K_STACKSIZE; 581 { 582 Ctx *ctx = new_RootContext(); 583 k.ctx = ctx; 584 } 585 return k; 586 }
konoha_init( src/main/konoha_api.c )は konoha_open からしか呼ばれない
50 #ifdef K_USING_THREAD 51 static volatile knh_thread_key_t ctxkey;
59 void konoha_init(void) 60 { 61 static int isInit = 0; 62 #ifdef K_USING_THREAD 63 if(isInit == 0) { 64 knh_thread_key_create((knh_thread_key_t*)&ctxkey); 65 } 66 #endif 67 knh_srand(0); 68 isInit = 1; 69 }
knh_thread_key_create は pthread_key_create のラッパ(現状。src/main/thread.c )。
ctxkey は src/main/konoha_api.c の中の knh_*Context という名前の関数から参照されている。Konoha は、スレッドを利用する場合、コンテキストへのポインタ( Ctx * )に Thread local storage 経由でアクセスするのだが、そのアクセスのためのキーを作成している。キーの初期化は 1 回でいいのでそのように書かれている。マルチスレッドで呼ばれない前提のコード
knh_srand は、ここと、src/main/apidata/numberapi.c の System_setRandomSeed 関数が呼んでいる。定義は src/main/number.c にある
61 void knh_srand(knh_uint_t seed) 62 { 63 if(seed == 0) { 64 #ifdef K_USING_POSIX 65 seed = (knh_uint_t)time(NULL) + getpid(); 66 #else 67 seed = (knh_uint_t)time(NULL); 68 #endif 69 } 70 #ifndef KONOHA_ON_LKM 71 #ifdef K_USING_INT32 72 init_genrand((unsigned long)seed); 73 #else 74 init_genrand64((unsigned long long int)seed); 75 #endif 76 #endif /* KONOHA_ON_LKM */ 77 }
seed が 0 なら time と getpid を元に seed を設定して、init_genrand を呼んでいる。init_genrand(64)? はこのモジュール内にはなく、src/ext/mt19937(ar|-64).c の中にある
konoha_open に戻って
580 if(stacksize < K_STACKSIZE) stacksize = K_STACKSIZE;
設定されたスタックが小さい( include/konoha/konoha_config.h で #define K_STACKSIZE 1024 と設定されている)ようなら大きくしている。が、結局使われていない
582 Ctx *ctx = new_RootContext();
new_RootContext を見る。これは src/main/runtime.c のローカル関数で、konoha_open からしか呼ばれていない
304 static Ctx* new_RootContext(void) 305 { 306 knh_Context_t *ctx = (knh_Context_t*)new_hContext(NULL);
new_hContext もローカル関数で、new_RootContext からしか呼ばれていない。しかし、コードを見ると、id=0 のコンテキストを引数に取って呼ぶような場合もあるように書かれている
67 volatile static size_t ctxid_counter = 1; 68 69 static knh_Context_t* new_hContext(Ctx *ctx0) 70 { 71 knh_Context_t *ctx; 72 if(ctx0 == NULL) { 73 ctx = (knh_Context_t*)malloc(sizeof(knh_Context_t)); 74 ctx0 = ctx; 75 ctx->ctxid = 0; 76 } 77 else { 78 KNH_ASSERT_CTX0(ctx0); 79 ctx = (knh_Context_t*)KNH_MALLOC(ctx0, sizeof(knh_Context_t)); 80 ctx->ctxid = ctxid_counter; 81 ctxid_counter++; 82 } 83 knh_bzero(ctx, sizeof(knh_Context_t)); 84 ctx->h.magic = K_OBJECT_MAGIC; 85 #ifdef KNH_HOBJECT_REFC 86 ctx->h.refc = K_RCGC_INIT; 87 #endif 88 ctx->h.flag = FLAG_CTX(FLAG_Context); 89 ctx->h.bcid = CLASS_Context; 90 ctx->h.cid = CLASS_Context; 91 ctx->h.ctxid = 0; 92 ctx->h.lock = LOCK_NOP; 93 ctx->unusedObject = NULL; 94 ctx->unusedObjectSize = 0; 95 ctx->parent = ctx0; 96 ctx->fsweep = knh_getDefaultSweepFunc(); 97 ctx->unusedContext = NULL; 98 return ctx; 99 }
ここで main に戻ってみると、konoha_open のあとは konoha_parseopt → konoha_shell か konoha_load konoha_runMain → konoha_close となっている
コンテキストについて読むのは次の節として、konoha_close( src/main/runtime.c )をざっと見る
597 KNHAPI(void) konoha_close(konoha_t konoha) 598 { 599 knh_Context_t *ctx; 600 KONOHA_CHECK_(konoha); 601 ctx = (knh_Context_t*)konoha.ctx; 602 if(ctx->share->threadCounter > 1) { 603 fprintf(stderr, "Many threads are still running... Found %d threads", (int)ctx->share->threadCounter); 604 return; 605 } 606 knh_Context_traverse(konoha.ctx, ctx, ctx->fsweep); 607 }
引数チェック(マジックナンバーの確認)をして、threadCounter が 1 より大きいようならメッセージを表示してそのまま戻る...が、この版を grep した限りでは threadCounter は 1 に初期化するだけで他からアクセスしてない。konoha 5 のコードでは src/main/systemtable.c の new_ThreadContext と knh_ThreadContext_dispose でインクリメント・デクリメントしている
knh_Context_traverse は、Konoha の GC 処理のルーチンで、マークフェーズ・スイープフェーズの両方でコンテキストのオブジェクトツリーを辿るのに使うルーチンである。ここでは ctx->fsweep を引数に指定しているのでスイープフェイズの呼び出しと同じ処理をおこなっている
コンテキストも Konoha のオブジェクトとして扱われている。ただし、マークルーチン中に IS_Context(o) というマクロでオブジェクトがコンテキストの場合だけ特別扱いして knh_Context_traverse を呼んでいたり、IS_SWEEP(ftr) というマクロ( include/konoha/konoha_t.h )で
691 #define knh_Object_sweep (ctx)->fsweep 692 #define IS_SWEEP(f) (f == knh_Object_sweep)
マクロが現れる位置で ctx を参照して、トラバース関数が ctx->fsweep と同じかどうかをチェックしていたり(どういうことなのか追い切れていない)、コンテキストはちょっと特殊な扱いをするようになっている
konoha_close から戻ったら、return 0 で main は終わる
Konoha 処理系の状態が全部入っている(らしい)オブジェクト
定義は include/konoha/konoha_t.h にある
266 typedef const struct knh_Context_t Ctx;
const 修飾子を付けて Ctx という別名が定義されている。構造体の定義で同時に typedef して型名 knh_Context_t も定義しているが、Ctx の定義の時点では本体の定義がまだなので struct を付ける必要がある
本体の定義は以下
804 typedef struct knh_Context_t { 805 knh_hObject_t h; 806 knh_Object_t *unusedObject; 807 size_t unusedObjectSize; 808 809 /* stack */ 810 knh_sfp_t* stack; 811 knh_sfp_t* esp; 812 size_t stacksize; 813 knh_sfp_t* stacktop; 814 knh_Ftraverse fsweep; 815 816 /* cache (stacksize * 2 + 1) */ 817 size_t cachesize; 818 struct knh_Method_t **mtdCache; 819 struct knh_Method_t **fmtCache; 820 struct knh_Mapper_t **mprCache; 821 822 /* shared table */ 823 const knh_share_t *share; 824 knh_stat_t *stat; 825 826 knh_flag_t flag; 827 knh_ushort_t ctxid; 828 const struct knh_Context_t *parent; 829 knh_mutex_t *ctxlock; 830 const struct knh_Context_t *unusedContext; 831 832 struct knh_System_t* sys; 833 struct knh_Script_t* script; 834 struct knh_String_t* enc; 835 struct knh_InputStream_t* in; 836 struct knh_OutputStream_t* out; 837 struct knh_OutputStream_t* err; 838 struct knh_Bytes_t* bufa; 839 struct knh_OutputStream_t* bufw; 840 struct knh_Bytes_t* bconvbuf; 841 struct knh_DictMap_t* props; 842 struct knh_Gamma_t *gma; 843 struct knh_Array_t *lines; 844 845 knh_case_t caseid; 846 // int hasError; 847 // struct knh_String_t *msgError; 848 849 } knh_Context_t ;
マクロが使用される文脈で ctx という名前でコンテキストへのポインタを参照できることを前提に書かれているマクロが多数ある
頭からみてゆく
805 knh_hObject_t h;
オブジェクトヘッダ。Konoha の全オブジェクトに共通のヘッダ
384 typedef struct knh_hObject_t { 385 knh_ushort_t magic; knh_flag_t flag; 386 knh_class_t bcid; knh_class_t cid; 387 knh_ushort_t ctxid; knh_lock_t lock; 388 //#ifdef KNH_HOBJECT_REFC 389 KNH_MT_VOLATILE knh_uintptr_t refc; 390 //#endif 391 void *meta; 392 } knh_hObject_t ;
magic はマジックナンバー。オブジェクトの識別に使う。DBG_ASSERT_ISOBJECT というマクロがあり
379 #define DBG_ASSERT_ISOBJECT(o) DBG_ASSERT((o)->h.magic == K_OBJECT_MAGIC)
ガベージコレクションなどで使われている
flag はオブジェクトがイミュータブルかどうか、などをセットするフラグ
knh_flag_t は他のフラグでも共通に使われている型( knh_ClassTable_t の cflag など)なので、コードの追跡にはあまり使えない
フラグの意味については、マクロが include/konoha/konoha_name.h の 503 行から 595 行にあり、名前でだいたいどういう意味のフラグがあるのかわかる
コンテキストの場合は FLAG_CTX(FLAG_Context) で初期化している。FLAG_CTX は
59 #ifdef KNH_DBGMODE 60 #define FLAG_CTX(f) (f|FLAG_Context_Verbose|FLAG_Context_Strict) 61 #else 62 #define FLAG_CTX(f) (f|FLAG_Context_Strict) 63 #endif
FLAG_Context は include/konoha/konoha_name.h で
367 #define CFLAG_Context ((knh_flag_t)0) 368 #define FLAG_Context FLAG_oflag(CFLAG_Context)
FLAG_oflag は include/konoha/konoha_class.h で #define FLAG_oflag(f) (f)
FLAG_Context_Verbose と FLAG_Context_Strict は include/konoha/konoha_name.h で
#define FLAG_Context_Verbose (knh_flag_t)(1<<2) と #define FLAG_Context_Strict (knh_flag_t)(1<<0) のように定義されている
knh_class_t 型の bcid と cid は、どちらもオブジェクトのクラスを示す id(実体は ClassTable の index )が入っていて、普通は bcid と cid は同じで bcid のほうを使う、ようである
ただし、オブジェクトが配列かイテレータの場合、bcid にコンテナ(配列かイテレータ)の id が、cid に要素の id が入るようである。src/main/class.c の knh_setClassName に以下のようなコードがある
122 if(t->bcid == cid) { 123 KNH_INITv(t->sname, new_T(t->cspi->name)); 124 } 125 else if(t->bcid == CLASS_Array || t->bcid == CLASS_IArray || t->bcid == CLASS_FArray) { 126 knh_cwb_t cwbbuf, *cwb = knh_cwb_open(ctx, &cwbbuf); 127 knh_write_sname(ctx, cwb->w, knh_class_p1(cid)); 128 knh_write(ctx, cwb->w, STEXT("[]")); 129 KNH_INITv(t->sname, knh_cwb_newString(ctx, cwb)); 130 } 131 else if(t->bcid == CLASS_Iterator) { 132 knh_cwb_t cwbbuf, *cwb = knh_cwb_open(ctx, &cwbbuf); 133 knh_write_sname(ctx, cwb->w, knh_class_p1(cid)); 134 knh_write(ctx, cwb->w, STEXT("..")); 135 KNH_INITv(t->sname, knh_cwb_newString(ctx, cwb)); 136 }
(総称型でもこのようなことをしている?)
コンテキストの場合は bcid == cid == CLASS_Context である。CLASS_Context は include/konoha/konoha_name.h の中で #define CLASS_Context ((knh_class_t)39)
ヘッダの ctxid は、そのオブジェクトが作られたコンテキストの id で、コンテキストオブジェクトの場合は 0 である(id == 0 のコンテキストの場合は自分自身の id、その他のコンテキストは id == 0 のコンテキストから作る
コンテキスト以外のオブジェクトの初期化は src/main/memory.c の new_hObject, new_Object_bcid, new_Object_init, new_Object_boxing にて
ヘッダの lock は LOCK_NOP への初期化以外は何もしてないようである LOCK_NOP の定義は include/konoha/konoha_t.h で #define LOCK_NOP ((knh_lock_t)0)
KNH_HOBJECT_REFC はリファレンスカウントガベージコレクションが有効になっていると 1 に #define される
include/konoha/konoha_t.h
372 #ifdef K_USING_RCGC 373 #define KNH_HOBJECT_REFC 1 374 #endif
K_USING_RCGC は configure 時に --enable-rcgc=yes にすると定義される。デフォルトでは no
(追記) だが、konoha_config.h の中に #define K_USING_RCGC 1 とあって、強制的に設定されている
refc の修飾子 KNH_MT_VOLATILE は include/konoha/konoha_t.h で
55 #ifdef K_USING_THREAD 56 #define KNH_MT_VOLATILE volatile 57 #else 58 #define KNH_MT_VOLATILE 59 #endif
のように定義されている。refc はリファレンスカウントガベージコレクションのリファレンスカウント
コンテキストでは K_RCGC_INIT に直接初期化している。他のオブジェクトではマクロを使っている
meta は src/langc/term.c knh_InputStream_parseToken で何かに使っている他は使ってないようだ
以上でヘッダ終わり。コンテキストの場合、ヘッダの初期化は src/main/runtime.c の new_hContext でおこなっている
84 ctx->h.magic = K_OBJECT_MAGIC; 85 #ifdef KNH_HOBJECT_REFC 86 ctx->h.refc = K_RCGC_INIT; 87 #endif 88 ctx->h.flag = FLAG_CTX(FLAG_Context); 89 ctx->h.bcid = CLASS_Context; 90 ctx->h.cid = CLASS_Context; 91 ctx->h.ctxid = 0; 92 ctx->h.lock = LOCK_NOP;
knh_Context_t の本体に戻って続きを見てゆくわけだが
806 knh_Object_t *unusedObject; 807 size_t unusedObjectSize;
初期化が runtime.c の new_hContext でおこなわれる
93 ctx->unusedObject = NULL; 94 ctx->unusedObjectSize = 0;
他は全て memory.c の中で操作されている
これは konoha のメモリフレームワークで以下のように使われている
マクロ KNH_CHECK_UNUSED_OBJECT
115 #define KNH_CHECK_UNUSED_OBJECT(ctx) { \ 116 if(unlikely(ctx->unusedObject == NULL)) { \ 117 KNH_ASSERT(ctx->unusedObjectSize == 0); \ 118 ((knh_Context_t*)ctx)->unusedObject = new_UnusedObject(ctx); \ 119 } \ 120 DBG_checkOPage(ctx, ctx->unusedObject TRACEDATA);\ 121 } \ 122
unusedObject が NULL なら unusedObjectSize をチェックして、new_UnusedObject で新しく unusedObject を作り? 設定している(unusedObjectSize はここで設定しなくていいのか? ← 問題ない。new_UnusedObject の中で設定している。というか ctx->unusedObject も new_UnusedObject の中で副作用で設定していて、return ctx->unusedObject されたものをまたマクロで ctx->unusedObject に設定している(冗長な代入?))
new_UnusedObject は 50 行ぐらいみっちりとコードがあるので詳しくはあとまわし
DBG_checkOPage を見る
133 static void DBG_checkOPage(Ctx *ctx, void *used TRACEARG) 134 { 135 size_t pageindex, size = ctx->share->OPageTableSize; 136 for(pageindex = 0; pageindex < size; pageindex++) { 137 void *ptr = ctx->share->OPageTable[pageindex].opage; 138 if(ptr < used && used < ptr + SIZEOF_OPage) return; 139 } 140 TRACE_SYSLOG(ctx, LOG_ERR, "not paged object %p", used); 141 }
ctx->share の(share の意味は今のところ不明)OPageTable のテーブルの中にあるかどうかをチェックしている( OPageTable は new_UnusedObject の中などで操作している)。LOG_ERR なので必ず見つかるはず、というロジックのようだ
TRACEDATA と TRACEARG というあやしいトークン(コンマがない!)は konoha_debug.h の中で次のように定義されている
11 #define TRACEARG ,char *_file, int _line, char *_func 12 #define TRACEDATA ,(char*)__FILE__, (int)__LINE__, (char*)__FUNCTION__
KNH_OBJECT_REUSE を見る
123 #define KNH_OBJECT_REUSE(ctx, used) { \ 124 DBG_checkOPage(ctx, used TRACEDATA);\ 125 used->ref = ctx->unusedObject;\ 126 ((knh_Context_t*)ctx)->unusedObject = used;\ 127 ((knh_Context_t*)ctx)->unusedObjectSize += 1;\ 128 }\ 129
OPageTable にあるかどうかをチェックして、used で指定されたオブジェクトを ctx->unusedObject につないで unusedObjectSize をインクリメント
ref は knh_Object_t 型で定義されている void * 型の要素で
394 typedef struct knh_Object_t { 395 knh_hObject_t h; 396 void *ref; 397 void *ref2_unused; 398 void *ref3_unused; 399 } knh_Object_t ;
状況によってあれこれに使われるもののようである。ここでは unusedObject がフリーリストになっていて、その next ポインタとして使われている
スタック
809 /* stack */ 810 knh_sfp_t* stack; 811 knh_sfp_t* esp; 812 size_t stacksize; 813 knh_sfp_t* stacktop; 814 knh_Ftraverse fsweep;
fsweep 以外は src/main/stack.c にほとんどの操作するコードがある
knh_sfp_t 型は include/konoha/konoha_t.h で
485 typedef struct knh_sfp_t { 486 union { 487 void *ref; 488 Object *o; 489 struct knh_Int_t *i; 490 struct knh_Float_t *f; 491 struct knh_Class_t *c; 492 struct knh_String_t *s; 493 struct knh_Bytes_t *ba; 494 struct knh_Pair_t *pair; 495 struct knh_Tuple_t *tuple; 496 struct knh_Range_t *range; 497 struct knh_Array_t *a; 498 struct knh_IArray_t *ia; 499 struct knh_FArray_t *fa; 500 struct knh_Iterator_t *it; 501 struct knh_DictMap_t *dmap; 502 struct knh_DictSet_t *dset; 503 struct knh_HashMap_t *hmap; 504 struct knh_HashSet_t *hset; 505 struct knh_Closure_t *cc; 506 struct knh_InputStream_t *in; 507 struct knh_OutputStream_t *w; 508 struct knh_Method_t *mtd; 509 struct knh_Mapper_t *mpr; 510 struct knh_Exception_t *e; 511 struct knh_ExceptionHandler_t *hdr; 512 struct knh_NameSpace_t *ns; 513 struct knh_Glue_t *glue; 514 struct knh_ObjectField_t *ox; 515 }; 516 union { 517 knh_boolean_t bvalue; 518 knh_int_t ivalue; 519 knh_uint_t uvalue; 520 knh_float_t fvalue; 521 knh_uint64_t data; 522 knh_code_t *pc; 523 knh_callinfo_t ci; 524 }; 525 } knh_sfp_t;
と定義されている。スタックの中身はこれらのどれかと決まっている?
stack.c の knh_stack_initexpand を見るとよくわからないがキャッシュの初期化などもやっている
fsweep は knh_Ftraverse 型で、
538 typedef void (*knh_Ftraverse)(Ctx *ctx, Object *);
という定義になっている
691 #define knh_Object_sweep (ctx)->fsweep
というマクロが参照しており、src/main/runtime.c と src/main/memory.c に fsweep を参照するコードがある
src/main/runtime.c では new_hContext という関数で knh_getDefaultSweepFunc() が返す関数ポインタに初期化している。knh_traverseSharedContext という関数の中に、ctx->fsweep を knh_Object_finalSweep (なにもしない関数)に書き換えているコードがある。konoha_close という関数で、knh_Context_traverse(konoha.ctx, ctx, ctx->fsweep); のように参照している
knh_getDefaultSweepFunc は src/main/memory.c の中にあり knh_Object_RCsweep を返している
src/main/memory.c のほうは knh_System_gc という関数の中で、いったん ctx->fsweep を退避して knh_Object_finalSweep を設定し、あとで元に戻す、ということをやっている
fsweep を直接参照してる箇所は以上である