以前の記事
修正対象
修正が必要になる関数は以下の表のとおりです。
ファイル | 関数 |
---|---|
decl.c | G__define_var |
ifunc.c | G__interpret_func |
pcode.c | G__LD_p0_struct |
pcode.c | G__get_LD_p0_p2f |
pcode.c | G__get_LD_p1_p2f |
pcode.c | G__get_LD_pn_p2f |
pcode.c | G__get_ST_p0_p2f |
pcode.c | G__get_ST_p1_p2f |
pcode.c | G__get_ST_pn_p2f |
var.c | G__class_2nd_decl |
var.c | G__letvariable |
var.c | G__getvariable |
bc_exec_asm.h | G__exec_asm |
decl.c
G__define_var
関数
ユーザー定義型の処理を行っているスコープの先頭部分に、初期化済みを表すフラグ変数を追加します。
/**************************************************************
* declaration of struct object, no pointer, no reference type
**************************************************************/
if(var_type=='u'&&
#ifndef G__OLDIMPLEMENTATION871
(G__def_struct_member==0||-1==G__def_tagnum||
'n'==G__struct.type[G__def_tagnum])
#else
G__def_struct_member==0
#endif
&&new_name[0]!='*'&&
G__reftype==G__PARANORMAL) {
#ifndef G__NCPRO_OLDIMPLEMENTATION39
struct G__var_array *var;
char name[G__MAXNAME];
char *p;
int hash,ig15;
#ifndef G__NCPRO_OLDIMPLEMENTATION40
int initflag=0;
#endif
strcpy(name,new_name);
p=strchr(name,'[');
if(p) *p='\0';
#ifndef G__NCPRO_OLDIMPLEMENTATION40
G__hash(name,hash,i)
var = G__getvarentry(name,hash,&ig15,&G__global,G__p_local);
if(var && '@'!=var->type[ig15]) initflag=1;
#endif
#endif
以下はtype
を元に戻している部分です。コンパイル済みライブラリに含まれている型だった場合は、ここで処理を行います。インタプリタで処理される型だった場合は、G__letvariable
関数の中で処理が行われます。
/************************************************************
* memory allocation and table entry generation
************************************************************/
store_struct_offset = G__store_struct_offset;
if(G__CPPLINK!=G__struct.iscpplink[tagnum]) {
/* allocate memory area for constructed object by interpreter */
G__var_type = var_type;
#ifndef G__OLDIMPLEMENTATION1349
G__decl_obj=1;
#endif
G__store_struct_offset=G__int(G__letvariable(new_name,reg,&G__global
,G__p_local));
#ifndef G__OLDIMPLEMENTATION1349
G__decl_obj=0;
#endif
#ifndef G__OLDIMPLEMENTATION1073
if(0==G__store_struct_offset &&
G__asm_wholefunction && G__asm_noverflow) {
G__store_struct_offset = G__PVOID;
}
#endif
}
else {
/* precompiled class,
* memory will be allocated by new in constructor function below */
G__store_struct_offset = G__PVOID;
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if(var && '@'==var->type[ig15]) var->type[ig15] = 'u';
#endif
}
バイトコードへのコンパイル時に、実行されなかったブロック内で定義された変数のtype
を差し替える処理を行います。ユーザー定義型の処理を行っているスコープの最後の部分に追加します。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if(!initflag && G__asm_loopcompile && G__no_exec_compile) {
G__hash(name,hash,i)
var = G__getvarentry(name,hash,&ig15,&G__global,G__p_local);
if(var) var->type[ig15] = '@';
}
#endif
} /* of if(var_type=='u'&&G__def_struct_member.... */
ifunc.c
G__interpret_func
関数
バイトコードインタプリタでの実行中に未初期化の変数に対してデストラクタが呼び出された場合は、thisポインタの値を一時的に0に変更することで呼び出しをスキップしています。
#ifdef G__ASM_IFUNC
if(G__asm_exec) {
ifn = G__asm_index;
#ifndef G__OLDIMPLEMENTATION864
#ifndef G__NCPRO_OLDIMPLEMENTATION40
store_struct_offset = G__store_struct_offset;
if('@'==result7->type) G__store_struct_offset = 0;
#endif
/* delete 0 ~destructor ignored */
if(0==G__store_struct_offset && -1!=p_ifunc->tagnum &&
0==p_ifunc->staticalloc[ifn] && '~'==p_ifunc->funcname[ifn][0]) {
#ifndef G__NCPRO_OLDIMPLEMENTATION40
G__store_struct_offset = store_struct_offset;
#endif
return(1);
}
#ifndef G__NCPRO_OLDIMPLEMENTATION40
G__store_struct_offset = store_struct_offset;
#endif
#endif
goto asm_ifunc_start;
}
#endif
pcode.c
G__LD_p0_struct
関数
バイトコードインタプリタでの実行中に未初期化の変数に対して再宣言が行われる場合は、変数管理テーブル内のtype
を元に戻した後で、スタックに積まれているG__value
型のデータのtype
を差し替えます。
スタック上のG__value
型のデータは、G__interpret_func
関数に引数result7
として渡されます。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if('@'==var->type[ig15]) {
var->type[ig15] = 'u';
buf->type = '@';
}
#endif
G__get_LD_p?_p2f
関数とG__get_ST_p?_p2f
関数
これらの関数によって、バイトコードのロード命令とストア命令の処理で呼び出される関数が決定されます。
以下はG__get_LD_p0_p2f
関数の修正内容で、未初期化の変数に対してG__LD_p0_struct
関数を呼び出すように設定しています。他の5つの関数も修正のパターンは同じです。
if(isupper(type)) {
if('Z'==type) done=0;
#ifndef G__OLDIMMPLEMENTATION1341
else if('P'==type || 'O'==type) *pinst = (long)G__LD_p0_double;
#endif
else *pinst = (long)G__LD_p0_pointer;
}
#ifndef G__NCPRO_OLDIMPLEMENTATION40
else if('@'==type) {
*pinst = (long)G__LD_p0_struct;
}
#endif
else {
var.c
G__class_2nd_decl
関数
バイトコードの出力モードを保存するための変数の宣言を、G__class_2nd_decl
関数の先頭部分に追加します。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
int store_no_exec_compile;
#endif
バイトコードの出力中に未初期化の変数に対して再宣言が行われる場合は、デストラクタの呼び出し前にtype
を元に戻した上で、ブロック内を実行せずにバイトコードの出力を行うモードに変更します。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
store_no_exec_compile=G__no_exec_compile;
if('@'==var->type[ig15]) {
var->type[ig15]='u';
G__no_exec_compile=1;
}
#endif
処理後に、バイトコードの出力モードを元に戻します。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
G__no_exec_compile=store_no_exec_compile;
#endif
G__letvariable
関数
初期化済みを表すフラグ変数の宣言を、G__letvariable
関数の先頭部分に追加します。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
int initflag=1;
#endif
変数が未初期化だった場合は、type
を元に戻して初期化済みフラグを解除します。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if('@'==var->type[ig15]) {
var->type[ig15]='u';
initflag=0;
}
#endif
G__class_2nd_decl
関数の呼び出しによる変数の再宣言が行われなかった場合は、G__letvariable
関数を終了する前に再びtype
を差し替えます。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if(!initflag) var->type[ig15]='@';
#endif
return(result);
G__getvariable
関数
初期化済みを表すフラグ変数の宣言を、G__getvariable
関数の先頭部分に追加します。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
int initflag=1;
#endif
バイトコードインタプリタでの実行中に未初期化の変数に対して再宣言が行われる場合は、変数管理テーブル内のtype
を元に戻します。
#ifdef G__ASM
/************************************************************
* If G__asm_exec is set by 'for(', 'while(' or 'do{}while('
* loop in G__exec_statements(), execute this part and skip
* parsing string.
************************************************************/
if(G__asm_exec) {
ig15=G__asm_index;
paran = G__asm_param->paran;
for(ig35=0;ig35<paran;ig35++) {
para[ig35] = G__asm_param->para[ig35];
}
para[paran]=G__null;
var=varglobal;
if(varlocal==NULL)
G__struct_offset =0;
else
G__struct_offset=G__store_struct_offset;
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if('@'==var->type[ig15]) {
var->type[ig15]='u';
initflag=0;
}
#endif
goto G__exec_asm_getvar;
}
#endif
各種ロード命令を処理するswitch文の中で、G__getvariable
関数の戻り値として使用されるG__value
型の変数result
のtype
を差し替えます。
戻り値はスタックに積まれた後で、G__interpret_func
関数に引数result7
として渡されます。
case 'u': /* struct, union */
G__GET_STRUCTVAR;
if(ig25<paran) {
G__tryindexopr(&result,para,paran,ig25);
}
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if(!initflag) result.type='@';
#endif
break;
実行されなかったブロックのバイトコードへのコンパイル時に呼び出された場合は、type
を元に戻して初期化済みフラグを解除します。
#ifndef G__OLDIMPLEMENTATION776
if(G__no_exec_compile &&
(G__CONSTVAR!=var->constvar[ig15] || isupper(var->type[ig15]) ||
G__PARAREFERENCE==var->reftype[ig15] ||
var->varlabel[ig15][1] || /* 2011 ??? */
var->p[ig15]<0x1000) &&
'p'!=tolower(var->type[ig15])) {
#else
if(G__no_exec_compile) {
#endif
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if('@'==var->type[ig15]) {
var->type[ig15]='u';
initflag=0;
}
#endif
その後、G__getvariable
関数を終了する前に再びtype
を差し替えます。
#ifndef G__NCPRO_OLDIMPLEMENTATION40
if(!initflag) var->type[ig15]='@';
#endif
return(result);
}
bc_exec_asm.h
G__exec_asm
関数
バイトコードインタプリタの本体です。エラー発生時に変数管理テーブル内のtype
を差し替える処理は、この関数に追加する必要があります。
変数管理テーブルへのポインタとインデックスを保存するための変数の宣言を、G__exec_asm
関数の先頭部分に追加します。
#ifndef G__NCPRO_OLDIMPLEMENTATION41
struct G__var_array *store_var=NULL,*store_varB=NULL;
int store_index=0,store_indexB=0;
int store_tagnumB=0;
#endif
G__LDST_VAR_P
命令とG__LD_VAR
命令の実行時に、命令バッファから変数管理テーブルへのポインタとインデックスを取り出して保存しておきます。
#ifndef G__NCPRO_OLDIMPLEMENTATION41
store_var=(struct G__var_array*)G__asm_inst[pc+4];
store_index=G__asm_inst[pc+1];
#endif
G__LD_IFUNC
命令の実行時に、デストラクタの呼び出しが完了したことによって変数が未初期化状態に戻った場合は、変数管理テーブルへのポインタとインデックスを差し替え処理用の変数にコピーしておきます。
また、コンストラクタの呼び出しが完了したことによって差し替え処理が不要になった場合は、ポインタをNULLに初期化しておきます。
#ifndef G__NCPRO_OLDIMPLEMENTATION41
if(store_var && '~'==funcnamebuf[0]) {
store_varB=store_var;
store_indexB=store_index;
store_tagnumB=store_var->p_tagtable[store_index];
store_var=NULL;
}
else if(store_varB && 0==strcmp(funcnamebuf,G__struct.name[store_tagnumB])) {
store_varB=NULL;
}
#endif
最後に、G__LD_FUNC
命令とG__LD_IFUNC
命令のエラー処理を行うif文の中に、type
の差し替え処理を追加します。
if(G__return!=G__RETURN_NON) {
#ifndef G__NCPRO_OLDIMPLEMENTATION41
if(store_varB) {
store_varB->type[store_indexB] = '@';
store_varB->p_tagtable[store_indexB] = -1;
store_varB->p_typetable[store_indexB] = -1;
}
#endif
G__asm_exec=0;
return(1);
}
以上で、バイトコードインタプリタでの実行中に変数の再宣言が行われた場合に発生していた、未初期化の変数に対してデストラクタが不正に呼び出されてしまう問題の修正は終わりです。
私がCINTのソースコードに行った他の修正の中で規模の大きい物については、今後もQiitaに解説記事を投稿する予定です。比較的簡単な修正については、Twitterで解説したいと思います。