各リソースは所定の挙動を有し、それは有効な状態とそれらの状態間の有効な遷移とに関して記述することができる。コンピュータプログラムに於けるエラー発生の源は、しばしばコンピュータプログラムの開発者がリソースの所定の挙動を把握していないことにある。コンピュータプログラム中のコンピュータ命令によって、リソースの所定の挙動に違反したリソースの使用がコンピュータに指示されると状態違反が発生する。状態違反の例として、例えば、ファイルの所定の挙動によれば読み出しをするにはファイルが開かれていなければならない場合において、ファイルを閉じた後のファイルからのレコードの読み出しがある。
コンピュータ100(第1図)には、中央演算装置(CPU)102、メモリ104、及び入力/出力回路(I/O回路)106が含まれており、これらは全てバス108を介して互いに接続されている。メモリ104は、磁気ディスクのような二次記憶装置、ランダムアクセスメモリ(RAM)、読み出し専用メモリ(ROM)を含む任意のタイプのメモリを含み得る。CPU102は、メモリ104からコンピュータプロセス110を読み出して実行する。コンピュータプロセス110は、ライブラリ関数112、動的に割り当てられるメモリ114、及び第2コンピュータプロセス116に対するアクセスを有する。I/O回路106は、ドライバ106A、106B、106C、106D、及び106Eを含んでおり、これらのドライバはそれぞれビデオモニタ118、二次記憶装置120、ネットワーク126、ポインティングデバイス(マウス122など)、キーボード124を駆動・制御する。
本明細書中では、リソースはコンピュータプロセスによって使用されるコンピュータシステムの一部であり、一般に、使用前に割り当てられ、使用後に開放、即ちフリーにされなければならない。リソースの例として、グローバルメモリ、ファイル、ウインドウ、メニュー、及びダイアログ(dialog)がある。コンピュータプロセス110のリソースには、例えば、動的に割り当てられるメモリ114、コンピュータプロセス116、磁気ディスク120が含まれる。
また本明細書中において、コンピュータプロセスとはコンピュータによって実行される一連の過程(step)である。コンピュータプログラムはコンピュータによって実行されうる一連の命令である。コンピュータプログラムの命令によって過程が定義され、それらがコンピュータにより実行されると、コンピュータプロセスが形成される。従って、コンピュータプロセス110の挙動をモデル化するため、コンピュータプロセス110を定義するコンピュータプログラムの解析がなされる。
関数レベルでの解析
コンピュータプログラムは、通常、既に開発されているコンポーネントと新たに開発されるコードとを組合せたものである。本明細書中において、“コード”はソースコード、即ち人間に理解可能な形式の一連のコンピュータ命令、及び/またはオブジェクトコード、即ちコンピュータに理解可能な形式の一連のコンピュータ命令を表す。コンピュータプログラムのコンポーネントとは、特定の部分プロセスを実行するように既に開発され、通常、その部分プロセスが忠実に実行されることが試験済みであるコンピュータ命令及び/またはデータ構造の集まりである。部分プロセスとは、コンピュータプロセス中の1または複数の過程であり、即ちコンピュータプロセスの一部である。コンピュータプログラムの開発者は、特定の部分プロセスを実行するためこのようなコンポーネントを使用し、通常、そのコンポーネントを信頼しており、実行時には定められた通りに機能するものと思っている。そのようなコンポーネントは、開発者が既に開発したコンポーネントまたは商業的に入手可能なコンポーネントの実行の要求、即ち呼び出し(call)を含むことができる。そのようにして、コンピュータプログラムの開発に於ける作業の重複が避けられている。
新たなコンピュータプログラムは、通常、開発済みのコンポーネントを組み合わせ、新たに書かれたコンピュータ命令を用いてこれらのコンポーネントを互いに接続することによって開発される。そのような組合せ及び相互接続の結果、他のコンポーネントまたはコンピュータプログラムによって使用可能な新たなコンポーネント、または新たなコンピュータプログラムが生成される。あるコンピュータプログラムのコンポーネントは、そのコンピュータプログラムによって定義されるコンピュータプロセスの部分プロセスを定義する。コンピュータプロセスの各部分プロセスは、そのコンピュータプロセスによって用いられるリソースの状態を変化させ得る。従って、あるコンピュータプロセスによって用いられるリソースの状態及び状態遷移を適切に解析するには、コンピュータプログラムのコンポーネントによって定義される部分プロセスの実行がリソースの状態に与える影響を確かめなければならない。例えば、第1コンポーネントによって定義される第1部分プロセスに於いて割り当てられ、第2コンポーネントによって定義される第2部分プロセスに於いて使用され、第3コンポーネントによって定義される第3部分プロセスに於いて開放されるリソースの使用を適切に解析するには、第1、第2、及び第3部分プロセスのリソースに対する影響を解析することが必要である。
コンピュータプログラムは、様々なコンピュータ言語の何れで書かれていても良い。典型的なコンピュータ言語は手続き型である。手続き型言語において、コンピュータプログラムのコンピュータ命令は編成されてコンポーネントを形成し、これらのコンポーネントは、しばしば手続き、または“関数”と呼ばれる。それらは各々実行されたとき特定の部分プロセスを実行するように設計される。手続き型言語の例として、C、Ada、Pascal、Fortran及びBasicがある。手続き型言語にはオブジェクト指向のものがあり、そのようなものとして例えばC++やSmallTalkがある。オブジェクト指向コンピュータ言語では、関数及びデータ構造が結合されてオブジェクトが形成され、それらのオブジェクトが更に編成されて“クラス(class)”として知られるコンポーネントが形成される。
コンピュータ言語にはグラフィックベースのものがあり、そのようなコンピュータ言語では、命令はコンピュータスクリーン上に表示されるグラフィックイメージとして表される。プログラマーはそれらのグラフィックイメージをリンクしてコンピュータプログラムを形成する。例えば、ワシントン州レッドモンド(Redmond,Washington)のマイクロソフト社(Microsoft Corporation)から入手可能な“Microsoft Visual Basic”は、そのようなグラフィックベースのコンピュータ言語である。コンピュータ言語には、マイクロソフト社から入手可能な“Microsoft Word”ワープロソフト用の“Microsoft Word Basic”というコンピュータ言語や、マサチューセッツ州ケンブリッジ(Cambridge,Massachusetts)のロータスディベロップメント社(Lotus Development Corporation)から入手可能な“Lotus 1−2−3”表計算ソフト用の“Lotus 1−2−3”マクロ言語のように、特定のソフトウェア製品を対象としたものもある。本発明は、任意のコンピュータ言語、即ちリソースが使用される任意のコンピュータ命令プロトコルに適用可能である。ソースコードのコンピュータ命令プロトコルについて上記で述べたが、本明細書中に開示することは、オブジェクトコード形式のコンピュータ命令にも同じように適用可能であることを理解されたい。以下に示す例示的な実施形態では、解析されるコンピュータ言語はCスタンダードに説明されている良く知られたC言語である。
C言語で書かれたコンピュータプログラムは、通常、いくつかの関数に分割される。関数は、実行されると、入力として0または1以上のパラメータを受け取り、出力として1つの戻り値項目(returned item)を生成することもあるが、戻り値項目を生成しないこともある。これらのパラメータ及び戻り値項目はデータ構造であり、その関数によってアクセス可能なデータを含み、例えばメモリ104のようなメモリに格納される。C言語で定義された関数の例を、後にコンピュータコードの抜粋(1)に示す。
本明細書中で述べる例示的な実施形態では、コンピュータプログラム内の各関数は個々に解析される。関数は、その関数のコンピュータ命令によって影響されるその関数のリソース、エスクターナル及び項目の使用及び変化をモデル化することによって解析される。関数の項目(item)は、その関数によってアクセス可能なメモリ(例えばメモリ104)中の位置を表す。項目は型(type)と値(value)とを有する。本発明の一実施例においてサポートされる項目の型には、整数、浮動小数点、及びポインタ(pointer)データが含まれる。項目の値とは、その項目によって表されるメモリ内の位置に格納された特定のデータによって表される値である。エクスターナル及びリソースは、関数の各項目に関連付けられ得る。項目については後により詳細に説明する。変数(variable)が、識別子(identifier)と1または複数の項目との間を関連付ける。
関数のエクスターナルは、その関数の文脈の外、即ち、その関数の実行の始まる前またはその関数の実行が終了した後に存在するコンピュータプログラムの一部を表す。関数のエクスターナルの例には、関数のパラメータ及び戻り値項目、グローバル定義変数、及びスタティック変数が含まれる。用語(i)“グローバル定義変数”及び(ii)“スタティック変数”は、本明細書中では、それぞれ(i)リンケージが“外部的(extern)”な変数、及び(ii)リンケージが“内部的(intern)”で格納期間が“静的”な変数を表すのに使用される。“ローカル定義変数”はリンケージが“内部的”で格納期間が“自動的(automatic)”な変数である。リンケージ(linkage)については、Cスタンダードのセクション6.1.2.2に説明されており、格納期間(storage duration)についてはCスタンダードのセクション6.1.2.4に説明されている。簡単に説明すれば、グローバル定義変数は、コンピュータプロセスの全部分プロセスに対し定義され、スタティック変数は幾つかの部分プロセスに対し定義されるが、コンピュータプロセスの全部分プロセスに対して定義されるとは限らない。
各部分プロセスは幾つかのリソースを使用する。例えば、プロセス110(第1図)の関数202(第2図)は、動的に割り当てられるメモリ114、及びコンピュータプロセス116を使用する。また関数202(第2図)は、(i)関数202A及び202B及び他の関数によってもアクセス可能なグローバル定義メモリ204と、(ii)ローカルメモリと、(iii)パラメータ208A〜208Cと、(iv)戻り値項目210も使用する。関数202は、これらのリソースの1または複数をモデル化することによって解析される。
各リソース及びエクスターナルは状態(state)を有する。関数の各コンピュータ命令の実行は、コンピュータ命令の実際の実行によって生じるであろうその関数のリソースまたはエクスターナルの状態の変化をモデル化することによってエミュレートされる。エクスターナルまたはリソースの状態が変化する場合、その状態変化はそれぞれ対応するエクスターナル挙動モデルまたはリソース挙動モデルと比較され、その状態変化がエクスターナルまたはリソースの適切な使用を反映しているかどうかが判定される。状態変化が不適切である場合は、状態違反が発生し、エラーが通知される。エラーのユーザへの通知は、(i)エラーメッセージをビデオモニタ118(第1図)または同様の出力装置に表示することによって、(ii)エラーメッセージをメモリ104または二次記憶装置120のエラーログファイルに記録することによって、または(iii)エラーメッセージの表示とエラーメッセージの記録を両方とも行うことによって可能である。
挙動モデル
関数モデルは、関数をその関数が割り当てる新たなリソース及びその関数のエクスターナルにその関数が施す演算に関して、抽象して表したものである。
上述したように、リソースは状態を有する。リソースの有効な状態及び有効な状態間遷移は、リソース挙動モデルによって表される。リソースの挙動のモデル化は、そのリソースの実際の挙動よりも大幅に簡略化され得る。例えば、リソースの状態は、状態図300(第3A図)によって表されるリソース挙動モデルに基づいてモデル化される。状態図300に基づくと、リソースは次の状態の何れかを取り得る。
状態UとXは似ているが別のものである。未割り当てのリソースに関連付けられた項目は不定値を有し、無効なリソースに関連付けられた項目は既知の無効値(invalid value)を有する。リソース挙動モデルは挙動がモデル化されるリソースの実際の挙動と同程度に複雑になり得る。しかしながら、状態図300に表されたような大幅に簡略されたリソース挙動モデルでも、そのようなリソースの使用に於いて発生し得る全エラーのほとんどを検出する上で有効である。
リソースは最初は割り当てられていないため、状態Uにある。各コンピュータ命令のエミュレートされた実行(それらを実際に実行するとリソースの状態変化を引き起こす)によって、リソースに演算(operation)が施される。リソースに演算を施すことによって、リソースの状態は状態図300に従って変化する。
以下にリソースに施すことのできる演算を示す。
このように、状態図300に従うと、未割り当てのリソース、即ち状態Uにあるリソースに対し、ある関数内の命令によって適確な割り当てがなされる場合、即ち演算aが施される場合、そのリソースは状態A、即ち割り当てられた状態となる。しかしながら、未割り当てのリソース、即ち状態Uにあるリソースが計算に於いて使用される(従って演算cを施される)と、そのリソースは状態Eとなる。状態Eは、プログラミングエラーの結果、状態違反が起こったことを示す。状態Eはリソースの予め定められた挙動を表すものではなくオプション的なものであるが、開示する実施例に於いては、状態違反を表す便利な方法として使用される。別の実施例では状態Eは省略され、状態違反は上記の例に於いてリソースが状態Uにあるとき、演算cが定義されないことに注目することによって検出される。
状態図300(第3A図)は表Cのようにまとめられる。
状態図300中の演算識別子に対応している上付の数字、及び表Cに於いて新たな状態識別子に付されている上付の数字は、特定のエラーを示している。これらのエラーは、次の表Dに示す通りである。
上述の例では、演算cを状態Uにあるリソースに施すことによって、状態図300に於いて状態Uから状態Eへと向かう“c2”によって識別される矢印によって示されているように、リソースは状態Eに置かれる。このように、この例に於けるエラーは表Dに示したエラー番号2、即ち未割り当てのリソースの使用である。
各関数モデルは、対応する関数の各エクスターナルにどの演算が施されるかを規定する。例えば、Cスタンダードのセクション7.9.5.3に説明されているC言語用に定義された関数fopen () は、2つのパラメータを定め、そのうち第1のパラメータは入力として受け取られ、開かれるべきファイルを特定する。また、開かれたファイルに対応するファイルポインタである戻り値項目を定める。ファイルポインタ、即ち、型“FILE”の項目を指定するポインタは、Cスタンダードのセクション7.9.1に説明されておりよく知られている。ファイルポインタは関数fopen () のエクスターナルであり、このパラメータによって特定されるファイルは、このエクスターナルに関連付けられるリソースである。関数fopen () に対する関数モデルは、初期状態が状態Qの新たなリソースが生成されることを定める。このリソースの初期状態は状態Aではなく状態Qであるが、それは関数fopen () はファイルが正常に開かれることを保証するものではないからである。
Cスタンダードのセクション7.9.5.1に説明されているC言語用に定義された関数fclose () は、ファイルポインタであるパラメータを定める。
関数fclose () を実行することによって、そのパラメータが指定するファイル記述子(file descriptor)を有するファイルが閉じられる。関数fclose () に対する関数モデルは、関連するファイルが閉じられること、従って開放されることが反映されるように、そのパラメータに演算kが施されることを定める。同様に、ファイルに対する読み出し及び書き込み演算を定義するC言語の関数に対する関数モデルは、ファイルが使用されたことが反映されるように、そのファイルを表すリソースに演算cが施されることを規定する。
あるリソースに対応する項目(例えば関数fopen () の戻り値項目であるファイルポインタなど)が、決定命令(decision instruction)中で述語(predicate)として使用される場合、演算pがそのリソースに施され、リソースの状態は状態図300に基づいて変化される。項目が関係式(例えば演算子の>、<、<=、>=、及び!=の何れかを含む演算)またはブール式(例えば演算子&&、||、及び!の何れかを含む演算)中にオペランドとして現れる場合、または項目が“switch”ステートメントに於いて制御式(control expression)として用いられる場合、項目は述語中で用いられることとなる。“switch”ステートメントはC言語用に定義されており、制御式の値に応じて関数の流れを制御する。“switch”ステートメントについては、Cスタンダードのセクション6.6.4.2に詳細に説明されている。
あるリソースに対応する項目が計算中で用いられる場合、演算cがリソースに施され、それにより状態図300に基づいてそのリソースの状態が変わる。項目が計算中で使用されるのは、(i)数学的演算(例えば+、/、*、または−)に対するオペランドとして現れる場合、または(ii)リソースがポインタのデリファレンス(dereference)または配列へのアクセスとして現れる場合、または(iii)リソースが配列指標として現れる場合である。
ポインタ及び配列については良く知られており、Cスタンダードにも記載されているが、念のためにポインタ及び配列について以下に簡単に説明する。C言語の文脈に於いて、ポインタはその値が他の項目のメモリ中のアドレスであるような項目である。従って、ポインタは他の項目を“指定(point)”する。ポインタをデリファレンスすることは、ポインタが指定する項目を検索することである。
開示される本発明の実施例を具現するべく用いられる以下により詳細に説明されるデータ構造は、他のデータ構造を指定するポインタを含むものとして説明される。データ構造を一意に特定するためのポインタ以外の機構も知られているが、これらの機構は本発明の原理を逸脱することなくポインタの代わりとなり得るものであることを理解されたい。
配列は、同様の構造の1または複数の項目の集合である。配列の項目はエレメントと呼ばれ、順番に番号付けされる。配列へのアクセスは、エレメントの番号、即ちエレメントの指標を参照することによる配列のエレメントへのアクセスである。
演算xは、NULLであるとされる項目に対応するリソースに施される。NULLは一般に無効値であり、ある項目が有効な値を有していないことを示すためにその項目に代入される。例えば、値がNULLであるポインタは項目の指定をしない。C言語の文脈では、NULLは“false(偽)”を表すブール値でもある。ある項目がNULLと比較されてその比較の結果がtrue(真)であるとされる場合、その項目はNULLである、即ちNULLの値を有するとされる。後により詳細に説明するように、関数を解析するには、実行時の関数の特定の挙動に関して様々な仮定を設定することが必要である。例えば、関数fopen () は、正常にファイルを開くか、またはファイルを開くのに失敗するか何れかである。戻り値項目、即ちファイルポインタがNULLと比較されその結果がtrueであるとされる場合、即ち関数fopen () がファイルを開くのに失敗したとされる場合、そのファイルを表すリソースに演算xが施される。これについては、後により詳細に説明する。
本発明の基本的原理を説明するための例
リソースのモデル化の有用性について以下に例を用いて説明する。以下のソースコード抜粋(1)はプログラミングエラーを含んでおり、開示される本発明の実施例ではそのプログラミングエラーを検出する。このソースコード抜粋(1)は公知のC言語に適合するものであり、関数example 1 () を定義している。ライン番号はC言語の一部ではないが、以下の説明をよりわかりやすくするために付け加えたものである。
関数example 1 () を解析するとき、エクスターナルを含む各項目の状態が追跡される。変数“str”はローカルに定義された、即ち関数example 1 () の文脈内でのみ定義されたものである。変数“str”はライン10で定義されているように、型が“char”であるデータを指定するポインタである。しかし最初、変数“str”は初期化されておらず特定のデータを指定しない。従って、変数“str”はリソースに関連付けられていない。
Cスタンダードのセクション7.10.3.3に説明されているC言語用に定義された関数malloc () の実行では、割り当てられるメモリ(例えばメモリ104(第1図))に対するリクエストを受け付け、そのメモリを割り当てるかまたはその割り当てに失敗する。関数malloc () は、戻り値項目として、メモリの割り当てに成功した場合は割り当てられたメモリを指定するポインタを、そうでない場合はNULLポインタをリターンする。このように、関数malloc () は初期状態がQである新たなリソースを生成し、その新たなリソースを関数malloc () の戻り値項目に関連付ける。ライン23で関数malloc () の戻り値項目の値が代入された後において、変数“str”は、メモリが割り当てられた場合は新たに割り当てられたメモリを指定し、そうでない場合はNULLポインタである。
ソースコード抜粋(1)のライン25において、変数“str”は関数fgets () 内でパラメータとして用いられている。この関数fgets () もCスタンダードのセクション7.9.7.2に説明されているC言語用に定義された関数である。関数fgets () を実行すると、ソースコード抜粋(1)のライン25の文脈において、第1パラメータ、即ち、変数“str”がデリファレンスされる。従って、変数“str”に関連付けられたリソースに対して演算iが施される。状態図300(第3A図)及び表C、表Dに示されているように、状態Qのリソースに演算iを施すとリソースは状態Aに置かれ、割り当てられているかどうか不確実なデータがチェックされることなしに使用されたことを示すエラーメッセージが生成される。
ソースコード抜粋(1)のライン29において、変数“str”は、変数“str”が指定するメモリをフリーにする即ち開放する関数free () にパラメータとして渡されている。従って、関数“str”に関連付けられたリソースに対し演算kが施される。状態図300及び表C、表Dに示されているように、演算kを状態Aにあるリソースに施すと、リソースは状態Uに置かれる。割り当てられているリソースを開放することは適正であるため、エラーは通知されない。
以下のテキスト(2)は、ソースコード抜粋(1)の関数example 1 () を解析する、開示される本発明の実施例によって生成されるエラーメッセージを示したものである。
テキスト(2)において、“example 1.c”は、上記のソースコード抜粋(1)を含む、従って関数example 1 () を定義しているファイルを表している。関数example 1 () は、ソースコード抜粋(1)のライン23において関数malloc () の呼び出し(即ち実行要求)において、割り当てを要求されるメモリ量に対してメモリが足りないかもしれないということを考慮していない。関数example 1 () の実行時に関数malloc () が要求されたメモリの割り当てに失敗すると、関数example 1 () の実行を含むコンピュータプロセスが突然アボートし、ユーザにはプロセスの予期されない終了に対する理由も示されないことになる。しかしながら、例えば上記のテキスト(2)を用いてそのような事態が考慮されていないことを検出し通知することによって、関数example 1 () の開発者に、関数example 1 () 中の欠陥を修正するのに必要な情報が与えられ、そのような事態に適切に対処することが可能となる。
本発明の有用性は、ソースコード抜粋(1)の関数example 1 () におけるファイルポインタ“fptr”の状態の追跡について検討することによって更に明らかとなる。ファイルポインタ“fptr”は関数example 1 () のローカル定義変数である。ファイルポインタ“fptr”は、型“FILE”のデータを指定するポインタである。最初、ファイルポインタ“fptr”は初期化されておらず、どのリソースにも関連付けられていない。
ライン14において、関数fopen () の戻り値項目がファイルポインタ“fptr”に代入される。上述したように、関数fopen () は、初期状態Qの新たなリソースを生成し、その新たなリソースに関数fopen () の戻り値項目を関連付ける。ライン15の“if”ステートメントは、ファイルポインタ“fptr”が指定するファイルが正常に開かれたかどうかをファイルポインタ“fptr”と“NULL”とを比べることによって判定する。
ファイルポインタ“fptr”がNULLである場合、ファイルは正常に開かれておらず、関数example 1 () は、ユーザにファイルを正常に開くことができなかったことを通知して終了する。逆に、ファイルポインタ“fptr”がNULLでない場合は、ファイルポインタ“fptr”が指定するファイルは正常に開かれていることがわかり、関数example 1 () はライン22へと進む。ライン15においてファイルポインタ“fptr”の比較をすることによって、ファイルポインタ“fptr”に関連付けられたリソースに演算pが施される。従って、ファイルポインタ“fptr”に関連付けられたリソースの状態は状態Qから状態Aへと変化する。従って、状態図300及び表Cに示されているように、計算(演算cの適用)または述語(演算pの適用)のどちらでファイルポインタ“fptr”を使用しても、エラーメッセージは生成されない。従って、ファイルポインタ“fptr”の処理に関してエラーは検出されない。
上述したように、関数fopen () 及びmalloc () は、実行時、パラメータ及び戻り値項目のリソースに対して特定の処理を実行する。関数fopen () やmalloc () のような関数は、コンピュータプロセス110によってアクセス可能なライブラリ関数112(第1図)内に含まれる。そのような関数の呼び出しは、関数202(第2図)に含まれる。本明細書中において、関数に対する“呼び出し(call)”とは、実行時にCPU102(第1図)のようなプロセッサに次のような処理をさせるステートメントである。即ち、(i)関数に0または1以上の項目をパラメータとして与え、(ii)関数を実行し、(iii)関数の評価により生じる値を表す戻り値項目(戻り値項目が関数によって定められる場合)を生成する。第1の関数が第2の関数に対する呼び出しを含んでいる場合、第1の関数は“呼び出し関数(calling function)”と呼ばれる。また、第2の関数は“被呼び出し関数(called function)”と呼ばれる。
関数202のステートメントによって呼び出される関数の実行によって影響される関数202のリソース(第2図)を適切に解析するため、そのような被呼び出し関数の挙動を記述する関数モデルがサポートされる。一実施例では、そのような関数モデルは、例えばCスタンダードにあるこのような関数の挙動を定める公知のテキスト形式の記述から生成され、コンピュータ100のメモリ104内に格納される。これらの関数モデルはその後コンピュータプログラムの解析に先立ってメモリ104から検索され取り出される。これについては以下により詳細に説明する。
以下に、上記のソースコード抜粋(1)の関数example 1 () によって呼び出される関数の関数モデルの例を示す。これらの被呼び出関数は全てCスタンダードのライブラリの“stdio”(入力/出力)ヘッダファイルからのものである。このヘッダファイルはC言語で使用される広く知られたファイルであり、Cスタンダードのセクション7.9以下に示されている。
開示される本発明の実施例に基づく関数モデルをメモリ104(第1図)において表す関数モデル構造体については、後に詳述する。関数モデル(3)は、関数malloc () の実行が関数malloc () のエクスターナルの状態に与える効果について定めている。関数モデル(3)によると、新たなリソースが生成され、状態Qに初期化され、関数malloc () の戻り値項目に関連付けられる。また関数モデル(3)は、関数malloc () の第1パラメータ、即ち、パラメータ0(parameter O)に演算Cが施されることも規定している。
関数モデル(4)は、関数free () の実行が関数free () のエクスターナルに及ぼす効果について規定しており、演算kが引数リスト(argument list)の第1パラメータ即ちパラメータ0に施されることを示している。
関数モデル(5)は、関数fgeps () を呼び出すことによって、(i)演算iがパラメータ0、即ち第1パラメータに施され、(ii)演算cがパラメータ1、即ち第2パラメータに施され、(iii)演算iがパラメータ2、即ち第3パラメータに施されることを示している。
リソースリークの検出
リソースをモデル化しリソースと関数のエクスターナルとの関連を追跡することによって、開示するエラー検出機構は、リソースリークを検出する有用な機構を提供する。リソースは、ある関数がそのリソースを割り当てられた状態にしたまま終了するとき、即ち、その関数のどのエクスターナルによってもそのリソースがアクセスできなくなったとき、その関数によって“リーク”される。リークされると、そのリソースはリークを発生させた関数の実行が終了した後にはそのリソースを指定するポインタが保持されないため、使用することができなくなる。リソースが再使用可能な場合、例えば、動的に割り当てられるメモリ114(第1図)のような場合、関数の実行の終了前にリソースをフリーにしないと、他の関数がそのリソースを再使用することができなくなる。ある部分プロセスが動的に割り当てられるメモリを繰り返しリークさせると、遂にはその部分プロセス含むコンピュータプロセスに使用可能な全メモリが消耗される結果となり得る。
リソースリークの検出の例として、ソースコード抜粋(6)の関数example 2 () について考えてみる。
変数“str”は関数example 2 () のローカル変数であり、従って関数example 2 () 以外の関数にはアクセス可能でない。変数“str”が指定するメモリは、ソースコード抜粋(6)のライン25の“return”命令の前にフリーにされていないため、このメモリは関数example 2 () をその一部として含むコンピュータプロセス110が終了するまで使用することができず、開放または再割り当てをすることもできない。従って、このリソースはコンピュータプロセス110から“リーク”する。
関数のエクスターナルは関数の実行の終了後も存在する項目であるため、エクスターナルを通じて到達可能(reachable)な割り当てられたリソースはリークされない。特定のエクスターナルに関連付けられていないリソースも、ある場合には、エクスターナルを通じて到達可能である。例えば、項目の配列の特定のエレメントに関連付けられたリソースは、その項目の配列の別のエレメントであるエクスターナルを通じて到達可能である。これは、C言語によると、ある配列のエレメントのメモリ中の位置は、その配列の他のエレメントの位置から計算可能であるからである。
リークは、関数のトラバース(traversal)の終了時にチェックされる。リークの検出については後により詳細に説明するので、ここでは簡単に要約する。エクスターナルを通じて到達可能な全てのリソースをマーク(印し付け)する。マークされず且つ割り当てられているリソースはリークされたものとして通知する。ライン25において変数“str”はリターンされないため、変数“str”はエクスターナルではない。従って、変数“str”によって指定されるメモリは、割り当てられており且つ関数example 2 () のトラバースの終了時にマークされていない。よって、変数“str”によって示されるメモリはリークされる。
関数example 2 () の解析によって、次のエラーメッセージが生成される。
従来技術のスタティックチェッカは、リソースリークを検出することができない。従来技術のランタイムチェッカ(runtime checker)は、関数にリソースリークを発生させ得る全ての起こり得るイベントを考慮しないことがしばしばあり、一般に大きなコンピュータプログラムの文脈の外で単一の関数を解析してその単一の関数内のリソースリークを検出することはできない。対照的に、開示される本発明の実施例では、大きなコンピュータプログラムの単一の関数を解析することによって、リソースリークが効果的に検出される。以下により詳細に説明するように、開示するエラー検出機構は、関数がリソースリークを発生させるような起こり得るイベントを全て考慮する。従って、本発明は、従来技術に対して大幅に向上している。
エクスターナルの複合状態
以下に詳細に説明するように、関数の解析は、その関数の制御の流れを追い、その関数の個々のステートメントの実行をエミュレートし、エクスターナル及びリソースの状態を追跡することによってなされる。関数の制御の流れとは、その関数の特定の実行時に実行されるその関数のコンピュータ命令の特定の順番である。制御が第1コンピュータ命令から第2コンピュータ命令へと移るとき、第2コンピュータ命令は第1コンピュータ命令の実行に続いて実行される。関数の制御の流れは、しばしば本明細書中では、関数の制御流れ経路と呼ばれる。関数の制御の流れは、しばしば、コンピュータプロセスに於いてその関数によって定義される部分プロセスの実行中に発生する特定のイベントに依存する。
関数の解析においては、関数の全ての可能な制御流れ経路を考慮することが望ましい。従って、関数の制御流れ経路に影響を与えうる全てのイベントを考慮することが望ましい。従来技術のスタティックチェッカは、しばしば、制御流れ経路を全く考慮しない。ランタイムチェッカは、関数の制御流れ経路に影響を与えるイベントを操作することによって、コンピュータプロセスの実行中にそのコンピュータプロセスが各制御流れ経路をたどるようにユーザが強制し得る範囲に於いて、特定の関数の制御流れ経路を考慮するのみである。一方、開示されるエラー検出機構は、ユーザが介入することなく、自動的に関数の各制御流れ経路の解析を行う。更に、開示するエラー検出機構は、関数の解析をその関数を含むコンピュータプロセスまたはコンピュータプログラムの文脈の外で行うことができる。従って、個々の関数を、より大きな関数またはコンピュータプログラムまたはプロセス中に組み込む前にエラーに対してより完全にチェックすることができる。
例として、ソースコード抜粋(6)の関数example 2 () について考えてみる。関数example 2 () の正確な制御流れ経路は、関数example 2 () をコンピュータプロセス内で実行するまでは未知である。例えば、ライン14に於いて呼び出される関数malloc () がリクエストされた通りにメモリを割り当てるのに成功した場合、制御は、ライン16の“if”ステートメントからライン19の関数fopen () の呼び出しへと流れる。言い換えると、ライン14で呼び出されたとき関数malloc () がリクエストされた通りに正常にメモリを割り当てる場合、ライン19の関数fopen () の呼び出しは、ライン16の“if”ステートメントの実行の後に続く。逆に、メモリの割り当てが失敗した場合、制御はライン16の“if”ステートメントからライン17の“return”ステートメントへと流れる。ライン14に於いて呼び出されたとき関数malloc () によるメモリの割り当てが正常になされるかどうかは、通常、コンピュータプロセスに於いて関数example 2 () が実行されるまでわからない。
関数example 2 () の解析に於いて、関数example2 () の全ての制御流れ経路が考慮されることが望ましい。様々に仮定を変えて関数を何度もトラバースすることによって、複数の制御流れ経路が考慮される。例えば、関数example 2 () は、一度はライン14で呼び出される関数malloc () がリクエストされたメモリを正常に割り当てるという仮定の下で、一度は関数malloc () がリクエストされたメモリの割り当てに失敗するという仮定のもとでトラバースされる。
後に詳細に説明する本発明の一実施例では、一つの関数に対し繰り返しトラバースがなされ、各トラバースに於いて、ランダムに仮定が設定される。関数example 2 () の各トラバースは、関数example 2 () のエクスターナルの状態を追跡する。各エクスターナルは、関数example 2 () の複数回のトラバースの結果得られる各エクスターナルの幾つかの状態を反映する複合状態(composite state)を有する。
エクスターナルは、複合RS、CP及びDK状態を有する。これらの複合状態は、2つの目的に用いられる。即ち、(i)関数の様々な制御流れ経路を考慮したとき不整合を生じるエクスターナルの使用を検出するため、(ii)関数の実行がその関数のエクスターナルに与える影響を記述する関数モデルを構築するため、に用いられる。構築された関数モデルは、その後、モデル化された関数を呼び出す他の関数を解析するのに用いることができる。
ある特定の関数の文脈内で、各エクスターナルはCP状態、DK状態、及びRS状態を有する。エスクターナルのCP状態は、使用される前にエクスターナルがチェックされるかどうかを判定するのに用いられる。“CP”という用語は、主として関心のある演算、即ち、演算p(エクスターナルのチェックを表す)の前の演算c(エスクターナルの使用を表す)、からきている。エクスターナルのDK状態は、関数によってエスクターナルの割り当て及び/または開放がなされるかどうかを判定するのに用いられる。“DK”という用語は、DK状態の目的、即ち、リソースがキル(“K”)される、即ち開放される前に定義(“D”)されるかどうかを判定するという目的から使用されている。エスクターナルのRS状態は、そのエクスターナルにリソースが関連付けられている場合に、関連付けられているリソースの状態である。“RS”という用語は、リソース(“R”)状態(“S”)からきている。
ある関数の各エクスターナルは、その関数の複数回のトラバースの結果生じる複数のCP、DK、及びRS状態をそれぞれ反映する複合CP状態、複合DK状態、及び複合RS状態を有する。繰り返しなされる関数の各トラバースの後において、エクスターナルの新たな複合RS状態が求められるが、以下により詳細に述べるように、エクスターナルの新たな複合RS状態は、そのエクスターナルの前の複合RS状態と、その関数の最も新しいトラバースの結果生じるそのエクスターナルに関連付けられたリソースのRS状態とから求められる。同様に、以下により詳細に述べるように、新たな複合CP及びDK状態は、前の複合CP及びDK状態と、関数の最も新しいトラバースの結果生じるCP及びDK状態とからそれぞれ求められる。
状態図350(第3B図)は、複合RS状態に対する状態及び状態遷移を表している。状態図350に於いて矢印は、関数のトラバースの結果生じるRS状態に基づく、前の複合RS状態からの複合RS状態遷移を表す。状態図350は表Eのようにまとめられる。
状態図400(第4A図)は、エスクターナルのCP状態に対する状態及び状態遷移を表す。状態図400に於いて矢印は、演算を施した結果生じるCP状態遷移を表す。エクスターナルは次のCPまたは複合CP状態を有し得る。
エスクターナルに施すことのできる演算は、表Bを参照して上記に示した通りである。状態図400は、以下の表Hにまとめられる。
状態図450(第4B図)は、エクスターナルの複合CP状態に対する状態及び状態遷移を表している。状態図450で使用されている矢印は、関数のトラバースの結果生じるCP状態に基づく、前の複合CP状態からの複合CP状態の遷移を表す。状態図450は以下の表Iにまとめられる。
状態図500(第5A図)は、エスクターナルのDK状態に対する状態及び状態遷移を表している。状態図500に於いて矢印は、演算を施した結果生じるDK状態遷移を表すのに使用されている。エスクターナルは、エスクターナルに関連付けられるリソースに関数の実行が与える効果を反映する、以下のDKまたは合成DK状態を有し得る。
エクスターナルに施すことのできる演算は、表Bを参照して上述した通りである。状態図500は以下の表Kにまとめられる。
状態図550(第5B図)はエクスターナルの複合DK状態に対する状態及び状態遷移を表す。状態図550に於いて矢印は、関数のトラバースから生じるDK状態に基づく、前の複合DK状態からの複合DK状態遷移を表すのに使用されている。状態図550は以下の表Lにまとめられる。
上記のソースコード抜粋(6)の関数example 2 () は、エクスターナルの複合状態の有用性を示す例となる。
上述したように、関数example 2 () の制御の流れは、エミュレーションによる関数の実行時のイベントに関連して設定される仮定に依存して複数の経路の内の何れかを通る。例えば、変数“str”がNULLでない場合は、ライン16の“if”ステートメントの後に、ライン17の“return”ステートメントが続き、そうでない場合はライン19の式が続く。関数example 2 () の戻り値項目は、関数example 2 () のエクスターナルである。関数example 2 () の戻り値項目への代入は、関数example 2 () の特定のトラバースに於いて設定される特定の仮定にのみ基づいて、ソースコード抜粋(6)のライン17、ライン25またはライン29に於いてなされる。
ライン17またはライン25では、戻り値項目にリソースは関連付けられない。従って、ソースコード抜粋(6)のライン17またはライン25の何れかを通って制御が流れるような関数example 2 () のトラバースの後において、戻り値項目を表すエスクターナルの複合RS状態は状態Uとなる。続いて、制御がライン29を通るように関数example−2 () をトラバースすると、戻り値項目を表すエクスターナルは、関数example 2 () 内で生成されたリソースに関連づけられ、適確に割り当てられる。即ち、状態Aとなる。ソースコード抜粋(6)のライン16乃至17により、関数malloc () の実行によってメモリが正常に割り当てられないというイベントに対して取られるべき動作が適切に記述されているため、リソースの適確な割り当てがなされるのである。
状態図350(第3B図)に示されているように、前の複合RS状態が状態Uで次のRS状態が状態Aであるようなエクスターナルは、状態Qの新たな複合RS状態を有する。これは、関数example 2の実行によって、その戻り値項目が指定するメモリの割り当てがなされ得るが、必ずしも割り当てがなされるというわけではないということを反映している。従って、関数example 2の挙動を記述する関数モデルを形成するとき、関数example 2の戻り値項目は、初期状態が状態Qである新たに生成されるリソースに関連付けられるものとして記述される。
また、複合状態を用いて、関数によるエクスターナルのつじつまのあわない使用を検出することができる。例えば、ある関数が、エクスターナルが割り当てられた状態、即ち状態AのRS状態で終了し、その関数のその後のトラバースに於いて、その関数が同じエクスターナルを開放した状態で、即ち状態KのRS状態で終了する場合、そのエスクターナルの複合RS状態は状態Eとなる。これはエラーとみなすことができる。なぜなら、一般に、呼び出し関数がある場合の実行においてはあるエクスターナルに関連付けられたリソースの割り当てをすることを求め、別の場合の実行においては同じエクスターナルに関連付けられたリソースを開放することを求めるということは考えられないからである。
コンピュータプログラムの解析
コンピュータプログラム610(第6図)の解析は、本発明に基づいて、本明細書中に説明するように、コンピュータプログラム610によって定められたリソースの使用を解析するリソースチェッカ(resourcechecker)602によってなされる。開示する実施例では、リソースチェッカ602は、バス108を介してCPU102に接続されたメモリ104から読み出されてCPU102において実行されるコンピュータプロセスである。
コンピュータプログラム610の本発明に基づく解析を、論理流れ図900(第9図)に示す。プロセスはステップ902において開始される。例えばキーボード124(第1図)やマウス122を用いてユーザが入力するコマンドによって、コンピュータプログラム610が解析される環境特性が特定され、コンピュータプログラム610(第6図)の解析が開始される。ユーザによって変更可能な環境特性には、(i)検出するべきエラーのタイプ、(ii)通知するべきエラーの最大数、(iii)解析するべき関数の最大数、(iv)各関数のトラバースの繰り返しの最大回数、及び(v)関数の全ての可能な制御流れ経路をトラバースするための特定の方法などがある。
プロセスは、ステップ902(第9図)からステップ904に進み、そこでリソースチェッカ602(第6図)は、コンピュータプログラムによって使用される様々な関数の実行がリソースに与える影響を記述する関数モデルを初期化する。リソースチェッカ602はモデルパーザ702(第7図)を含んでおり、モデルパーザ702は、モデル記述ファイル604(第6図)からモデルを読み出し、読み出したモデルから後述する関数モデル構造体を構築する。リソースチェッカ602において関数モデル構造体を生成することによって、関数モデルが初期化される。
ステップ904(第9図)については、後に論理流れ図904(第10図)を参照してより詳細に説明する。
プロセスはステップ904(第9図)からステップ906に進み、そこでリソースチェッカ602の一部であるプログラムパーザ704(第7図)は、コンピュータプログラム610(第6図)を読み出してきて、従来の技術を用いて、コンピュータプログラム610が従う言語に基づきパージング(parsing)を行う。プログラムパーザ704(第7図)はコンピュータプログラム610(第6図)をパージングして、より小さなプログラムコンポーネント、例えば関数、に分解する。ステップ906(第9図)では、1つの関数がコンピュータプログラム610(第6図)からパージングにより抽出され、抽出されたその関数を表す関数構造体が、後に詳述する動的検査エンジン(dynamic inspection engine)706に送られる。別の実施例では、後述するプリプロセッサがコンピュータプログラム610をパージングし、パージングにより抽出されたコンピュータプログラム610内の複数の関数を表現する複数の関数構造体が格納される。この別の実施例では、プログラムパーザ704は関数構造体を一つ取り出しては、取り出した関数構造体を動的検査エンジン706に送る働きをする。プロセスはステップ906(第9図)からステップ908に進む。
ステップ908において、リソースチェッカ602の一部である動的検査エンジン706(第7図)によって、“サブジェクト関数(subjectfunction)”、即ちステップ906(第9図)においてプログラムパーザ704によって動的検査エンジン706に送られた関数構造体によって表される関数、の解析がなされる。言い換えると、サブジェクト関数の実行の結果コンピュータプログラム610によって使用されるリソースに生じる効果が求められ、サブジェクト関数の実行によって影響される各リソースの状態遷移が解析される。これについては後により詳細に説明する。ステップ904において初期化された関数モデルが、サブジェクト関数のエクスターナル、リソースの状態及び状態遷移を解析するのに用いられる。検出された状態違反はプログミングエラーとして通知される。
サブジェクト関数の挙動が、そのエクスターナル及びリソースに関して定められると、モデルパーザ702はモデル記述ファイル604内にサブジェクト関数の挙動を記述する関数モデルを形成し格納する。ステップ908(第9図)については、論理流れ図908(第24図)を参照して後により詳細に説明する。
プロセスは、ステップ908(第9図)からテストステップ910に進み、そこでプログラムパーザ704(第7図)は更にコンピュータプログラム610(第6図)をパージングして、コンピュータプログラム610に、ステップ908(第9図)に基づいて動的検査エンジン706(第7図)によって解析されるべき関数がまだ含まれているかどうか判定する。上述した別の実施例では、プログラムパーザ704(第6図)は、ステップ908(第9図)に基づいて動的検査エンジン706(第7図)によって解析されなければならないコンピュータプログラム610の関数を表す関数構造体がまだあるかどうか判定する。動的検査エンジン706(第7図)がまだ処理していないコンピュータプログラム610の関数を表す関数構造体がある場合、プロセスはステップ906(第9図)に進み、そこでプログラムパーザ704(第6図)は、その関数構造体を上述したように動的検査エンジン706(第7図)に送る。動的検査エンジン706(第7図)がコンピュータプログラム610の関数を表す関数構造体を全て処理し終わっている場合は、プロセスは論理流れ図900に従って終了する(第9図)。
モデルの初期化
論理流れ図900のステップ904(第9図)を参照して上述したように、関数の挙動を記述する関数モデルは初期化される。ステップ904を、論理流れ図904(第10図)に詳細に示す。プロセスはステップ1002において開始され、上述したような関数モデルを含むモデル記述ファイル604(第6図)が開かれる。
一実施例では、関数モデルはテキスト形式で格納され、読み込まれ、そしてメモリ104(第1図)内の後に詳述するデータ構造内に格納される。関数モデルは、関数を識別する情報と、関数のエクスターナルに対するエクスターナルモデルのシングルリンクトリスト(singly−linkedlist)とを含む。関数を識別する情報には(i)関数名、(ii)関数が定義されているソースコードファイルの名前、(iii)関数の定義が始まるソースコードファイル中のテキストラインの番号、及び(iv)関数の簡単な説明が含まれる。ソースコードファイルは、メモリ104(第1図)(通常、磁気ディスクのような二次記憶装置)に格納された、コンピュータプログラム610のようなコンピュータプログラムを格納するファイルである。シングルリンクトリストの形態で格納されたエクスターナルモデルは、関数の実行が関数のエクスターナルに与える影響を、これらのエクスターナルに施される演算と、それらのエクスターナルに関連して生成されるリソースとに関して定めるものである。
エクスターナルモデルは、エクスターナルの型を特定する情報、エクスターナルを識別する情報、及び関数の実行がエクスターナルに与える影響を定める情報を含む。エクスターナルを識別する情報は、エクスターナルがパラメータの場合はパラメータ番号(parameter number)、エクスターナルがグローバルまたはスタティック変数の場合は変数名、エクスターナルが戻り値項目である場合はNULLである。関数の実行がエクスターナルに与える影響を定める情報には、(i)エクスターナルに施される演算のリスト、(ii)エクスターナルに関連して新たなリソースが生成されるかどうかを示すフラグ、及び(iii)新たなリソースが生成される場合、その新たなリソースの初期状態が含まれる。
モデル記述ファイル604(第6図)に格納されているモデルのテキスト書式は、以下の“Backus−Naur Form(BNF)”定義(8)に従う。“Backus−Naur Form”は形式的言語を記述するためのよく知られた書式である。
関数モデルは、テキスト書式においては、BNF定義(8)のノンターミナル<function−spec>によって表される。BNFにおいて、ターミナル(terminal)とは、ある特定のBNF定義においてそれ以上拡張されない項目であり、一方ノンターミナル(non−terminal)とは更に拡張される項目である。ターミナル<function−name>は、関数に割り当てられる識別予、即ち関数モデルによって表された関数を他の関数が呼び出すとき使用する識別子である。ターミナル<function−name>は、関数の定義に使われるコンピュータ言語において有効な関数識別子であればどのようなものでもよい。ターミナル<defining−file>は、関数の定義がなされるソースコードファイルを英数字で識別するものである。このような英数字による識別として、例えばソースコードファイルのパスを記述してもよい。ターミナル<defining−line>は、負でない数字、即ち0乃至9の数字を用いたテキスト表現によって、ターミナル<defining−file>によって識別されるソースコードファイルのどのテキストラインにおいてモデル化される関数の定義が始まるのかを特定するものである。
BNFでは、オプション項目は、角型括弧(“[]”)で囲って表される。従って、ターミナル<function−prefix>、ターミナル<defining−file>、<defining−line>、及び<description>は、オプションである。また、連続したスラッシュ記号(“//”)はコメント文の開始を表すものであり、これらのスラッシュ、及びこれらのスラッシュの後にそのテキストラインの最後まで続くテキスト文は、BNF定義の一部とはみなされないことに注意されたい。
BNF定義(8)のターミナル<description>は、1または複数のキャラクタ(即ち、文字、数字、及び/またはシンボル)の並びである。ターミナル<description>はリソースチェッカ602(第6図)によっては使用されないが、テキスト書式で表されたモデルを読むユーザの便宜及び理解のために提供されるものである。BNF定義(8)のターミナル<param−number>は、0乃至9の数字を用いた負でない整数のテキスト表現であり、パラメータリスト中の特定のパラメータを指定するものである。パラメータゼロが、関数の呼び出しにおけるパラメータリストの中の最初の、即ち最も左側のパラメータである。後続のパラメータには順番に番号が付される。BNF定義(8)のターミナル<var−name>は、変数の識別子である。
モデル記述ファイル604(第6図)から抽出される関数モデルは、それぞれ、各関数の実行がその関数のエクスターナルに及ぼす影響を記述する。プロセスはステップ1002(第10図)からループステップ1004に進み、モデル記述ファイル604(第6図)に格納されている各関数モデルは、ループステップ1004(第10図)とNEXTステップ1004とによって定められるループに従って取り出され処理される。ループの各繰り返しにおいて、処理されている関数モデルは、現関数モデル(current function model)と呼ばれる。ループステップ1004とNEXTステップ1014とによって定められるループに従って、モデル記述ファイル中に格納された全ての関数モデルの処理が完了すると、プロセスはループステップ1004からステップ1006に進み、モデル記述ファイル604(第6図)は閉じられ、論理流れ図904(第10図)に基づくプロセスは終了する。
モデル記述ファイルから取り出される各関数モデルに対し、プロセスはループステップ1004からステップ1008に進み、そこで上述したBNF定義(8)のノンターミナル<function−prefix>に対応する現関数モデルの一部が、現関数モデルから分離される。プロセスはステップ1010に進み、そこで関数モデル構造体が初期化されて、ステップ1008において現関数モデルから分離された情報が関数モデル構造体に格納される。
関数モデル構造体1100(第11図)は、フィールド“name”1102、フィールド“file”1110、フィールド“line”1112、及びフィールド“description”1108を含んでいる。BNF定義(8)のターミナル<function−name>、<defining−file>、<defining−line>、及び<description>(これらは全てノンターミナル<function−prefix>の部分である)に対応する関数モデルの部分が関数モデルから抽出され、関数モデル構造体1100のフィールド“name”1102、フィールド“file”1110、フィールド“line”1112、及びフィールド“description”1108にそれぞれ格納される。プロセスはステップ1010(第10図)からループステップ1012に進む。
ループステップ1012とNEXTステップ1028とによってループが定められており、その各繰返しにおいて、上述したBNF定義(8)のノンターミナル<extern−list>に対応する関数モデルの部分に示されたエクスターナルの処理がなされる。ループステップ1012とNEXTステップ1028とによって定められたループの各繰返しにおいて処理されているエクスターナルは、サブジェクトエクスターナルと呼ばれる。現関数モデル中に定められた全エクスターナルの処理がループステップ1012とNEXTステップ1028とによって定められたループに従って終了すると、プロセスはループステップ1012からNEXTステップ1014へ進む。プロセスはNEXTステップ1014からループステップ1004へと進み、モデル記述ファイル604(第6図)から取り出された別の関数モデルが更に処理されるか、あるいは全ての関数モデルが処理されている場合は、上述したように、プロセスはステップ1006(第10図)へと進む。
BNF定義(8)のノンターミナル<extern−Iist>に対応する現関数モデルの部分に示された各エクスターナルに対し、プロセスはループステップ1012からステップ1016へと進む。ステップ1016では、例えばエクスターナルモデル構造体1200(第12図)のような、新たなエクスターナルモデル構造体が生成される。
エクスターナルモデル構造体1200は、フィールド“equivalent”1202、フィールド“type”1204、フィールド“parameter number”1206、フィールド“name”1208、フィールド“next”1210、フィールド“number of operations”1212、フィールド“operations”1214、フィールド“new resource”1218、フィールド“initial state”1220、及びフィールド“description”1222を含んでいる。ステップ1016(第10図)では、BNF定義(8)のノンターミナル<external>の定義内のターミナル<param−number>に対応するサブジェクトエクスターナルモデルの部分が、サブジェクトエクスターナルモデルから取り出され、エクスターナルモデル構造体1200のフィールド“parameter number”1206(第12図)に格納される。
一実施例では、フィールド“equivalent”1202が用いられて、第2のエクスターナルモデル構造体が特定される。そうすることによって、エクスターナルモデル構造体1200が、第2のエクスターナルモデル構造体に関係付けられる。例えば、関数の戻り値項目が第1パラメータである場合、そのようにすることが適切であろう。本明細書中に開示する実施例では、フィールド“equivalent”1202は使用されず、従ってこのフィールドはNULL値に初期化される。ステップ1016(第10図)から、プロセスはステップ1018へと進む。
ステップ1018では、サブジェクトエクスターナルモデルによって表されたエクスターナルの型を定める、BNF定義(8)のノンターミナル<extern−type>に対応するサブジェクトエクスターナルモデルの部分が、サブジェクトエクスターナルモデルから抽出される。上記のBNF定義(8)に示されているように、エクスターナルモデルによって表されるエクスターナルは、戻り値項目、パラメータ、あるいはグローバル定義/スタティック変数であり得る。
サブジェクトエクスターナルモデルによって表されたエクスターナルの型を示すデータは、エクスターナルモデル構造体1200のフィールド“type”1204(第12図)に格納される。プロセスはステップ1018(第10図)からループステップ1020へと進む。
上記のBNF定義(8)に示されているように、関数の実行は、その関数の各エクスターナルに1または複数の影響または“結果(result)”を与え得る。各結果はBNF定義(8)においてノンターミナル<result>として表される。1または複数の結果がノンターミナル<result−list>に含まれる。ループステップ1020とNEXTステップ1024とによって定められるループにおいて、サブジェクトエクスターナルモデルのノンターミナル<result−list>のリスト内の各結果の処理がなされる。ループステップ1020とNEXTステップ1024とによって定められるループの繰返しにおいて、処理されている結果を、処理対象結果(subject result)と呼ぶ。以下に述べるように、サブジェクトエクスターナルモデルの全ての結果をループステップ1020とNEXTステップ1024とによって定められるループに基づいて処理すると、プロセスはループステップ1020からステップ1026へ進む。
サブジェクトエクスターナルモデルに対する各結果に対し、プロセスはループステップ1020からステップ1022に進む。ステップ1022において、処理対象結果がサブジェクトエクスターナルモデルから抽出される。この結果は、エクスターナルモデル構造体1200(第12図)のようなエクスターナルモデル構造体に格納される。例えば、上記で定義した関数モデル(3)は、第1エクスターナル、即ち戻り値項目に対し1つの結果を、第2エクスターナル、即ちパラメータ0に対し1つの結果を定めている。戻り値項目の結果は‘(new Q“memory”)’と定められており、戻り値項目に対し新たなリソースが生成されること、そのリソースの初期状態は状態Qであることが示され、更に、そのリソースの簡単な説明として“memory(メモリ)”が与えられている。
従って、この戻り値項目に対するエクスターナルモデルをエクスターナルモデル構造体1200が表現する場合、(i)フィールド“new resource”1218が“true”のブール値に設定され、新たなリソースが生成されることが示され、(ii)新たなリソースの初期状態が状態Qであることを示すようにフィールド“initial state”1220がセットされ、(iii)テキスト“memory“がフィールド“despription”1222に格納される。
第2の例として、上記の関数モデル(3)は、第2エクスターナル、即ちパラメータ0に対して結果“(op c)”を定めている。結果“(op c)”は、エクスターナルに演算Cが施されることを表す。従って、エクスターナルモデル構造体1200がパラメータ0に対するエクスターナルモデルを表す場合、初期値として0を有するフィールド“number of operations”1212がインクリメント(1増加)され、演算識別子“c”が、フィールド“number of operations”1212によって示される位置に対応するフィールド“operations”1214に格納される。この例では、フィールド“number of operations”1212は値1を格納し、フィールド“operations”1214内の最初の演算識別子は演算cの識別子となる。もし、第2の演算が第2のエクスターナルに施されるとすると、フィールド“number of operations”1212はもう一度インクリメントされ値2となり、フィールド“operations”1214内の第2の演算識別子が第2の演算の識別子となる。
プロセスはステップ1022(第10図)からNEXTステップ1024を介して上述したループステップ1020へ戻る。上述したようにプロセスは、サブジェクトエクスターナルモデルに対する全ての結果の処理が終了すると、ループステップ1020からステップ1026に進む。
ステップ1026では、サブジェクトエクスターナルモデルを表すエクスターナルモデル構造体が、現関数モデル構造体内のエクスターナルのシングルリンクトリストに加えられる。上記の関数モデル(3)の文脈において説明する。まず、関数モデル構造体1100Aのフィールド“first external”1004A及び“last external”1106Aにエクスターナルモデル構造体1200Aを指定するポインタを格納することによって、エクスターナルモデル構造体1200A(第13図)が関数モデル構造体1100Aに付け加えられる。続いて、エクスターナルモデル構造体1200Aのフィールド“next”1210A及び関数モデル構造体1100Aのフィールド“last external”1106Aに、第13図に示すようにエクスターナルモデル構造体1200Bを指すポインタを格納する(フィールド“last external”1106Aに前に格納されたポインタは取って代わられる)ことによって、第2エクスターナルモデル構造体1200Bが関数モデル構造体1100Aに加えられる。
プロセスはステップ1026(第10図)からNEXTステップ1028を介してループステップ1012へと進む。上述したように、全エクスターナルモデルが処理されていると、プロセスはループステップ1012からNEXTステップ1014を介してループステップ1004へ進む。上述したように、全関数モデルの処理が終了していると、プロセスはループステップ1004からステップ1006へ進み、上述したようにテキストフォーマットで記載された関数モデルを含むファイルが閉じられる。論理流れ図904に基づくプロセスは、ステップ1006の後終了する。
関数の内部表現
プログラムパーザ704(第7図)によってコンピュータプログラム610(第6図)のパージングがなされると、コンピュータプログラム610はメモリ104において一連の関数構造体によって表される。上述した別の実施例では、プログラムパーザ704は、コンピュータプログラム610から、例えばC言語のような特定のコンピュータ言語に従うソースコンピュータプログラムを予めパージングすることによって形成された関数構造体を取り出す働きをする。ソースコンピュータプログラムはソースコードプリプロセッサによってパージングされるが、このソースコードプリプロセッサは、ソースコンピュータプログラムが従うコンピュータ言語に基づいてソースコンピュータプログラムのパージングを行い、コンピュータプログラム610中にソースコンピュータプログラム内に定義された関数を表す関数構造体を形成し格納するものである。このソースコードプリプロセッサ(図示せず)はリソースチェッカ602とは別個のコンピュータプロセスである。
この別の実施例では、ソースコードプリプロセッサはマサチューセッツ州、ケンブリッジ(Cambridge,Massachusetts)の“Free Software Foundation,Inc”から入手可能な“GNU C Compiler”に基づくものである。本明細書の一部であり、全体が本出願に引証として含まれる付録Bとして示されているコンピュータ命令のリストは、抽出されたコンピュータプログラムの関数を、ソースコードプロセッサから、抽出された関数を表すための後に詳述するデータ構造へと移すためのデータ構造及び関数を定義したものである。一実施例では、公知の上記した“GNU C Compiler”のような従来のコンパイラが用いられてコンピュータプログラムのパージングがなされ、パージングされたプログラムは、付録Bに定義されているようなデータ構造中に表される。
以下に、関数構造体について説明する。関数構造体内のフィールド及び関係について理解を深めることによって、以下の動的検査エンジン706(第7図)の処理に対する説明がより容易となる。
関数構造体1400(第14図)は、コンピュータプログラム610、または上述したような別の実施例ではソースコードプログラム、によって定められた関数を表すものであり、(i)フィールド“name“1402、(ii)フィールド“line”1404、(iii)フィールド“file”1406、(iv)フィールド“result”1408、(v)フィールド“externals”1410及び(vi)フィールド“statement”を含んでいる。関数構造体1400のフィールド“name”1402は、関数構造体1400によって表される関数の識別予を特定する。例えば、上記のソースコード抜粋(1)の関数example 1 () の識別子は“example 1”である。
フィールド“file”1406及びフィールド“line”1404は、それぞれ、関数構造体1400によって表される関数が定義されるソースコードファイル及びそのファイル内のライン番号を示す。例えば、上記のソースコード抜粋(1)が、ファイル名“example 1.c”のソースコードファイルの全内容であるとした場合、関数example 1 () を表す関数構造体のフィールド“file”1406及びフィールド“line”1404は、それぞれ、テキスト“example 1.c”及び整数値7のデータを格納する。
フィールド“result”1408は、宣言構造体1418を指定する。この宣言構造体1418は、以下に述べる宣言構造体1506と同様であり、関数構造体1400によって表される関数がリターンする結果(result)の型を規定する。例えば、上記のソースコード抜粋(1)の関数example 1 () は、ソースコード抜粋(1)のライン7に示されているように、整数型の結果をリターンする。即ち、型“int”のデータをリターンする。従って、関数構造体1400が関数example 1 () を表す場合、フィールド“result”1408は、整数データを規定する宣言構造体1418を指定する。
関数構造体1400のフィールド“externals”1410は、後により詳細に説明するエクスターナルリスト構造体1414を指定するポインタである。後に詳述するように、エクスターナルリスト構造体1414のようなエクスターナルリスト構造体は、複数のエクスターナルリスト構造体をシングルリンクトリスト(singly−linked list)を形成するようにリンクするのに用いられるポインタを含んでいる。従って、リストの長さが1の場合も含み、一つのエクスターナルリスト構造体を指定することは、エクスターナルリスト構造体のシングルリンクトリストを指定することである。関数構造体1400のフィールド“externals”1410によって指定されるこのようなシングルリンクトリストは、関数構造体1400によって表される関数のエクスターナルを表すエクスターナルリスト構造体を含む。
関数構造体1400のフィールド“first stmt”1412は、後により詳細に説明するステートメント構造体1416を指定するポインタである。後により詳細に説明するように、ステートメント構造体1416のようなステートメント構造体は、複数のステートメント構造体をシングルリンクトリストを形成するようにリンクするのに用いられるポインタを含んでいる。従って、リストの長さが1の場合も含み、一つのステートメント構造体を指定することは、ステートメント構造体のシングルリンクトリストを指定することである。関数構造体1400のフィールド“first stmt”1412によって指定されるこのようなシングルリンクトリストは、関数構造体1400によって表される関数のステートメントを表すステートメント構造体を含む。
エクスターナルリスト構造体
エクスターナルリスト構造体1414を第15図に詳細に示す。エクスターナルリスト構造体1414は、関数構造体1400(第14図)によって表される関数のエクスターナルを表すものであり、フィールド“first decl”1502(第15図)、フィールド“next”1504、及びフィールド“first external”1510を含んでいる。フィールド“first decl”1502は、後により詳細に説明するように、エクスターナルリスト構造体1414によって表されるエクスターナルのデータ型を規定する宣言構造体1506を指定するポインタである。フィールド“next”1504は、エクスターナルのシングルリンクトリストにおいてエクスターナルリスト構造体1414のすぐ後にエクスターナルリスト構造体1508が続く場合、エクスターナルリスト構造体1508を指定するポインタである。エクスターナルリスト構造体のシングルリンクトリストにおいてエクスターナルリスト構造体1414に続くエクスターナルリスト構造体がない場合、エクスターナルリスト構造体1414のフィールド“next”1504はNULLとなる。即ち、NULLデータを含む。フィールド“first external”1510は、後により詳細に説明するように、エクスターナルリスト構造体1414によって表されるエクスターナルの状態を示すエクスターナル状態構造体(図示せず)を指定するポインタである。
宣言構造体
宣言構造体1506を第16図に詳細に示す。宣言構造体は、宣言される変数または関数、即ち宣言において規定される変数または関数を規定する構造体である。C言語の文脈中の宣言については周知であり、Cスタンダードにも示されている。宣言構造体1506は、フィールド“kind”1602、フィールド“name”1604、フィールド“type”1606、フィールド“item”1608、及びフィールド“model”1610を含んでいる。
フィールド“kind”1602は、宣言される項目または関数が、グローバルに定義されるものであるか、スタティックなものであるか、あるいはローカルに定義されるものであるかを示すデータを含む。フィールド“name”1604は、項目または関数の識別子を特定するテキストデータを含む。上述したように、C言語の文脈において、項目または関数はテキスト形式の識別子によって識別され、識別子はCスタンダードのセクション6.1.2に述べられているような所定の書式に従わなければならない。
宣言構造体1506のフィールド“type”1606は、宣言される項目または関数によって表されるデータの特定の型を定める型構造体1612を指定するポインタである。型構造体1612については、後で説明する。フィールド“item”1608は、宣言される項目を表す項目構造体2700を指定するポインタである。宣言構造体1506が宣言される関数を表現する場合、フィールド“item”1608はNULLであり、従って項目構造体は指定されない。
宣言構造体1506のフィールド“model”1610は、宣言構造体1506が関数の宣言を表し、その関数のモデルが関数モデル構造体1100によって表される場合、関数モデル構造体1100を指定するポインタである。宣言構造体1506が関数の宣言を表さない場合、フィールド“model”1610はNULLであり、即ち、NULLデータを含む。従って関数モデル構造体は指定されない。また、宣言構造体1506が関数の宣言を表すが、その関数の関数モデル構造体が存在しない場合も、フィールド“model”1610はNULLとなる。
型構造体
第17図に型構造体1612を詳細に示す。型構造体1612のような型構造体は、整数、浮動小数点、英数文字、及び構造体のようなユーザ定義による型、のような特定のデータ型を規定する。型構造体1612は、フィールド“kind”702、フィールド“name”1704、フィールド“size”1706、フィールド“points to”1708、及びフィールド“fields”1710を含んでいる。フィールド“kind”1702は、型構造体1612によって表される型が、整数、実数(即ち浮動小数点数値データ)、ポインタ、配列、構造体(即ち、C言語に対しデータ型“struct”で定義されたもの)、あるいはユニオン(union)のいずれであるか特定するデータを含む。これらの各型は公知であり、Cスタンダードのセクション6.1.2.5及び6.5以降に述べられている。
型構造体1612のフィールド“name”1704は、型構造体1612によって表される型がユーザ定義されるものである場合、その型の識別子を特定する英数字データを含む。型構造体1612によって表される型がC言語によって予め定められたものである場合、フィールド“name”1704はNULLである。
フィールド“size”1706は、型構造体1612によって表される型のサイズを規定する。型が配列でない場合、フィールド“size”1706は、型構造体1612によって表される型の項目に含まれるデータのビット数を示す。例えば、型が32ビット整数の場合、型構造体1612のフィールド“size”1706は値32を示す。型が配列の場合、フィールド“size”1706は配列全体に含まれるデータのビット数、即ちその配列のエレメントによって表されるその型の項目に含まれるデータのビット数にその配列全体に含まれるエレメントの数を掛けたものを表す。例えば、宣言“int array[10];”によって、10個のエレメントからなる配列が宣言されるとする。型“int”が32ビット整数の場合、宣言された配列のサイズは、10エレメント×32ビットとなる。従って、その配列のサイズは320ビットとなる。
構造体1612によって表される型が別のデータ型を指定するポインタの場合、フィールド“points to”1708は別のデータ型を表す型構造体、即ち型構造体1712を指定するポインタである。型構造体1712は型構造体1612と同様である。逆に、型構造体1612によって表される型がポインタでない場合、フィールド“points to”1708はNULLとなる。
型構造体1612によって表される型が構造体型(即ちC言語の型“struct”)またはユニオン型の場合、フィールド“fields”1710は、それぞれ、その構造体型またはユニオン型の第1フィールドを表すフィールド構造体1714を指定するポインタとなる。後により詳細に説明するように、ある特定の構造体型またはユニオン型のフィールドに対応するフィールド構造体は、シングルリンクトリストを形成するようにリンクされる。型構造体1612によって表される型が構造体型でもユニオン型でもない場合、型構造体1612のフィールド“fields”1710はNULLとなる。
フィールド構造体
フィールド構造体1714を第18図に詳細に示す。フィールド構造体1714は、フィールド“name”1802、フィールド“size”1804、フィールド“offset”1806、及びフィールド“next”1808を含んでいる。フィールド構造体1714について、C言語に基づく以下の型定義の例の文脈において説明する。
ソースコード抜粋(9)の型定義は、識別子が“point”であり2つのフィールドを有する構造体型を定義している。型“point”は従って構造体型である。各フィールドは型“int”であり、これは通常32ビット整数である。また、各フィールドは、識別子“x”または“y”を有している。
フィールド構造体1714のフィールド“name”1802は、フィールド構造体1714によって表されるフィールドの識別子を特定する英数字データを含む。例えば、ソースコード抜粋(9)に定義されている構造体の第1フィールドを表すフィールド構造体のフィールド“name”はテキスト“x”を含む。
フィールド構造体1714のフィールド“size”1804は、フィールド構造体1714によって表されるフィールドに含まれるデータのビット数を示す。例えば、カリフォルニア州、マウンテンビュー(Mountain View)のサンマイクロシステムズ社(Sun Microsystems,Inc.)から販売されている“SunOS C compiler”によってコンパイルされるようなC言語の典型的な例では、型“int”の項目は32ビット長さである。ソースコード抜粋(9)の例では、各フィールドは32ビット整数であり、従って32ビットのデータを含む。従って、個々のフィールドを表す各フィールド構造体のフィールド“size”は整数値32を示す。
フィールド構造体1714のフィールド“offset”は、フィールド構造体1714によって表されるフィールドのデータまでの構造体の開始からのオフセットを表す。例えば、ソースコード抜粋(9)におけるフィールド“x”は型“point”の第1フィールドであるため、オフセットは0である。
型“point”を第19図に図式的に示す。型“point”のフィールド“x”は32ビット長さであり、オフセット0で始まる。型“point”のフィールド“y”は32ビット長さであり、オフセット32で始まる。従って、フィールド構造体1714X(これはフィールド構造体1714(第18図)と全く同様であり、型“point”のフィールド“x”を表すものである。)のフィールド“offset“1806X(第20図)は、整数値0を示す。同様に、フィールド構造体1714(第18図)と全く同様な、型“point”のフィールド“y”を表すフィールド構造体1714Yのフィールド“offset”1806Y(第20図)は、整数値32を示す。
フィールド構造体1714のフィールド“next”1808(第18図)は、所与の構造体型のフィールド構造体のシングルリンクトリストにおける次のフィールド構造体を指定するポインタである。例えば、型“point”を表す型構造体1612Pのフィールド“fields”1710P(第20図)は、型“point”の第1フィールドであるフィールド“X”を表すフィールド構造体1710Xを指定する。型“point”の次のフィールドはフィールド“Y”である。従って、フィールド構造体1714Xのフィールド“next”1808Xは、型“point”のフィールド“y”を表すフィールド構造体1714Yを指定する。型“point”のフィールド“y”は、型“point”の最後のフィールドであり、その後に型“point”の他のフィールドはない。
従って、フィールド構造体1714Yのフィールド“next”1810YはNULLである。
ステートメント構造体
上述したように、関数構造体1400のフィールド“first stmt”1412(第14図)は、ステートメント構造体1416を指定する。ステートメント構造体1416は第21図に詳細に示されている。ステートメント構造体1416のようなステートメント構造体は、C言語に基づきまとまってある関数を形成する複数のステートメントを表す。ステートメント1416は次のフィールドを含んでる:(i)フィールド“kind”2102、(ii)フィールド“line”2104、(iii)フィールド“next”2106、(iv)フィールド“flags”2108、及び(v)フィールド“pointers”2110。
ステートメント構造体1416のフィールド“kind”2102は、ステートメント構造体1416によって表されるステートメントの種類を表す。フィールド“kind”2102は、以下のステートメント種類のうちの1つを特定する:エラー、宣言、式、ブロック、“if”、“else”、“return”、ループ、“switch”、“break”、“continue”、及び“goto”。ステートメント構造体によるこれらのステートメント種類の各々の表現について、以下により詳細に説明する。
ステートメント構造体1416のフィールド“line”2104は、関数構造体1400(第14図)によって表される関数を定義するソースコードファイル内において、ステートメント構造体1416によって表されるステートメントが現れる、即ち、ステートメント構造体1416によって表されるステートメントを含むテキストラインを表す。ステートメントが現れるラインは、エラーを生じさせる特定のステートメントが、検出されたエラーの通知によってユーザに特定されるようにするため、ステートメント構造体1416内に保持される。
ステートメント構造体1416のフィールド“next”2106(第21図)は、ステートメントのブロックにおいてステートメント構造体1416によって表されるステートメントのすぐ後に続くステートメントを表す別のステートメント構造体2112を指定するポインタである。このようにして、ステートメントブロックの全ステートメントが、シングルリンクトリストを形成するようにリンクされたステートメント構造体によって表される。ステートメント構造体1416(第21図)によって表されるステートメントがステートメントブロックの最後のステートメントの場合、フィールド“next”2106はNULLとなり、他のステートメント構造体を指定しない。
ステートメント構造体1416のフィールド“flags”2108は符号なし32ビット整数であり、その個々のビットはステートメント構造体1416によって表されるステートメントに関連するエラーがユーザに通知されたかどうかを示すフラグとして用いられる。エラーが通知されるときにはいつも、通知されるべきエラーに対応するフィールド“flags”2108のフラグがチェックされる。フラグがセットされている場合、そのフラグはステートメント構造体1416によって表されるステートメントの文脈において既にそのエラーの通知がなされていることを表すため、エラーは通知されない。フラグがセットされていない場合は、エラーは通知され、エラーの通知を反映するようにフラグがセットされる。このようにして、各タイプのエラーは、ある特定のステートメントに対して1回だけ通知される。
ステートメント構造体1416のフィールド“pointers”2110は、ステートメント構造体1416によって表されるステートメントの各部分を表す構造体を指定する1または複数のポインタの配列である。この配列中のポインタの数は、ステートメント構造体1416によって表されるステートメントの特定の種類に依存する。
エラー、“break”、及び“continue”ステートメントは部分を有さず、従ってステートメント構造体1416がエラー、“break”、あるいは“continue”ステートメントを表す場合、フィールド“pointers”2110はNULLである。エラーステートメントとは、C言語に従わないステートメントである。“break”及び“continue”ステートメントは周知であり、それぞれCスタンダードのセクション6.6.6.3及び6.6.6.2に説明されている。
宣言ステートメントには、特定の型のデータを有する宣言される変数が含まれ、更に恐らくはその変数に対する初期値も含まれる。従って、ステートメント構造体1416が宣言ステートメントを表す場合、フィールド“pointers”2110は2つのポインタからなる配列となる。第1のポインタは宣言される変数を表す宣言構造体を指定する。初期値が定められる場合、第2のポインタは、宣言される変数の初期値を与えるべく評価される式を表す式構造体を指定する。宣言される変数に対し初期値が定められない場合は、第2のポインタはNULLである。
式ステートメント(expression statement)とは、それ自身が式であるステートメントである。式(expression)はC言語の公知のコンポーネントであり、1または複数の項目、関数に対する呼び出し、及び演算子の集まりである。C言語では、全ての式は値(value)を有する。式の評価の結果は、値がその式の値であるような項目となり、この項目はしばしば式の項目と呼ばれる。本明細書中では、しばしば、式の項目の値を式の値と言う。式の評価については、以下により詳細に説明される。
ステートメント構造体1416が式ステートメントを表す場合、フィールド“pointers”2110は、式構造体2200(第22図)のような式構造体を指定する1つのポインタからなる配列となる。式構造体2200は、フィールド“kind”2202、フィールド“type”2204、フィールド“item”2206、フィールド“num operands”2208、及びフィールド“operands”2210を含んでいる。フィールド“kind”2202は、式構造体2200によって表される式の種類を特定する。式が演算子を含む場合、フィールド“kind”2202はその演算子を示す。
フィールド“type”2204は、式のデータ型、即ち、その式の評価の結果を示す項目の型を表す型構造体2212を指定するポインタである。型構造体2212は、上述した型構造体1612と同様である。式構造体2200のフィールド“item”2206は、式の評価の結果を示す項目を表す項目構造体2214を指定するポインタである。項目構造体2214は上述した項目構造体2700(第27図)と同様である。式構造体2200によって表された式の評価の前においては、フィールド“item”2206はNULLである。
式構造体2200によって表される式の中のオペランドの数はフィールド“num operands”2208によって示される。フィールド“operands”2210は式構造体の配列であり、その各々は式構造体2200によって表される式の一つのオペランドを表す。この配列の長さは、フィールド“num operands”2208に示されたオペランドの数に等しい。C言語において定義される式の様々な型、及び各型の式のオペランドの数及び型については周知であり、Cスタンダードにも記載されている。
ブロックステートメントとは、1または複数のステートメントをグループにまとめたステートメントである。ブロックステートメントの実行は、1または複数のステートメントの実行である。ブロックステートメントは部分、即ち、1または複数のステートメントを有する。ステートメント構造体1416(第21図)がブロックステートメントを表す場合、フィールド“pointers”2110はシングルポインタであり、このポインタは1または複数のステートメントの最初のステートメントを表すステートメント構造体を指定する。1または複数のステートメントを表すステートメント構造体は、上述したようにフィールド“next”2106を用いることによってシングルリンクトリストを形成するようにリンクされる。
“if”ステートメントは、“if”ステートメントの述語としばしば呼ばれる式を評価して、その評価の結果が“true”のブール値になる場合は、第2のステートメントを実行させるものである。ステートメント構造体1416が“if”ステートメントを表す場合、フィールド“pointers”2110は2つのポインタからなる配列となる。第1のポインタは、その値によって第2ステートメントが実行されるか否かが判定される式を表す式構造体を指定する。第2ポインタは、第2ステートメントを表すステートメント構造体を指定する。
“else”ステートメントは、“if”ステートメントのすぐ後に置かれ、“if”ステートメントの述語の評価が“false”のブール値なる場合、第3ステートメントを実行させる。ステートメント構造体1416が“else”ステートメントを表す場合、フィールド“pointers”2110は2つのポインタからなる配列となる。第1ポインタは、第3ステートメントを表すステートメント構造体を指定する。第2ポインタは、ある式構造体を指定するかまたはNULLである。この式構造体によって表される式は、しばしば、“else”ステートメントの述語と呼ばれる。第2ポインタが式構造体を指定する場合、第3ステートメントは、“if”ステートメントの述語の評価が“false”のブール値となり、“else”ステートメントの述語の評価が“true”のブール値となる場合にのみ実行される。Cスタンダードにも記載され広く知られているように、これは“else if”ステートメントを表す。第2ポインタがNULLの場合、第3ステートメントは、“if”ステートメントの述語の評価が“false”のブール値となる場合にのみ実行される。
“return”ステートメントは、被呼び出し関数の実行を終了させ、制御を呼び出し関数に移すとともに、場合によっては呼び出し関数に戻り値項目を与える。呼び出し関数に制御を移すとともに、呼び出し関数に戻り値項目を与えることは、呼び出し関数に戻り値項目をリターンすると言われる。ステートメント構造体1416が“return”ステートメントを表す場合、フィールド“pointers”2110はシングルポインタであり、このポインタは式構造体を指定するかまたはNULLである。このポインタが式構造体を指定する場合、その式構造体によって表される式の評価の結果を示す項目が呼び出し関数にリターンされる。
ループステートメントは、0回または1回以上実行される第2ステートメントを生成するものである。C言語におけるループステートメントの例として、“for”ステートメント、“do”ステートメント、及び“while”ステートメントがあるが、それらはCスタンダードのセクション6.6.5に記載されており広く知られている。ステートメント構造体1416がループステートメントを表す場合、フィールド“pointers”2110はシングルポインタであり、第2ステートメントを表すステートメント構造体を指定する。
“switch”ステートメントは、式を評価し、式の評価の結果を示す値に応じてブロックステートメント内の制御をそのブロックステートメント内の特定のステートメントへ移す。式は、しばしば“switch”ステートメントの述語と呼ばれる。ステートメント構造体1416が“switch”ステートメントを表す場合、フィールド“pointers”2110は2つのポインタからなる配列となる。第1のポインタは、その値に応じて制御が移行する式を表す式構造体を指定する。第2のポインタは、ブロックステートメントを表すステートメント構造体を指定する。
“goto”ステートメントは第2ステートメントへの制御の移行を発生させる。一実施例では、ステートメント構造体1416が“goto”ステートメントを表す場合、フィールド“pointers”2110は、第2ステートメントを表すステートメント構造体を指定するシングルポインタからなる配列である。より簡略化された実施例では、“goto”ステートメントは被呼び出しルーチンの実行を終了させるものとして取り扱われる。この実施例では“goto”ステートメントは部分を有さず、フィールド“pointers”2110はポインタを含まない配列である。
このようにして、関数構造体1400(第14図)は動的検査エンジン706によって解析されるべき関数を表現する。
関数の解析
上述したように、動的検査エンジン706(第7図)は、ステップ908(第9図)においてコンピュータプログラム610(第6図)のパージングの結果得られた各関数構造体を解析する。ステップ908は論理流れ図908(第24図)に、より詳細に示されている。ステップ908の実行において、処理されている関数構造体はサブジェクト関数構造体と呼ばれる。同様に、サブジェクト関数構造体によって表される関数は、サブジェクト関数と呼ばれる。論理流れ図908のステップ2404及び2408において、サブジェクト関数は様々な仮定のもとで解析される。
上記において詳しく説明したように、ある特定の関数の制御流れ経路は、しばしばその関数が実行されるまでは未知であるイベントに依存する。実行後でも、その関数のある実行時に生じたイベントがその関数の全ての実行においても常に発生するとは限らない。そのため、制御の流れが未知のイベントに依存するような関数の解析は、それらの未知のイベントについての様々な仮定の下で繰り返しなされる。
一実施例では、関数の全ての可能な制御流れが決定され解析される。例えば、制御は、関数内の全ての“if”ステートメントに対して2つの可能な経路の一方に沿って流れ得るし、また、関数内の全ての“switch”ステートメントに対して複数の可能な経路の内の1つに沿って流れ得る。“switch”ステートメントの場合、可能な経路の数は、その“switch”ステートメントに関連する“case”ステートメントの数に等しい。これには、存在する場合には、“default”ステートメントも含まれる。いったん関数の全ての可能な制御流れ経路が決定されると、その関数の各制御流れ経路が1回ずつ用いられるようにして、その関数は繰り返し解析される。このようにして、その関数の制御の流れに影響を与え得る全ての可能なイベントを考慮して関数の解析がなされる。
より簡略化された実施例では、関数の特定の制御流れ経路がその関数内の各“if”ステートメント及び各“switch”ステートメントにおけるイベントに関してランダムな仮定を設定することによってランダムに選択される。関数は様々な制御流れ経路がランダムに選択されるようにして繰り返し解析される。関数を解析する回数は、関数の全ての可能な制御流れ経路が解析される可能性が十分高くなるように選択されるか、あるいは別の方法として1つのルーチンを解析するのに費やされる労力を制限するように選択することもできる。
ステップ2404及び2408(第24図)は、後者のより簡略化された方の実施例を例示したものである。ステップ2404において、サブジェクト関数が解析される回数が決定される。ステップ2404は、論理流れ図2404(第25図)としてより詳細に示されている。ステップ2502において、サブジェクト関数内で“if”ステートメントが使用される回数が決定される。詳述すると、実行エンジン802(第8図)によって、関数構造体1400のフィールド“first−statement”1412(第14図)により直接的または間接的に指定されたステートメント構造体のシングルリンクトリスト内の各ステートメント構造体のフィールド“kind”2102(第21図)が、“if”ステートメントを示すデータと比較される。ステートメント構造体のフィールド“kind”と“if”ステートメントを示すデータとが整合する回数が、“if”ステートメントがそのサブジェクト関数内で使用される回数として記録される。
ステップ2502から、プロセスはステップ2504に進み、そこでサブジェクト関数が解析される回数が決定される。一実施例では、サブジェクト関数が解析される回数は、“if”ステートメントがサブジェクト関数内で使用される回数に対応する。その例を表Mに示す。
ステップ2504の後、論理流れ図2404に基づくプロセス、従ってステップ2404(第24図)は、終了する。プロセスはステップ2404からステップ2408へ進み、サブジェクト関数は上述したステップ2404で決定された数だけ繰り返し解析される。ステップ2408の一回の繰返し、即ち、サブジェクト関数の1回の解析は、論理流れ図2600(第26図)に示される。
サブジェクト関数の1回の繰返しの解析は、ステップ2602で開始され、そこで、各エクスターナルに対するエクスターナル状態構造体が初期化される。エクスターナル状態構造体は、まずそのエクスターナル状態構造体に対応する、即ちそのエクスターナル状態構造体によって表される状態を有するエクスターナルに対応する項目構造体を生成し、続いてそのエクスターナルのDK及びCP状態を状態Oに設定することによって初期化される。項目構造体は、項目を表すメモリ104(第1図)内の構造体である。
項目構造体2700(第27図)は、以下のフィールドを含んでいる。
即ち、フィールド“resource”2702、フィールド“external”2704、フィールド“value”2706、フィールドfirst in bunch2708、フィールド“size of bunch”2710、フィールド“type code”2712、フィールド“initialized”2714、フィールド“head in bunch”2716、フィールド“known bunch size”2718、及びフィールド“invalid pointer”2720を含んでいる。
各項目は、リソース及び/若しくはエクスターナルに関連付けられ得る。項目構造体2700により表現される項目がリソースの1つと関連している場合、項目構造体2700のフィールド“resource”2702が、そのリソースを表現するリソース状態構造体を指定する。逆に、その項目がリソースと関連していない場合、フィールド“resource”2702は、そのことを示すべくNULLにされる。項目構造体2700により表現される項目がエクスターナルの1つと関連している場合、項目構造体2700のフィールド“external”2704が、そのエクスターナルを表現するエクスターナル状態構造体を指定する。逆に、その項目がエクスターナルと関連していない場合、フィールド“external”2704は、そのことを示すべくNULLにされる。
項目構造体2700のフィールド“value”2706は、項目構造体2700によって表現される項目の実際の値を定めるデータを含んでいる。つまり、フィールド“value”2706に格納されたデータは、メモリ104(第1図)における、項目構造体2700(第27図)によって表現された項目によって指定された位置に格納された実際のデータを表現する。項目構造体2700のフィールド“type code”2712は、その項目のメモリ位置に格納されたデータの型を指定する。ここに開示する実施例において支援されているデータの型には、long、pointer、及びdoubleがある。最近のC言語の実装においては、“long”は32ビットの符号付き整数、“pointer”はメモリ104のようなメモリにおけるアドレスを指定する値、また、“double”は64ビットの浮動小数点である。フィールド“value”2706内には各データの型に対応するサブフィールドがある。使用されるのはただ1つのサブフィールドである。即ちフィールド“type code”2712で指定されたデータの型に対応するサブフィールドのみが使用される。
項目構造体2700のフィールド“initialized”2714は、項目構造体2700により表現されたその項目が初期化されているか否か、即ち項目構造体2700によって表現される項目が既知の値を有するか否かを示す。項目構造体2700のフィールド“invalid pointer”2720は、項目構造体2700によって表現される項目が無効ポインタであるか否かを示す。C言語においては、ポインタがメモリ104(第1図)における有効な位置を指定している場合、そのポインタは有効である。そうでない場合、そのポインタは無効である。NULLポインタは、C言語の多くの実装において0に選択される特定の無効ポインタである。一実施例においては、フィールド“initialized”2714及び“invalid pointer”2720は、それぞれ1つのビットよりなる。
フィールド“first in bunch”2708、“size of bunch”2710、“head in bunch”2712、及び“known bunch size”2718は、メモリの集群(bunch)を解析するのに用いられる。メモリの集群については、後に詳しく説明する。
ステップ2602(第26図)において、ひとたびこのサブジェクト関数のエクスターナルが初期化されると、処理はステップ2604に進む。ステップ2604において、サブジェクト関数の各ステートメントが評価される。ステートメントの評価は、そのステートメントの実行をエミュレートすることによって行われる。ステートメントの評価により、エクスターナル及び/若しくはリソースに対して施した演算の結果が得られ、エクスターナル及び/若しくはリソースのそれぞれの状態の変化が生ずる。後に詳しく説明するが、各ステートメントの評価は論理流れ図2800(第28図)に従って個別に行われる。
ひとたびサブジェクト関数の各ステートメントが評価されると、処理はステップ2604(第26図)からステップ2606に進む。ステップ2606においてはサブジェクト関数の様々なリソースの状態のリーク(leak)がチェックされる。ステップ2606の詳細は論理流れ図2606(第41図)に示されており、これについては後に説明する。ステップ2606から処理はステップ2608に進み、ここでサブジェクト関数の各エクスターナルが更新される。エクスターナルの更新は、エクスターナルのDK及びCP状態、及びそのエクスターナルに関連付けられたリソースのRS状態に基づいて、エクスターナルの複合DK、CP、及びRS状態を更新することによって行われる。これらの状態はサブジェクト関数の現反復的解析(current iterative analysis)から得られる。1つのエクスターナルの更新は、論理流れ図4200(第42図)に示されており、後に完全に説明する。
ステップ2608(第26図)が終了すると、論理流れ図2600に従った処理、即ちサブジェクト関数の反復的解析の1回分が終了する。
ステートメントの評価 上述のように、サブジェクト関数の各ステートメントは論理流れ図2800(第28図)に従って個別に評価される。処理はテストステップ2802から開始されるが、このステップでは実行エンジン802(第8図)が、そのステートメント、すなわちサブジェクトステートメントの構造体を表すステートメント構造体のフィールド“kind”2102(第21図)を検索することによって、そのステートメントが式であるか否かを判定する。そのステートメントはサブジェクトステートメント構造体によって表されるサブジェクトステートメントである。即ち、その時点で論理流れ図2800(第28図)に従って評価されているステートメントをサブジェクトステートメントと称する。
サブジェクトステートメントが式である場合、すなわちフィールド“kind”2102が、サブジェクトステートメントが式であることを示している場合には、処理はテストステップ2802(第28図)からステップ2804に進み、そこで実行エンジン802(第8図)が式、すなわちサブジェクトステートメントの評価を行う。実行エンジン802は、その式に含まれる項目上の関数及び演算子の実行をエミュレートすることによって式の評価を行う。ステップ2804(第28図)は論理流れ図2900(第29図)に従って実行されるが、これについては後に詳述する。
後に詳しく述べるように、論理流れ図2900に従う処理は、式の評価の結果得られた項目に対して演算を施し得る。ステップ2804の文脈に於いては、式の項目に対して施される演算は無い。テストステップ2802(第28図)に於いて、サブジェクトステートメントが式でない場合は、処理はテストステップ2806に進む。
テストステップ2806に於いては、実行エンジン802(第8図)がサブジェクトステートメント構造体のフィールド“kind”2102とそのサブジェクトステートメントが宣言であることを示すデータとを比較する。defining宣言は、項目を生成させるC言語に従ったステートメントである。declaring宣言は、その項目があたかも特定の型であるかのようにその項目を取り扱うようCPU102(第1図)に指示するC言語に従ったステートメントである。ここで述べたもの以外の場合には、宣言はdefining宣言となる。サブジェクトステートメントが宣言である場合、処理はテストステップ2806(第28図)からステップ2808に進み、ここでその宣言が処理される。これについては後に詳しく述べる。逆に、サブジェクトステートメントが宣言でない場合は、処理はテストステップ2806からテストステップ2810に進む。
テストステップ2810に於いては、実行エンジン802(第8図)が、サブジェクトステートメント構造体のフィールド“kind”2102(第21図)と、サブジェクトステートメントが“if”ステートメントであることを示すデータとを比較する。そのステートメントが“if”ステートメントである場合は、処理はテストステップ2810(第28図)からステップ2812に進み、ここでそのサブジェクトステートメントが処理される。ステップ2812は後に説明する論理流れ図2812(第35図)により詳細に示されている。逆に、サブジェクトステートメントが“if”ステートメントでない場合は、処理はテストステップ2810(第28図)からテストステップ2814に進む。
テストステップ2814に於いては、実行エンジン802(第8図)が、サブジェクトステートメント構造体のフィールド“kind”2102(第21図)
と、そのサブジェクトステートメントが“return”ステートメントであることを示すデータとを比較する。サブジェクトステートメントが“return”ステートメントである場合は、処理はテストステップ2814(第28図)からステップ2816に進み、ここでそのステートメントが処理される。ステップ2816は後に説明する論理流れ図2816(第39図)により詳細に示されている。逆に、そのサブジェクトステートメントが“return”ステートメントでない場合は、処理はテストステップ2814(第28図)からテストステップ2818に進む。
テストステップ2818に於いては、実行エンジン802(第8図)が、そのサブジェクトステートメントのフィールド“kind”2102(第21図)と、そのサブジェクトステートメントがループ若しくはブロックステートメントであることを示すデータとを比較する。サブジェクトステートメントがループ若しくはブロックステートメントである場合には、処理はテストステップ2818(第28図)からステップ2820に進み、そのステートメントが処理される。ステップ2820は後に説明する論理流れ図2820(第40図)により詳細に示されている。逆に、そのサブジェクトステートメントはループステートメントでもブロックステートメントでもない場合には、処理はテストステップ2818(第28図)からテストステップ2822に進む。
テストステップ2822に於いては、実行エンジン802(第8図)が、サブジェクトステートメントのフィールド“kind”2102(第21図)と、そのサブジェクトステートメントが“goto”ステートメントであることを示すデータとを比較する。そのサブジェクトステートメントが“goto”ステートメントである場合には、処理はテストステップ2822(第28図)からステップ2824に進み、そこで実行エンジン802(第8図)が、後に詳述するように“return”条件を示すデータを制御レコード内に格納する。この制御レコードは、後に詳述するようにそのサブジェクト関数の実行のエミュレーション時に適切な制御の流れを与えるために用いられる。“return”条件は、サブジェクト関数の反復的解析を終了させる。
サブジェクトステートメントが“goto”ステートメントでない場合には、処理は論理流れ図2800に従って終了する。
ステップ2804、2808、2812、2816、2820、または2824のいずれかが実行されれた後、処理は論理流れ図2800に従って終了する。従って、実行エンジン802(第8図)によるステートメントの評価は、そのステートメントが式であるか、宣言であるか、“if”ステートメントであるか、“return”ステートメントであるか、ブロックステートメントであるか、ループステートメントであるか、若しくは“goto“ステートメントであるかによって、それぞれステップ2804(第28図)、ステップ2808、2812、2816、2820、2824、若しくは2828において行われるのである。
式の評価
上述のように、式は論理流れ図2900(第29図)に従って評価されるが、この場合実行エンジン802(第8図)が、動的検査エンジン706の一部であるステートマシン804をして、実行エンジン802によって指定された演算がある場合にはその演算をその式の項目に施すようにする。上述のように、ステップ2804(第28図)の文脈に於いては、実行エンジン802(第8図)はこのような演算の指定を行わない。ここに開示する本発明の実施例では、式を評価することで、式であるステートメント若しくは式を含むステートメントの実行によりその式のオペランドである項目に及ぼす効果を判断している。
論理流れ図2900(第29図)に従う処理は、ステップ2902から開始されるが、ここでは式の評価を行うために、その式に於ける演算子によって指定された処理が実行される。上述のように、式は、動的検査エンジン706(第7図)内に於いて式構造体2200(第22図)のような式構造体によって表現される。式構造体2200のフィールド“kind”2202はその式の演算子の種類を指定し、またフィールド“operands”2210はその演算子の演算が施されるオペランドを含む。
このサブジェクト関数の解析は、コンピュータプロセス内に於けるそのサブジェクト関数の実行の文脈内で行われるので、このサブジェクト関数のエクスターナルの初期値は未知である。従って、このサブジェクト関数に於ける式は、既知の値に評価しなくてもよい。従って、ステップ2902(第29図)での実行エンジン802(第8図)による式の評価により、式の評価により既知の若しくは部分的に既知の値が生成される場合には項目が生成され、そうでない場合にはNULLが生成される。後に詳述するように、項目は、「部分的に既知」の値を有し得る。例えば、項目が0でない値を有していることは知ることはできるが、項目の正確な値はまだ知ることができず、この場合その項目の値は「部分的に既知」であるという。ステップ2902は論理流れ図2902(第33a図、第33b図、第33c図、第33d図、及び第33e図)に詳しく示されており、後に詳細に説明する。ステップ2902(第29図)から、処理はテストステップ2904に進み、ここで実行エンジン802(第8図)が、式の評価によりNULLでなく項目が生成されたか否か、及びステートマシン804によりその項目に演算が施されるか否かを判定する。その式の評価により項目が生成されない場合、若しくはその項目に演算が施されない場合には、処理はテストステップ2904(第29図)から後に説明するステップ2908に進む。逆に、その式の評価により項目が生成されその項目に演算が施される場合には、処理はテストステップ2904からステップ2906に進み、そこでステートマシン804(第8図)がその項目に演算を施す。
評価された式の項目に演算を施すために、ステートマシン804は、その項目にエクスターナル及びリソースがそれぞれ関連付けられている場合には、その項目に関連付けられたエクスターナル及びリソースに対して演算を施す。例えば、項目構造体2700(第27図)のフィールド“external”2704が、項目構造体によって表現された項目に関連付けられたエクスターナルを表現するエクスターナル状態構造体を指定する。同様に、項目構造体2700のフィールド“resource“2702が、そのアイテムに関連付けられたリソースを表現するリソース状態構造体を指定する。
エクスターナルに施される演算には、例えばエクスターナル状態構造体3000のフィールド“DK”3004(第30図)及び“CP”3008の更新がある。このエクスターナル状態構造体3000は、そのエクスターナルを表現するエクスターナル状態構造体である。フィールド“DK”3004の更新は、そのエクスターナルに施される演算に従って、また上述の状態図500(第5a図)に従って行われる。例えば、フィールド“DK”3004(第30図)が状態Oを示しており、そのエクスターナルに演算mが施される場合、フィールド“DK”3004は状態Qを示すように更新される。フィールド“CP”3008の更新は、状態図400(第4a図)に従って行われる。
リソースに施される演算には、例えば、リソース状態構造体3100のフィールド“state”3102(第31図)及び“modified”3108の更新がある。このリソース状態構造体は、そのリソースを表現するリソース状態構造体である。フィールド“state”3102は、上述の状態図300(第3A図)に従って更新される。フィールド“modified”3108(第31図)は、現在行を指定するデータをフィールド“modified”3108に格納することによって更新され、最後にリソースを修正したステートメントを示すようにされる。この現在行とは、コンピュータプログラム610(第6図)上のそのサブジェクトステートメントが存在する行を指す。リソースに関連するプログラミングエラーを知らせると共に、最後にリソースを修正したステートメントをユーザに対して示すことによって、サブジェクト関数の開発者がプログラミングエラーを除去する助けとなる。
後に詳しく説明するが、エクスターナル状態構造体3000のフィールド“DK”3004(第30図)若しくは“CP”3008の更新、またはリソース状態構造体3100のフィールド“state”3102(第31図)の更新によって、状態図300、400、または500(第3A図、第4図、及び第5図)に基づくエラーが生じた場合には、このエラーはユーザに通知される。式の評価により生成された項目も、論理流れ図3200(第32図)に基づいて状態違反を調べられる。
テストステップ3202(第32図)においては、ステートマシン804(第7図)が項目構造体2700のフィールド“initialized”2714(第27図)を調べて、項目構造体2700によって表現された項目が初期化されたか否かを判定する。その項目が初期化されていない場合には、処理はテストステップ3202(第32図)からステップ3204に進み、ここでエラーが報告される。論理流れ図3200の各ステップの実行はステップ2906(第29図)内においてのみ実行されることから、演算が施されるのは論理流れ図3200(第32図)における項目に対してである。従って、その項目が初期化される場合、演算の適用で示されるように、初期化される前にその項目が使用され、これがエラーとなる。
その項目が初期化された場合には、処理はテストステップ3202からテストステップ3206に進む。テストステップ3206においては、ステートマシン804(第8図)が、項目構造体2700のフィールド“invalid pointer”2720(第27図)を検査することによりその項目が無効ポインタであるか否かを判定するとともに、施される演算と、演算i、即ち間接演算とを比較する。その項目が有効ポインタで、演算iが施される場合には、処理はステップ3208(第32図)に進み、そこでエラーが通知される。逆に、その項目が有効ポインタでなく、若しくは演算i以外の演算が施される場合には、処理はステップ3206からテストステップ3210に進む。
テストステップ3210において、ステートマシン804(第7図)は、その項目が、テストステップ3206に関して上述したのと同様に無効ポインタであるか否かを判定し、施される演算と演算kとを比較する。C言語の文脈においては、無効ポインタを解放することは、ファイル及び動的に割振られるメモリを管理するのに使用するデータ構造をライブラリ関数が損なうことになり得るために、エラーとなる。NULLポインタを解放することは一般的にはエラーではないが、不手際なプログラム作成方法と一般に考えられ、エラーとして通知される。
その項目が無効ポインタであり、演算kがその項目に施される場合には、処理はステップ3212に進み、そこでエラーの通知がなされる。逆にその項目が無効ポインタでないか、若しくは演算k以外の演算が施される場合には、処理は論理流れ図3200に従って終了する。また、ステップ3204、3208、及び3212の終了後も、処理は論理流れ図3200に従って終了する。
従って、ステップ2906(第29図)において、項目及びそれに関連するリソースまたはエクスターナルに演算が施され、何らかのエラーが検出されてそれがユーザに通知される。ステップ2906から、処理はステップ2908に進む。また、式の評価により生成された項目が存在しない場合、若しくは上述のようにその式に対して施される演算が無い場合は、処理はテストステップ2904から直接ステップ2908に進む。ステップ2908においては、実行エンジン802は、式、即ち、サブジェクトステートメントを含んでおり、更に、項目が生成された場合にはその項目を、生成されなかった場合には数値がNULLである項目を含んでいる。詳述すると、実行エンジン802はサブジェクトステートメントを表現する式構造体2200のフィールド“item”2206(第22図)に、その項目を指定するポインタを格納する。従って、後に行われるその式の評価においては、単に、その項目がフィールド“item”2206が指定する部位に戻されるだけとなり、これによって冗長な処理が回避される。テップ2908(第29図)の後、処理は論理流れ図2900に従って終了する。
定数
上述のように、論理流れ図2902(第33A図〜第33E図)に詳細に示されているステップ2902(第29図)においては、実行エンジン802(第8図)は、式における演算子によって規定されている通りに式を処理する。実行エンジン802は、演算の型に応じてその式を処理する。テストステップ3301(第33A図)においては、論理流れ図2902に従って処理が開始されるが、ここでは実行エンジン802(第8図)が、その式が演算子は含んでいないが、代わりに定数を含んでいるか否かを判定する。式構造体2200(第22図)がサブジェクトステートメントの式を表現している場合は、実行エンジン802(第8図)は、式構造体2200のフィールド“kind”2202(第22図)と、式が定数であることを示すデータとを比較することによってこの判定を行う。定数は、その値がコンピュータプロセスの実行中に変化しない項目である。例えば、式“10”は、整数であり、その値が常に10である定数である。
その式が定数でない場合には、処理は後に述べるテストステップ3303(第33A図)に進む。逆に、式が定数である場合には、処理はテストステップ3301からステップ3302に進む。ステップ3302においては、実行エンジン802(第8図)が定数を表現する項目構造体を生成し、その項目構造体を、その定数の値を有するように初期化する。ステップ3302の終了後、処理は論理流れ図2902及びステップ2902(第29図)に従って終了する。
変数
上述のように、その式が定数でない場合には、処理はテストステップ3301(第33A図)からテストステップ3303に進む。テストステップ3303においては、実行エンジン802(第8図)が、その式を表現する式構造体のフィールド“kind”2202(第22図)と、その式が変数であることを示すデータとを比較することによって、その式が変数であるか否かを判定する。変数である式は、その変数の項目の現項目値に評価される。例えば、サブジェクト関数の以前に処理されたステートメントが、識別子が“i”、即ち型が“int”、即ち整数である変数“i”である変数を宣言する場合には、式“i”は、変数“i”の項目の現項目値に評価される。式が変数でない場合には、処理は後に説明するテストステップ3305(第33A図)に進む。逆に、その式が変数である場合には、処理はテストステップ3303からステップ3304に進む。
ステップ3304においては、実行エンジン802(第8図)が変数iの項目を検索する。式構造体2300(第23図)がその式を表現する場合、即ち変数を表現する場合には、(i)フィールド“num operands”2308が、1つのオペランドを指定する値1を有し、(ii)フィールド“operands”2310が宣言構造体2310を指定する1つのポインタの配列となる。宣言構造体2316のフィールド“item”2324は、式2300の値として検索される。宣言構造体2316に関連付けられた項目が存在しない場合には、フィールド“item”2324はNULLとなる。従って、存在する項目構造体またはNULLの何れかが、ステップ3304(第33A図)において取り出されるのである。ステップ3304の終了後、処理は論理流れ図2902即ちステップ2902(第29図)に従って終了する。
2項演算子
上述のように、その式が宣言でない場合には、処理はテストステップ3303(第33A図)からテストステップ3305に進む。テストステップ3305において、実行エンジン802(第8図)は、その式を表現する式構造体のフィールド“kind”2202(第22図)と2項演算子を示すデータとを比較することによって、その式の演算子が2項演算子であるか否かを判定する。2項演算子は2つのオペランドに対して演算を施す演算子であって、関係演算子ではない演算子である。例えば、式“a+b”には、加算を指定する2項演算子“+”が含まれている。関係演算子については後に説明する。
論理流れ図2902(第33A図)に従って処理される式の演算子が2項演算子でない場合には、処理はテストステップ3305からテストステップ3309に進む。逆に、式の演算子が2項演算子である場合には、処理はテストステップ3305からステップ3306に進む。ステップ3306においては、左側のオペランド、即ち“lhs”が論理流れ図2900(第29図)に従って式として評価される。2つのオペランドを有し、式構造体2200(第22図)によって表現される式のlhsは、フィールド“operands”2210の第1の要素である式構造体よって表現される。このlhsは、論理流れ図2900(第29図)に従って評価されるとともに、演算Cが施される。論理流れ図2900に従って演算を施すことについては、前に説明した。従って、論理流れ図2900に従った式の評価は反復的に行われる。つまり、論理流れ図2900に従った式の評価により、論理流れ図2900に従ったその式の部分式(subexpression)の評価も行われることになり得るのである。このような再帰的プログラミングは周知の技術である。
2項演算子は、左側のオペランドと共に、右側のオペランド、即ち“rhs”を有する。例えば、式“(a+b)*c”は、lhs“(a+b)”と、rhs“c”とを有する。というのはこの式の中で最も優先順位の高いものは、演算子“*”、即ち乗算演算子だからである。2つのオペランドを有し、式構造体2200(第22図)によって表現される式のrhsは、フィールド“operands”2210の第2要素の式構造体によって表現される。ステップ3306(第33A図)において式のlhsが評価された後、処理はステップ3307に進み、ここで実行エンジン802(第8図)が、論理流れ図2900(第29図)に従ってこの式のrhsの評価を行うとともに、演算cを施す。演算cは、式のlhs及びrhsの双方に施されるが、これはそれぞれが計算において使用されるからである。このような計算において、式のlhs若しくはrhsの何れかを不適切に使用している場合は、上述のように演算を施すことによってエラーメッセージが生成される。
ステップ3307(第33A図)から、処理はステップ3308に進み、ここでその式の2項演算子を用いて式の評価が行われる。この式のlhs及びrhsのデータの型は演算子によって呼び出される演算の型に影響する。C言語において、特定の演算子が特定の型のオペランドに対して実行する演算の型は、Cスタンダードのセクション6.3他に記載されており、周知である。
式の演算子は、Cスタンダードに記載された所定の演算子の適用方法に従って式のオペランドに適用される。例えば、演算子が算術加算演算子(即ち“+”)である場合には、演算子を2つのオペランドに適用した結果、2つのオペランドの算術和が得られる。第2の例として、演算子が、一方の値が他方より大きいことを表す関係演算子(即ち、“>”)である場合には、この演算子は2つのオペランド、即ちlhsとrhsとに適用することにより、lhsの値がrhsの値より大きい場合にはブール値“true(真)”が得られ、逆の場合にはブール値“false(偽)”が得られる。
コンピュータプログラム610におけるリソースの不適切な使用を検出するリソースチェッカ602(第6図)のために、実行エンジン802(第8図)によりC言語の全ての演算子が適切に適用される必要は必ずしもない。式が実行エンジン802(第8図)によって適用され得ない演算子を含む場合、この式はNULLに評価され、その式の評価により値が未知の項目が生成されることが示される。しかし、実行エンジン802が、できる限り多くのC言語の演算子を適用して、リソースチェッカ602によって検出されたリソースの不適切な使用を正しく改善し得るのは好ましいことである。
ステップ3308(第33A図)の後、処理は論理流れ図2902、即ちステップ2902(第29図)に従って終了する。
関係演算子
上述のように、式の演算子が2項演算子でない場合には、処理はテストステップ3305(第33A図)からテストステップ3309に進む。テストステップ3309においては、実行エンジン802(第8図)が、その式の演算子が関係演算子であるか否かを判定する。関係演算子は2つのオペランド、即ちlhs及びrhsに対して演算を行い、演算結果として2つのオペランドの値を比較したブール値に対応する数値を有する項目を生成する演算子である。ブール値は、“true”若しくは“false”の何れかである。関係演算子の具体例には、“==”(等しい)、“>=”(大きいか若しくは等しい)、“<=”(小さいか若しくは等しい)、及び“!=”(等しくない)がある。
この式の演算子が関係演算子でない場合は、処理はテストステップ3309(第33A図)から、後に説明するテストステップ3313に進む。逆に式の演算子が関係演算子である場合は、処理はテストステップ3309からステップ3310に進む。ステップ3310においては、実行エンジン802(第8図)が、論理流れ図2900(第29図)に従って式のlhsを式として評価するとともに、演算pを適用する。ステップ3310(第33A図)から、処理はステップ3311へ進み、ここで実行エンジン802(第8図)が論理流れ図2900(第29図)に従って、式のrhsの評価を行うとともに、演算pを適用する。演算pは式のlhs及びrhsの双方に適用されるが、これはそれぞれが比較のために使用されるからである。このような比較処理において、式のlhs若しくはrhsの何れかが不適切に使用されている場合には、上述のように演算が適用されることによりエラーメッセージが生成される。
処理はステップ3311(第33A図)からステップ3312に進み、ここで式の関係演算子が用いられて式の評価が行われる。ステップ3312は、前に説明したステップ3302に類似している。ステップ3312の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
単項演算子
上述のように、この式の演算子が関係演算子でない場合には、処理はテストステップ3309(第33A図)からテストステップ3313に進む。テストステップ3313においては、実行エンジン802(第8図)が、式の演算子が単項演算子であるか否かを、その式を表現する式構造体のフィールド“kind”2202(第22図)と、単項演算子を示すデータとを比較することによって判定する。単項演算子は、ただ1つのオペランドを有する演算を特定する演算子である。例えば、式“−a”は、ただ1つのオペランド“a”と、オペランド“a”に対して数値を負にする演算を特定する単項演算子“−”とを有する。式が1つのオペランドを有し、かつ式構造体2200によって表現されている場合には、その式のただ1つのオペランドが、フィールド“operands”2210の第1の要素である式構造体によって表現される。
式の演算子が単項演算子でない場合には、処理はテストステップ3313(第33B図)から、後に説明するテストステップ3313(第33B図)に進む。逆に、式の演算子が単項演算子である場合には、処理はテストステップ3313(第33B図)からステップ3314に進む。ステップ3314においては、実行エンジン802(第8図)が、論理流れ図2900(第29図)に従って式のオペランドを式として評価するとともに、演算cを適用する。演算cは、式のオペランドに適用されるが、これはこのオペランドが計算において使用されるからである。このような計算において式のオペランドが不適切に使用されている場合には、上述のように、演算を適用することによりエラーメッセージが生成される。
処理はステップ3314(第33B図)からステップ3315に進み、ここでは式の単項演算子が式の評価のために用いられる。ステップ3315は上述のステップ3308に類似している。ステップ3315の終了後、処理は論理流れ図2902、即ちステップ2902(第29図)に従って終了する。
特定の演算子による処理
上述のように、式の演算子が単項演算子でない場合には、処理はテストステップ3313(第33B図)からテストステップ3317(第33B図)に進む。更に上述したように、論理流れ図2902における上述のステップでは、式の演算子の型に従って式の処理が行われる。テストステップ317及びそれに続くステップにおいて、式の演算子の型がテストステップ3301(第33A図)、3303、3305、3309及び3313で判定される演算子の型の中に入っていない場合に、実行エンジン802(第8図)が、式の特定の演算子に従って式の処理を行う。
インクリメント演算子またはデクリメント演算子
ステップ3317(第33B図)においては、実行エンジン802(第8図)が、式の演算子とインクリメント演算子及びデクリメント演算子とを比較する。つまり、式構造体2200(第22図)がその式を表現している場合、実行エンジン802は、フィールド”kind”2202(第22図)と、インクリメント演算子若しくはデクリメント演算子を指定するデータとを比較する。インクリメント演算子若しくはデクリメント演算子は、1つのオペランドに対してそのオペランドを1つずつ増減させる演算を施す。その式の演算子がインクリメント演算子でもデクリメント演算子でもない場合には、処理は、後に説明するテストステップ3320(第33B図)に進む。逆に、式の演算子がインクリメント演算子またはデクリメント演算子である場合には、処理はテストステップ3317からステップ3318に進む。
ステップ3318においては、実行エンジン802(第8図)が、論理流れ図2900(第29図)に従ってそのオペランドを評価し、演算cを適用する。演算cを適用するのは、このオペランドが計算において使用されるからである。上述のように、演算cをそのオペランドに対して適用することにより生じた何らかのエラーは検出され通知される。
処理はステップ3318(第33B図)からステップ3319に進み、ここでは式のインクリメント演算子またはデクリメント演算子が用いられて、その式の評価が行われる。ステップ3319は、上述のステップ3308(第33A図)に類似している。ステップ3319(第33B図)の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
“Not”演算子
上述のように、式の演算子がインクリメント演算子でもデクリメント演算子でもない場合には、処理はテストステップ3317(第33B図)からテストステップ3320に進む。テストステップ3320においては、実行エンジン802(第8図)が、その式を表す式構造体のフィールド“kind”2202(第22図)と“Not”演算子を指定するデータとを比較することによって、C言語の“Not”演算子と式の演算子とを比較する。“Not”演算子は、ただ1つのオペランドに対して演算を施し、そのオペランド自体をブール値項目として取り扱って、オペランドの論理的否定のブール値に対応する値を有する項目を生成する。ブール値項目は、真若しくは偽の何れかのブール値に対応する値を有する項目である。
その式の演算子が“Not”演算子でない場合は、処理はテストステップ3320(第33B図)からテストステップ3323に進む。逆に式の演算子が“Not”演算子である場合は、処理はテストステップ3320から3321に進む。ステップ3321においては、実行エンジン802(第8図)が、論理流れ図2900(第29図)に従ってオペランドの評価を行うとともに、演算pを施す。演算pを施すのは、このオペランドは真理値計算を行うのに使用されるからである。上述のように、オペランドに演算pを適用することによって生じたエラーは全て通知される。
処理はステップ3321(第33B図)からステップ3322に進み、ここで“Not”演算子がその式を評価するのに用いられる。ステップ3322は、前に説明したステップ3308(第33A図)に類似している。ステップ3322(第33B図)の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
“And”演算子及び“Or”演算子
上述のように、式の演算子が“NOt”演算子でない場合には、処理はテストステップ3320(第33B図)からテストステップ3323に進む。テストステップ3323において、実行エンジン802(第8図)は、その式を表現する式構造体のフィールド“kind”2202(第22図)と、“And”演算子及び“Or”演算子を指定するデータとを比較することによって、C言語の“And”演算子及び“Or”演算子と式の演算子との比較を行う。この“And”演算子及び“Or”演算子は2つのオペランド、即ちlhs及びrhsに演算を施し、そのオペランドをブール値項目として取り扱ってそのブール値の論理積及び論理和のブール値を有する項目を生成する。
式の演算子が“And”演算子でも“Or”演算子でもない場合には、処理はテストステップ3323(第33B図)からテストステップ3327に進む。逆に、式の演算子が“And”演算子または“Or”演算子である場合には、処理はテストステップ3323からステップ3324に進む。
ステップ3324においては、実行エンジン802(第8図)が論理流れ図2900(第29図)に従って、その式のlhsをその式そのものとして評価するとともに、演算pを施す。ステップ3324(第33B図)から、処理はステップ3325に進み、ここでは実行エンジン802(第8図)が、論理流れ図2900(第29図)に従って式のrhsの評価を行うと共に、演算pを適用する。演算pは式のlhs及びrhsの双方に施されるが、これはそれぞれが真理値計算に用いられるからである。このような真理値計算において式のlhsまたはrhsの何れかを不適切に用いた場合には、上述のように演算pを適用することによってエラーメッセージが生成される。
処理はステップ3325(第33B図)からステップ3326に進み、ここで式の“And”演算子または“Or”演算子が用いられて、式の評価が行われる。ステップ3326は、前述のステップ3308(第33A図)に類似している。ステップ3326(第33B図)の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
複合演算子
上述のように、式の演算子が“And”演算子でも“Or”演算子でもない場合には、処理はテストステップ3323(第33B図)からテストステップ3327に進む。テストステップ3327において、実行エンジン802(第8図)は、その式の演算子が複合演算子(compound operator)であるか否かを、その式を表現する式構造体のフィールド“kind”2202(第22図)と、複合演算子を指定するデータとを比較することによって判定する。C言語においては、複合演算子、即ちカンマ(“,”)は、2つのオペランド、即ち式のlhs及びrhsに対して演算を施し、演算結果としてrhsを評価した値を有する項目を生成する。つまり、2つのオペランドは個別に評価され、式のrhsを評価した値が式の値になるのである。
式の演算子が複合演算子でない場合には、処理はテストステップ3327(第33C図)からテストステップ3330に進む。逆に、式の演算子が複合演算子である場合には、処理はテストステップ3327からステップ3328に進む。ステップ3328においては、実行エンジン802(第8図)が論理流れ図200(第29図)に従って式のlhsをその式として評価し、また演算の適用は行わない。ステップ3328(第33C図)から処理はステップ3329に進みここでは実行エンジン802(第8図)が論理流れ図2900(第29図)に従って式のrhsを評価する。論理流れ図2900に従って、複合演算子を含む式を評価する場合に施される演算は、ステップ3329(第33C図)においてrhsを評価するときにrhsに施される演算と類似したものである。式のrhsの評価によって生成される項目は、その式そのものの項目として戻される。
ステップ3329(第33C図)の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
間接演算子
上述のように、式の演算子が複合演算子でない場合には、処理はテストステップ3327(第33C図)からテストステップ3330に進む。テストステップ3330においては、実行エンジン802(第8図)がその式の演算子が間接演算子(indirection operator)であるか否かを、その式を表す式構造体のフィールド”kind”2202(第22図)と間接演算子を指定するデータとを比較することによって判定する。C言語においては、間接演算子(即ち“*”)は1つのオペランドに対して演算を施し、演算結果としてメモリ、即ちメモリ104(第1図)内のそのオペランドによって指定されたアドレスに格納された値を有する項目を生成する。例えば、式“*a”は、メモリ内のアドレス“a”に格納された値を有する項目に対して評価を行う。間接演算子は、配列の要素を参照するためにも使用され得る。例えば、宣言“int array[10]”によって規定された配列の第2要素は、“array[1]”または“*(array+1)”の何れかによって特定され得る。後者の式はその配列の要素のサイズのアレイの始まる点を0としたときの相対位置(オフセット)1に格納された値、即ち配列の第2要素の値を参照するものである。
その式の演算子が間接演算子でない場合には、処理はテストステップ3330(第33C図)からテストステップ3335に進む。逆に、その式の演算子が間接演算子である場合には、処理はテストステップ3330からテストステップ3331に進む。
テストステップ3331においては、実行エンジン802(第8図)がそのオペランドが配列であるか否かを判定する。式構造体2220(第22図)がそのオペランドを表現している場合には、フィールド“type”2204(第22図)がそのオペランドの型を指定する型構造体を指定する。例えば、型構造体1612(第17図)が式構造体2220のフィールド“type”2204(第22図)によって指定されている場合には、実行エンジン802(第8図)がそのオペランドが配列であるか否かを、フィールド“kind”1702(第17図)と配列を指定するデータとを比較することによって判定する。
そのオペランドが配列である場合には、処理はテストステップ3331(第33C図)からステップ3332に進み、ここで実行エンジン802(第8図)が、論理流れ図2900(第29図)に従って、そのオペランドをその式として評価し、かつ演算は適用しないが、その演算子を、配列の間接アドレス指定手段として、即ち上述の“*(array+1)”の型式の配列の1つの要素の参照要素として取り扱う。逆に、オペランドが配列でない場合には、処理はテストステップ3331(第33C図)からステップ3333に進み、ここでは実行エンジン802(第8図)が、論理流れ図2900(第29図)に従ってその式のオペランドを評価するとともに、演算iを適用し、かつその演算子を間接アドレスを指定するポインタ、即ち上述の式“*a”のためのものとして取り扱う。
処理はステップ3332(第33C図)またはステップ3333の何れかから、ステップ3334に進み、ここでは実行エンジン802(第8図)が、そのオペランドをデリファレンスする。オペランドのデリファレンスにおいて、その式の評価により、オペランドが指定する項目が生成される。オペランドが項目を指定していない場合には、式はNULLに評価される。ステップ3334(第33C図)の終了後、論理流れ図2902に従って、即ちステップ2902(第29図)に従って処理は終了する。
コンポーネント参照演算子
上述のように、式の演算子が間接演算子でない場合には、処理はテストステップ3330(第33C図)からテストステップ3335に進む。テストステップ3335においては、実行エンジン802(第8図)が、その式の演算子がコンポーネント参照演算子であるか否かを、その式を表現する式構造体のフィールド“kind”2202(第22図)と、コンポーネント参照演算子を指定するデータとを比較することによって判定する。C言語においては、コンポーネント参照演算子(即ち、“.”または“−>”)は、2つのオペランド、即ち式のlhs及びrhsに対して演算を施し、演算結果としてrhsによって特定されたlhsのフィールド項目を生成する。例えば、宣言“struct{int a;char *b}c,*d”が宣言しているのは、項目“c”及び第2項目に対するポインタ“d”であって、それぞれが、データの型“int”、即ち整数の第1フィールド項目“a”及びデータの型が”char”、即ち文字であるデータを指定する第2フィールド項目“b”を有することを表している。式“c.a”は、項目“c”の整数フィールド項目に評価される。同様に、式“d−>a”は、ポインタ“d”が指定する項目の整数フィールド項目に評価される。
式の演算子がコンポーネント参照演算子でない場合には、処理はテストステップ3335(第33C図)からテストステップ3338(第33D図)に進む。逆に式の演算子がコンポーネント参照演算子である場合には、処理はテストステップ3335(第33C図)からステップ3336に進む。
ステップ3336においては、実行エンジン802(第8図)が論理流れ図2900(第29図)に従って式のlhsを評価し、また演算は施されない。ステップ3336(第33C図)から、処理はステップ3337に進み、ここで実行エンジン802(第8図)が、式のrhsによって指定されるフィールドを検索する。ステップ3337の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
配列参照演算子
上述のように、式の演算子がコンポーネント演算子でない場合には、処理はテストステップ3335(第33C図)からテストステップ3338(第33D図)に進む。テストステップ3338においては、実行エンジン802(第8図)が式の演算子が配列参照演算子であるか否かを、その式を表現する式構造体のフィールド“kind”2202(第22図)と、配列参照演算子を指定するデータとの比較を行うことによって判定する。C言語においては、配列参照演算子(即ち、“ [] ”)は2つのオペランド、即ちその式のlhs及びrhsに対して演算を施し、演算結果として、rhsにより特定されるlhsの配列の要素を生成する。例えば、宣言“int array[10]”が宣言するのは10個の整数の配列である。式“array[b]”は、配列“array”の、項目“b”によって指定される位置に存在する整数の要素に評価される。項目“b”、即ちrhsは指標(index)と称されることもある。
C言語においては、配列参照演算子は、非配列ポインタからのオフセットを参照するためにも使用されうる。例えば、“datum”が型“int”である変数である場合、式“datum[2]”は、メモリ104(第1図)の、変数“datum”を表現する項目から2メモリ位置だけオフセットした位置に格納されたデータに評価される。2メモリ位置は、変数“datum”の型、即ちデータの型“int”の変数2つ分の長さに等しい。上述のように、C言語の文脈においては、“array[i]”及び“*(array+i)”は、等価な式である(Cスタンダードのセクション6.3、2.1参照)。
その式の演算子が配列参照演算子でない場合は、処理はテストステップ3338(第33D図)からテストステップ3344に進む。逆に、この式の演算子が配列参照演算子である場合には、処理はテストステップ3338からステップ3339に進む。
ステップ3339において、実行エンジン(第8図)は、論理流れ図2900(第29図)に従ってその式の指標を評価し、また演算cを施す。演算cを施すのは、この指標は計算に使用されるからである。ステップ3339(第33D図)から、処理はテストステップ3340に進み、ここで実行エンジン802(第8図)が、lhsが配列であるか否かを、テストステップ3331(第33C図)に関連して前に説明した方法で判定する。lhsが配列である場合には、処理はテストステップ3340(第33D図)からステップ3341に進み、ここで実行エンジン802(第8図)が、論理流れ図2900(第2図)に従ってその式のrhsを指標として評価する。このとき演算は施されない。逆に、lhsが配列でない場合には、処理はテストステップ3340(第33D図)からステップ3342に進み、ここで実行エンジン802(第8図)が論理流れ図2900(第29図)に従ってその式のrhsをポインタとして評価し、かつ演算iを施す。
処理はステップ3341(第33D図)若しくはステップ3342の何れかから、ステップ3343に進み、ここで実行エンジン802(第8図)が、lhsによって特定された配列の、rhsによって指定された要素を検索する。ステップ3343(第33D図)の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
アドレス演算子
上述のように、その式の演算子が配列演算子でない場合には、処理はテストステップ3338(第33D図)からテストステップ3344に進む。テストステップ3344においては、実行エンジン802(第8図)が、その式の演算子がアドレス演算子であるか否かを、その式を表現する式構造体のフィールド“kind”2202と、アドレス演算子を指定するデータとを比較することによって判定する。C言語においては、アドレス演算子(即ち“&”)は、1つのオペランドに対して演算を施し、演算結果としてオペランドのアドレスがその値である項目を生成する。例えば、式“&a”は、メモリ、即ちメモリ104内の、項目“a”が格納されているアドレスをその値とする項目に評価される。
その式の演算子がアドレス演算子でない場合には、処理はテストステップ3344(第33D図)からテストステップ3347に進む。逆に、その式の演算子がアドレス演算子である場合には、処理はテストステップ3344からステップ3345に進む。
ステップ3345においては、実行エンジン802(第8図)が、論理流れ図2900(第29図)に従ってオペランドの評価を行い、このとき他の演算は施されない。ステップ3345(第33D図)から、処理はテストステップ3346に進み、ここでこのアドレス演算子はその式を評価するのに使用される。つまり、オペランドのアドレスが決定されるのである。ステップ3346は、前に説明したステップ3308(第33A図)に類似している。ステップ3346(第33D図)が終了した後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
関数の呼出し
上述のように、式の演算子がアドレス演算子でない場合には、処理はテストステップ3344(第33D図)からテストステップ3347に進む。テストステップ3347においては、実行エンジン802(第8図)が、その式の演算子が関数の呼出しであるか否かを、その式を表現する式構造体のフィールド“kind”2202(第22図)と、関数の呼出しを指定するデータとを比較することによって判定する。C言語においては、関数演算子(即ち“ () ”)は、関数の呼出しを意味する。関数の呼出しは、その関数の戻り値項目(returned item)に評価される。例えば、式“abc () ”は、識別子が“abc”である関数の実行の呼出しを行う。同様に、式“xyz(d,e,f)”は、その識別子が“xyz”である、パラメータとして項目d,e,及びfが与えられている関数を呼び出す。
式の演算子が関数の呼出しでない場合には、処理はテストステップ3347(第33E図)からテストステップ3353に進む。逆に、式の演算子が関数の呼出しである場合には、処理はテストステップ3347からループステップ3348に進む。ループステップ3348、ステップ3349、及びNEXTステップ3350は、各パラメータの評価が行われるループを形成する。ステップ3349において、実行エンジン802(第8図)は、論理流れ図2900(第29図)に従ってパラメータの評価を行い、また演算は施さない。式構造体2200(第22図)がその式を表現している場合、即ち関数の呼出しを表現している場合には、(i)フィールド“num operands”2208が呼び出された関数のパラメータの数をオペランドの数として特定し、(ii)フィールド“operands”2210は式構造体の配列であり、この式構造体の各要素は呼び出された関数のパラメータを表現する式構造体である。
各パラメータの評価は、フィールド“operands”2210の各要素を評価することによって行われる。呼び出された関数のパラメータを表現する項目の配列は、ループステップ3348、ステップ3349、及びNEXTステップ3350によって形成されたループにおいて構成され、後に(第46図に関連して)詳しく説明するように、ステップ3352において呼び出された関数の実行をエミュレートするのに用いられる。
ひとたび呼出される関数の各パラメータが評価されると、処理はループステップ3348(第33E図)からステップ3351に進む。ステップ3351においては、実行エンジン802(第8図)が、呼び出される関数のエクスターナルにおける実行の効果を表現する関数モデル構造体を抽出する。関数モデル構造体は、第11図〜第13図に関連して以前に説明してある。ステップ3351(第33E図)から、処理はステップ3352に進み、ここで実行エンジン802(第8図)が、呼び出される関数の実行のエミュレートについては後に詳しく説明する。ここで簡単に説明すると呼び出される関数のエミュレートは、上述の関数モデル(3)、(4)、及び(5)から形成される関数モデル構造体のような関数モデル構造体において特定された演算を適用することによって行われる。この呼び出された関数に対応する関数モデル構造体は、呼び出される関数のエクスターナルにおける実行の効果を表現する演算を特定する。ステップ3352(第33E図)の終了後、処理は、論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
代入
上述のように、その式の演算子が関数の呼出しでない場合には、処理はテストステップ3347(第33E図)からテストステップ3353に進む。テストステップ3353においては、実行エンジン802(第8図)が、その関数の演算子が代入演算子であるか否かをその式を表現する式構造体のフィールド“kind”2202(第22図)と、代入演算子を指定するデータとを比較することによって判定する。C言語においては、代入演算子(即ち“=”)が、2つのオペランド、即ちlhs及びrhsに対して演算を施し、rhsの値をlhsに移す。代入は、移された値を有する項目に評価される。例えば、式“a=b”は、項目“b”の値を項目“a”に移し、項目“a”の新たな値を有する項目に評価される。
式の演算子が代入演算子でない場合には、処理は論理流れ図2902(第33A図〜第33E図)に従って、即ちステップ2902(第29図)に従って終了し、論理流れ図2902に従って処理された式はNULL値に評価される。NULL値は、有効値を持たない状態を示すために用いられる。
実行エンジン802(第8図)が、コンピュータプロセス内におけるサブジェクト関数の実行の文脈を欠いた式を適切に評価することができない場合には、式はNULL値に評価される。最も重要なことは、式の適切な評価ではなく、エクスターナル及びリソースのそれぞれの状態変化を追跡することである。この状態の正確な追跡を確実に行うために、式の評価はできる限り多く行われる。
逆にテストステップ3353(第33E図)において、式の演算子が代入演算子である場合には、処理はステップ3353に進む。ステップ3353においては、実行エンジン802(第8図)は、論理流れ図2900(第29図)に従って式のlhsを式そのものとして評価し、また演算は施さない。ステップ3354(第33E図)から、処理はステップ3355に進み、ここでは実行エンジン802(第8図)が論理流れ図2900(第29図)に従って式のrhsの評価を行い、また演算は施さない。処理はステップ3355(第33E図)からステップ3356に進み、ここでは実行エンジン802(第8図)が、式のrhsの評価によって生成された項目の値を、式のlhsの評価によって生成された項目に代入する。ステップ3356については、後に論理流れ図3356(第45図)に関連して詳細に説明する。ステップ3356(第33E図)の終了後、処理は論理流れ図2902に従って、即ちステップ2902(第29図)に従って終了する。
従って、ステップ2804(第28図)においては、実行エンジン802(第8図)が、論理流れ図2900(第29図)に従って式の評価を行う。式の評価においては、実行エンジン802(第8図)が、前に詳しく説明したように、必要な場合にはエクスターナル及びリソースのそれぞれの状態に対して演算を施す。前に説明したように、式の評価に際しての演算の適用によって生ずる何らかの状態違反は、コンピュータプログラム610におけるエラーとしてユーザに通知される。
宣言の処理
上述のように、宣言ステートメントはステップ2808(第28図)において処理されるが、このステップ2808は論理流れ図2808(第34図)において詳細に示されている。宣言ステートメントは、そのフィールド“pointers”が2つのポインタの配列であるステートメント構造体1416(第21図)のようなステートメント構造体によって表現される。上述のように、宣言ステートメントを表現するステートメント構造体の第1ポインタは宣言構造体1506(第16図)のような宣言構造体を指定し、第2ポインタは宣言された項目の初期値を定める式構造体を指定する。
論理流れ図2808(第34図)に従った処理は、ステップ3402から開始され、このステップでは、項目構造体2700(第27図)のような項目構造体が、宣言された項目に対して生成される。生成された項目構造体を指定するポインタは、宣言構造体1506のフィールド“item”1608(第16図)に格納される。フィールド“type code”2712(第27図)は、宣言において指定されたデータの型に従って、即ち宣言構造体1506のフィールド“type”1606(第16図)に従って設定される。フィールド“initialized”2714(第27図)は、その項目が初期化されていないことを示すように設定される。
処理はステップ3402(第34図)からテストステップ3404に進み、ここでは実行エンジン802(第8図)が、その宣言ステートメントが、宣言された項目のための初期値を特定しているか否か、即ちその宣言ステートメントを表現するステートメント構造体の第2ポインタが、式構造体を指定しているか、若しくはNULL値であるか判定する。その宣言ステートメントが初期値を特定していない場合、即ち第2ポインタがNULLである場合には、処理は論理流れ図2808(第34図)に従って終了する。逆にその宣言ステートメントが初期値を特定する式を含んでいる場合、即ち第2ポインタが式ステートメントを指定している場合には、処理はテストステップ3404からステップ3406に進む。ステップ3406において、実行エンジン802(第8図)が、前に詳しく説明したように、論理流れ図2900(第29図)に従って式構造体によって表現された式を評価する。論理流れ図2900に従って式を評価するときに、演算は施されない。処理はステップ3406(第34図)からステップ3408に進み、そこでは式が評価される項目が宣言された項目に代入される。更に、宣言された項目を表現する項目構造体のフィールド“initialized”2714(第27図)が、その項目が初期化されたことを示すように設定される。
ステップ3408(第34図)の終了後、処理は論理流れ図2808に従って、ステップ2808(第28図)に従って終了する。従ってステップ2808においては、新たな項目が生成され、初期値が指定される場合にはその新たな項目がその初期値を有するように初期化される。
決定処理
上述のように、実行エンジン802(第8図)は、ステップ2812(第28図)において決定処理、即ち“if”ステートメントの処理を行う。ここでステップ2812は論理流れ図2812(第35図)に詳細に示されている。処理はステップ3502から開始されるが、ここでは実行エンジン802(第8図)が、論理流れ図2900(第29図)に従って“if”ステートメントの述語である式の評価を行い、演算pを適用する。C言語においては、“if”ステートメントは述語及び第2ステートメントを含む。
この述語は、第2ステートメントが実行されるか否かを決定するブール値項目に評価される式である。例えば、ステートメント“if(a==b)ci1;”は、述語、即ち式“a==b”が、その値が“true”であるブール値項目に評価される場合、即ち項目“a”が項目“b”と等しい場合にのみ、第2ステートメント、即ちステートメント“ci1”が実行されるという処理を特定する。項目“a”が項目“b”と等しくない場合には、ステートメント“ci1”は実行されない。
処理はステップ3502(第35図)からテストステップ3504に進み、ここでは実行エンジン802(第8図)が、述語が既知の値を有する項目に評価されたか否かを判定する。実行エンジン802はこの判定を、述語の評価によって生成された項目とNULLとを比較することによって行う。上述のように、実行エンジン802が式の評価を適切に行うことができない場合には、式はNULLに評価される。述語が既知の値を有する項目に、即ちNULL以外の値に評価された場合には、処理はテストステップ3504(第35図)からテストステップ3506に進む。
テストステップ3506においては、実行エンジン802(第8図)が、述語の評価によって生成された項目の値とブール値“true”とを比較する。述語が、“true”のブール値を有するブール値項目に評価された場合には、処理はステップ3508(第35図)に進み、そこで実行エンジン802(第8図)が、“if”ステートメントの第2ステートメントを実行する。第2ステートメントは、前に詳しく説明したように論理流れ図2800(第28図)に従って処理される。従って、論理流れ図2800は再帰的に実行される。
一方、述語がブール値“false”に評価された場合には、処理はテストステップ3506(第28図)からステップ3510に進み、そこで実行エンジン802(第8図)が、後に詳述する制御レコードに、“else”条件を示すデータを格納する。制御レコードは、後に詳しく述べるように、サブジェクト関数の実行のエミュレーションにおいて適切な制御の流れを与えるために用いられる。ステップ3508(第35図)またはステップ3510の何れかが終了した後、処理は論理流れ図2812に従って、即ちステップ2812(第28図)に従って終了する。
上述のように、実行エンジン802(第8図)は、テストステップ3504(第35図)において、述語が既知の値に評価された否かを判定する。述語が既知の値に評価されない場合、即ちNULLに評価されるか、未知の値を有する項目に評価された場合には、処理はテストステップ3504からステップ3512に進む。ステップ3512において、実行エンジン802(第8図)はその述語が評価されうるブール値をシミュレートする。一実施例においては、実行エンジン812(第8図)は、ブール値“true”またはブール値“false”からランダムに選択を行う。ステップ3512(第35図)から、処理はステップ3514に進む。
ステップ3514において、実行エンジン802(第8図)は、ステップ3512(第35図)において選択されたブール値から、できる限り多くの推測を行う。例えば、述語が式“a&&b”(即ちa AND b)であり、述語がブール値“true”を有するように選択された場合、実行エンジン802(第8図)は、項目“a”と項目“b”の双方がブール値“true”を有するものと推測する。a及びbの双方が“true”の場合にのみ式“a&&b”が“true”と評価されることから、この推測がなされ得るのである。ステップ3514(第35図)は、論理流れ図3600(第36図)に従った述語の処理を含んでいる。論理流れ図3600の各ステップにおいては、その式の評価により得られることが推定される推定ブール値から、式における値を推測する。
“Not”演算子
論理流れ図3600に従った処理はテストステップ3602から開始されるが、このステップでは実行エンジン802(第8図)が、式の演算値と、“NOt”演算子との比較を行う、即ちその式を表現する式構造体のフィールド“kind”2202(第22図)と、“Not”演算子を特定するデータとの比較を行う。“Not”演算子(即ち“!”)は、1つのオペランドに対して演算を施し、演算結果としてそのオペランドの値の論理的否定をその値とする項目を生成する。例えば、式“!a”は、オペランド、即ち式“a”の否定である。
式の演算子が“Not”演算子でない場合には、処理はテストステップ3602(第36図)から、以下に説明するテストステップ3606に進む。逆に、式の演算子が“Not”演算子である場合には、処理はテストステップ3602からステップ3606に進む。ステップ3606においては、実行エンジン802(第8図)が論理流れ図3600(第36図)に従ってオペランドを処理し、推定値の論理的否定を推定する。例えば、式“!(a&&b)”、即ちNOT(a AND b)が、“true”と推定された場合には、式“a&&b”が“false”と推定される。即ち、論理流れ図3600に従った処理は再帰的に実行される。ステップ3604の終了後、処理は論理流れ図3600に従って終了する。
“And”演算子または“Or”演算子
上述のように、式の演算子が“Not”演算子でない場合には、処理はテストステップ3602(第36図)からテストステップ3606に進む。テストステップ3606においては、実行エンジン802(第8図)が、式の演算子と“And”演算子及び“Or”演算子とを比較する。つまり、実行エンジン802は、その式を表現する式構造体のフィールド“kind”2202(第22図)と“And”演算子及び“Or”演算子を指定するデータとの比較を行うのである。上述のように、“And”演算子及び“Or”演算子(即ち“&&”及び“||”)は、2つのオペランド、即ちlhs及びrhsに演算を施し、演算結果として2つのオペランドの値の論理積及び論理和をその値とする項目を生成する。式の演算子が“And”演算子でも“Or”演算子でもない場合には、処理はテストステップ3606(第36図)から、後に説明するテストステップ3610に進む。逆に、式の演算子が“And”演算子、若しくは“Or”演算子の何れかである場合には、処理はテストステップ3606からステップ3608に進む。
ステップ3608はテストステップ3702から処理が開始される論理流れ図3608(第37図)に詳細に示されている。テストステップ3702においては、実行エンジン802(第8図)が、式の演算子と、“And”演算子との比較を行い、更にその式が評価されたときに推定されるブール値、即ち推定値と、ブール値“true”とを比較する。演算子が“And”演算子でない場合、若しくは推定値が“true”でない場合には、処理はテストステップ3702から、後に説明するテストステップ3706に進む。逆に演算子が“And”演算子である場合、及び推定値が“true”である場合には、処理はテストステップ3702からステップ3704に進む。
ステップ3704においては、各オペランド、即ち式のlhs及びrhsのそれぞれが、論理流れ図3600(第36図)に従って推定値“true”と共に1つの式として処理される。このような、lhs及びrhsの双方が“true”であるという推測は適切である。というのは、式“a&&b”が“true”である場合には、両オペランド、即ち式“a”及び“b”が共に“true”でなければならないからである。上述のように、C言語の演算子“&&”は論理積演算を意味する。ステップ3704(第37図)の終了後、処理は論理流れ図3608に従って、即ちステップ3608(第36図)に従って終了する。
上述のように、演算子が“And”演算子でなく、若しくは推定値が“true”でない場合には、処理はテストステップ3702(第37図)からテストステップ3706に進む。テストステップ3706においては、実行エンジン802(第8図)が、式の演算子と“Or”演算子との比較を行い、かつ推定値とブール値“false”との比較を行う。式の演算子が“Or”演算子でなく、若しくは推定値が“false”でない場合には、処理は論理流れ図3608に従って、即ちステップ3608(第36図)に従って終了する。逆に、演算子が“Or”演算子であり、推定値が“false”である場合には、処理はテストステップ3706(第37図)からステップ3708に進む。
ステップ3708において、各オペランド、即ち式のlhs及びrhsのそれぞれが、論理流れ図3600(第36図)に従って、推定値“false”と共に1つの式として処理される。このようなlhs及びrhsが“false”であるという推測は適切である。というのは、表現“a||b”が“false”である場合には、両オペランド、即ち“a”及び“b”が共に“false”でなければならないからである。上述のように、C言語の演算子“||”は、論理和演算を意味する。ステップ3708(第37図)の終了後、処理は論理流れ図3608に従って、即ちステップ3608(第36図)に従って終了する。
ステップ3608の終了後、処理は論理流れ図3600に従って終了する。
関係演算子
上述のように、式の演算子が“And”演算子でも、“Or”演算子でもない場合には、処理はテストステップ3606からテストステップ3610に進む。テストステップ3610においては、実行エンジン802(第8図)が式の演算子と、以下の関係演算子との比較を行う。その関係演算子とは即ち、より小さい(“<”)、より小さいか若しくは等しい(“<=”)、より大きい(“>”)、より大きいか若しくは等しい(“>=”)、等しい(“==”)、及び等しくない(“!=”)という関係を表す演算子である。詳述すると、実行エンジン802は、その式を表現する式構造体のフィールド“kind”2202(第22図)と、これらの関係演算子のそれぞれを指定するデータとの比較を行うのである。上述のように、関係演算子は、2つのオペランド、即ち式のlhs及びrhsに演算を施し、演算結果として、lhsとrhsとの関係に対応する値を有するブール値項目を生成する。式の演算子が関係演算子でない場合には、処理はテストステップ3610(第36図)から、後に説明するテストステップ3614に進む。逆に式の演算子が関係演算子である場合には、処理はテストステップ3610からステップ3612に進む。
ステップ3612においては、実行エンジン802(第8図)が、関係演算子の評価を行う。ステップ3612(第36図)は、ステップ3802から処理が開始される論理流れ図3612(第38図)に詳細が示されている。ステップ3802においては、実行エンジン802(第8図)が、lhs及びrhsの双方が既知であるか否か即ち既知の値を有する項目に評価されるか否かということ、及びlhs及びrhsの双方が未知であるか否か、即ち未知の値を有する項目に評価されるか否かということを判定する。項目構造体2700(第27図)によって表現される項目が未知の値を有する場合には、フィールド“type code”2712が項目の型が未知であることを表す。式のlhs及びrhsの双方が既知であるか、若しくはその双方が未知の場合には、論理流れ図3612(第38図)に従って、即ちステップ3612(第36図)に従って処理が終了するが、これは両オペランドが既知である場合には推測すべきものがなく、また両オペランドが共に未知の場合には推測し得るものがないからである。逆に、オペランドの一方が既知で他方が未知の場合には、処理はテストステップ3802(第38図)からテストステップ3804に進む。
テストステップ3804においては、実行エンジン802(第8図)が、何れかのオペランドが未定義であるか否かを判定する。オペランドが項目構造体によって表現される項目に評価されない場合、即ちオペランドがNULL値に評価される場合にはそのオペランドは未定義である。このような場合においては、推測値を代入すべき項目は存在しない。従って、一方のオペランドが未定義である場合には、処理は論理流れ図3612(第38図)に従って、即ちステップ3612(第36図)に従って終了する。逆に両オペランドが共に定義されている場合には、処理はテストステップ3804(第38図)から、テストステップ3806に進む。
テストステップ3806においては、実行エンジン802(第8図)が、式の演算子と“=”演算子及び“not equal”演算子とを比較し、推定値とブール値“true”及び“false”との比較を行う。(i)式の演算子が“=”演算子であり、推定値が“true”であるという条件、及び(ii)式の演算子が“not equal”演算子であり、推定値が“false”であるという条件の双方が満たされていない場合には、処理はテストステップ3806(第38図)から後に説明するテストステップ3814に進む。逆に、(i)式の演算子が“=”演算子であり、推定値が“true”であるという条件、及び(ii)式の演算子が“not equal”演算子であり、推定値が“false”であるという条件の何れか一方が満たされている場合には、処理はテストステップ3806からテストステップ3808に進む。
テストステップ3808において、1つのオペランドが既知の値であり、他方のオペランドは未知の値を有する。その値が既知であるオペランドは、“known item”に評価される。その値が未知のオペランドは“unknown item”に評価される。更に、テストステップ3808においては、2つのオペランドが等しい値を有することが推定される。テストステップ3808において、実行エンジン802(第8図)は、既知の項目がNULL値を有するか否かを判定する。(i)項目の型が、フィールド“type code”で、即ち項目構造体2700のフィールド“type code”2712(第27図)で指定されているように、“long”または“pointer”の何れかであることを示しているという条件、及び(ii)項目の値が、フィールド“value”において、即ち項目構造体2700のフィールド“value”2706において指定されているように、0であることを示しているという条件の双方が満たされている場合には、その項目はNULL値を有する。
既知の項目がNULL値を有する場合、処理はテストステップ3808(第38図)からステップ3810に進み、ここでは演算xを施すことにより、その末知の項目に関連するリソースに、無効という印付けがなされる。リソースが関連付けられた未知の項目の値が、その値がNULLである既知の項目に等しいと推定されることから、即ちそのリソースに対するポインタがNULLであると推定されることから、このリソースは無効である。
処理はステップ3810からステップ3812に進む。更に、既知の項目がNULL値を有していない場合、処理はテストステップ3808から直接ステップ3812に進む。ステップ3812においては、既知の項目の値が、以下に詳しく説明する方法で未知の値に代入される。従って、既知の項目の値は、その既知の項目と未知の項目とが等しいと推定される場合には既知の項目の値から推測される。ステップ3812が終了した後、処理は論理流れ図3612に従って、即ちステップ3612(第36図)に従って終了する。
上述のように、(i)式の演算子が、“=”演算子であり、推定値が“true”であるという条件、及び(ii)式の演算子が“not equal”演算子であり、推定値が“false”であるという条件の双方が満たされない場合には、処理はテストステップ3806(第38図)からテストステップ3814に進む。テストステップ3814においては、実行エンジン802(第8図)が、(i)式の演算子が“not equal”演算子であり、推定値が“true”であるという条件を満たすか否か、または(ii)式の演算子が“=”演算子であり、推定値が“false”であるという条件を満たすか否かということを判定する。条件(i)も、条件(ii)も何れもが満たされない場合には、論理流れ図3612に従って、即ちステップ3612(第36図)に従って処理が終了する。そうでない場合、即ち条件(i)若しくは条件(ii)の何れかが満たされる場合には、処理はテストステップ3814(第38図)からテストステップ3816に進む。
テストステップ3816においては、実行エンジン802(第8図)が2つのオペランド、即ちlhs及びrhsが互いに等しくないことを推測する。既知の項目がNULL値を有する場合は、未知の項目が非NULL値を有するものと推測される。C言語の様々なインプリメンテーションの文脈においては、NULLは値ゼロである。従って、未知の項目は非ゼロ値を有するものと推測される。テストステップ3816(第38図)においては、実行エンジン802(第8図)が、既知の項目の値とNULLとを比較する。既知の項目の値がNULLでない場合には、処理は論理流れ図3612(第38図)に従って終了する。逆に、既知の項目の値がNULLである場合は、処理はテストステップ3818に進む。
テストステップ3818においては、実行エンジン802(第8図)が、状態Qにあるリソースが既知の項目に関連付けられているか否かを判定する。上述のように、リソースが割り当てられた状態、割り当てられていない状態、または無効状態の何れにあるかが未知である場合には、リソースは状態Qにある。未知の項目が状態Qにあるリソースと関連付けられている場合には、処理はステップ3820(第38図)に進み、ここでは演算aが、その未知の項目に関連付けられたリソースに対して施される。リソースが関連付けられている項目がNULLと等しくないことが推定されていることから、そのリソースを明確に割り当てられた状態におくために演算aが施されるのである。ステップ3820から、処理はステップ3822に進む。更に、未知の項目が状態Qにあるリソースに関連付けられていない場合には、処理はテストステップ3818から直接ステップ3822に進む。
ステップ3822において、その未知の項目を表現する項目構造体のフィールド“type code”2712(第27図)は、その項目が非ゼロであることを示すように設定され、その未知の項目を表現する項目構造体のフィールド“invalid pointer”2720は、その未知の項目は無効ポインタ、即ちNULL値を有していないことを示すように設定される。ステップ3822の終了後、処理は論理流れ図3612に従って終了する。
従って、論理流れ図3612に従って、即ちステップ3612(第36図)に従って、その式のオペランドの1つが既知であり、他のオペランドが既知ではなくかつ両オペランドが等しいと推定されるとき、若しくは等しくないと推定されているときには推測がなされる。推測がなされるのは、式の両オペランドの値、及び式のオペランドに関連するリソースの状態の双方についてである。ステップ3612が終了した後、処理は論理流れ図3600に従って終了する。
複合演算子
上述のように、式の演算子が関係演算子でない場合には、処理はテストステップ3610からテストステップ3614に進む。テストステップ3614において、実行エンジン802(第8図)は、式の演算子と複合演算子(“,”)とを比較する。つまり、実行エンジン802は、その式を表現する式構造体のフィールド“kind”2202(第22図)と、複合演算子を指定するデータとの比較を行うのである。複合演算子は2つのオペランド、即ちlhs及びrhsに対して演算を施す。ここでlhs及びrhsの双方が評価され、式はrhsに評価される。例えば、式“a,b”の評価では、オペランド“a”とオペランド“b”の双方の評価を行った上で、その式“a,b”が“b”に評価される。
演算子が複合演算子である場合には、処理はステップ3616(第36図)に進み、そこでは実行エンジン802(第8図)が論理流れ図3600(第36図)に従って処理を行い、推定値として、その式が評価されると推定されるブール値を与える。rhsの値はその式の推定値から推測されるが、これはその式がrhsに評価されるからである。例えば、式“a==b,c==d”が真であると推定される場合には、rhS、即ち式“c==d”が真であると推測される。従って、処理は論理流れ図3600に従って、ステップ3616において、処理は論理流れ図3600に従って再帰的に式のrhsに施される。ステップ3616の終了後、処理は論理流れ図3600に従って終了する。演算子が複合演算子でない場合には、処理は論理流れ図3600に従って終了し、またステップ3616はスキップされる。
従って、上述のように、論理流れ図3600に従って、式及び部分式(subexpression)を再帰的に処理することによって、実行エンジン802(第8図)はステップ3514(第35図)において実行し得る限り、式の推定値から式の項目及び式の項目に関連するリソースについての推測を行う。処理はステップ3514から前に説明したテストステップ3506に進む。
従って、ステップ2812(第28図)において、論理流れ図3812に従い、実行エンジン802(第8図)は“if”ステートメントにおける決定処理を行うのである。
戻り(return)処理
上述のように“return”ステートメントはステップ2816において処理される。“return”ステートメントは、関数の実行を終了し、戻り値項目が定義されている場合にはその関数の戻り値項目に値を代入する。ステップ2816は、処理がステップ3902から開始される論理流れ図2816(第39図)に詳細に示されている。
ステップ3902においては、実行エンジン802(第8図)が、“return”ステートメントが戻り値項目を指定しているか否かを判定する。“return”ステートメントは、それが式を含んでいる場合、戻り値項目を指定する。例えば、ステートメント構造体1416(第21図)が“return”ステートメントを表現している場合、フィールド“pointers”2110は、式構造体を指定する1つのポインタであるか、若しくはNULLである。“return”ステートメントが戻り値項目を指定する場合、即ちフィールド“pointers”2110が式構造体を指定している場合、処理はテストステップ3902(第39図)からステップ3904に進み、ここで“return”ステートメントの式が論理流れ図2900(第29図)に従って評価され、また、演算は施されない。ステップ3904(第39図)において“return”ステートメントの式の評価において演算は施されないが、前にステップ3306、3307、及び3314(第33A−B図)に関連して説明したように、式のオペランドに何らかの演算を施すことも可能である。ステップ3904(第39図)から処理はステップ3906に進み、ここでは実行エンジン802(第8図)がステップ3904(第39図)において式の評価を行うことにより生成された項目を、サブジェクト関数の戻り値項目を表現する項目構造体に代入する。1つの項目の値を他の項目に代入することに関しては、後に詳しく説明する。
処理はステップ3906からステップ3908に進む。また、“return”ステートメントが戻り値項目を指定していない場合、処理はテストステップ3902から、直接ステップ3908に進む。ステップ3908においては、実行エンジン802(第8図)が、後に詳述するように、サブジェクト関数の実行のエミュレーション時においてサブジェクト関数を通る制御の流れを与えるのに用いられる“return”条件を示すデータを、制御レコード内に格納する。ステップ3908(第39図)の後、処理は論理流れ図2816に従って、即ちステップ2816(第28図)に従って終了する。
ブロック処理
多くの関数においては、ステートメントのブロックがその関数の挙動を規定する。C言語の文脈においては、ステートメントのブロックは、それ自体がブロックステートメントである始め括弧、即ち“{”と、終わり括弧、即ち“}”との間に囲まれている。C言語で規定される関数においては、一般的にステートメントの第1ブロックが、ステートメントの第2ブロックを含んでいる。例えば、ソースコードの抜粋(1)は、第9行から第32行までのステートメントの第1ブロックの中に、第16行から第21行までのステートメントの第2ブロックを含んでいる。このとき、ステートメントの第1ブロックは、ステートメントの第2ブロックのスーパーブロックであり、ステートメントの第2ブロックはステートメントの第1ブロックのサブブロックである。
(p118b)上述のように、ブロックステートメント構造体は、ステートメント構造体、即ちブロックの第1ステートメントを表現しているステートメント構造体1416(第21図)を指定するポインタを含む。ステートメント構造体1416は上述のように、ブロックの各ステートメントを表現するステートメント構造体のシングルリンクトリスト(singly−linked list)を維持するために用いられるフィールド“next”2106を含んでいる。上述のように、ブロックステートメントはステップ2820(第28図)において処理され、このステップ2820については論理流れ図2820(第40図)にその詳細が示されており、またこの論理流れ図は、ブロックのステートメントの実行のエミュレーションのためのステートメントのブロックのステートメント構造体の処理について説明している。
ステップ4002においては、実行エンジン802(第8図)が、ブロックの第1ステートメントを表現するステートメント構造体を検索する。上述のように、そのブロックの第1ステートメントを表現するステートメント構造体は、ブロックステートメント構造体によって指定されている。検索されたステートメント構造体は、現ステートメント構造体である。処理はステップ4002(第40図)からテストステップ4004に進む。
テストステップ4004においては、実行エンジン802(第8図)が、現ステートメント構造体が“else”ステートメントを表現し、制御レコードが“else”条件を示しているか否かを判定する。C言語においては、“else”ステートメントは周知のものであり、Cスタンダードのセクション6.6.4.1に記載されている。ステートメント構造体1416(第21図)のようなステートメント構造体は、フィールド“kind”2102がそのように示されている場合には、“else”ステートメントを表現する。制御レコードは、サブジェクト関数の実行のエミュレーション時に制御の流れを管理するために、実行エンジン802(第8図)内部に維持されている。ステップ3510(第35図)に関連して上述したように、制御レコードは、“if”ステートメントを処理するときに“else”条件を示すように設定され、“if”ステートメントの述語がブール値“false”に評価される。現ステートメント構造体が“else”ステートメントを表現していないか、若しくは制御レコードが“else”条件を示していない場合には、処理はテストステップ4004(第40図)からステップ4006に進み、ここで現ステートメント構造体によって表現されたステートメントの実行が、上述のように、論理流れ図2800(第28図)に従ってエミュレートされる。逆に、現ステートメント構造体が“else”ステートメントを表現し、かつ制御レコードが“else”条件を示していない場合には、処理はテストステップ4004(第40図)から以下に説明するステップ4014に進み、ステップ4006はバイパスされる。
上述のように、ステートメントのブロックはサブブロックを含み得る。また、上述のように、ステップ4006において、現ステートメント構造体は論理流れ図2800(第28図)に従って処理される。従って、ステップ4006(第40図)におけるブロックステートメントの実行により、ステップ2820(第28図)が再帰的に実行され、従って論理流れ図2820(第40図)の各ステップが再帰的に実行されて、サブブロックの実行のエミュレーションが行われる。ひとたびサブブロックのステートメントが論理流れ図2820に従って処理されると、ステップ4006におけるブロックステートメントの処理は完了し、サブブロックのステートメントの論理流れ図2820に従った処理は続行される。処理はステップ4006からテストステップ4008に進む。
テストステップ4008においては、実行エンジン802(第8図)が、論理流れ図2800(第28図)に関連して上述したようにステートメントの実行のエミュレーションによって設定された制御レコードと、“return”条件、“exit”条件、若しくは“long jump”条件を示すデータとの比較を行う。ステップ3908(第39図)に関連して上述したように、ステップ4006(第40図)において、“return”ステートメントの実行のエミュレーション時に、制御レコードは“return”条件を示すように設定される。“exit”条件は、実行エンジン802(第8図)がライブラリ関数exit () の呼出しを処理するときに生起する。このライブラリ関数exit () については、Cスタンダードのセクション7.10.4.3に記載されている。“long jump”条件は、実行エンジン802が、ライブラリ関数longjmp () の呼出しを処理するときに生起する。このライブラリ関数longjmp () についてもCスタンダードのセクション7.6.2.1に記載されている。制御レコードが“return”条件、“exit”条件、若しくは“long jump”条件を示している場合、処理は論理流れ図2820(第40図)に従って終了する。逆に制御レコードが“return”条件、“exit”条件、若しくは“long jump”条件の何れをも示していない場合には、処理はテストステップ4008からテストステップ4010に進む。
テストステップ4010においては、実行エンジン802(第8図)が制御レコードと、“break”条件若しくは“continue”条件を示すデータとを比較する。ステップ4006においては、“break”ステートメント若しくは“continue”ステートメントの実行のエミュレーション時に、制御レコードが、“break”条件若しくは“continue”条件を示すように設定される。制御レコードが“break”条件若しくは“continue”条件の何れかを示すように設定されている場合、処理はステップ4010に進み、ここで制御レコードが“next”条件を示すように設定され、処理は論理流れ図2820に従って終了する。
“next”条件は、ブロックの“next”ステートメントの実行がエミュレートされる場合の一般的な処理条件である。従って、ステートメントの現ブロックが“break”ステートメントを実行する場合、現ブロックの処理は終了し、処理は現ブロックのスーパーブロック内における現ブロックのすぐ後に続くステートメントに進む。これに対して、“return”条件は制御レコードをリセットしない。従って、ステップ4006における“return”ステートメントの実行のエミュレーションにより、上述のテストステップ4008を通した現ブロックの処理が終了し、同様に現ブロックの任意のスーパーブロックの処理も終了する。
一方、制御レコードが“break”条件も“continue”条件をも示していない場合には、処理はテストステップ4010からステップ4014に進む。更に、現ステートメント構造体が“else”ステートメントを表現し、制御レコードが上述のように“else”条件を示していない場合には、処理はテストステップ4004から4014に進む。ステップ4014においては、実行エンジン802(第8図)が現ステートメント構造体のフィールド“next”2106(第21図)を検索し、検索されたステートメント構造体を現ステートメント構造体にして、前の現ステートメント構造体を置き換える。
処理はステップ4014(第40図)からテストステップ4016に進み、ここで実行エンジン802(第8図)が、現ステートメント構造体を指定するポインタとNULLとを比較する。現ステートメント構造体を指定するポインタがNULLである場合には、ステートメントのブロックの最終ステートメントが論理流れ図2820(第40図)に従って処理され、処理は論理流れ図2820に従って終了する。逆に、現ステートメント構造体を指定するポインタがNULLでない場合には、処理はテストステップ4016から、上述のテストステップ4004に進む。
従って、ステートメントのブロックの実行はエミュレートされ、ステートメントのブロックにおける制御の流れは、論理流れ図2820(第40図)の通りに流れることになる。
リークの処理
上述のように、サブジェクト関数のステートメントの実行がひとたびエミュレートされると、ステップ2606(第26図)においてリーク(leak)が検出される。ステップ2606については論理流れ図2606(第41図)にその詳細が示されている。処理は論理流れ図2606に従ってループステップ4102から開始される。ループステップ4102及びNEXTステップ4106はループを形成し、その中でサブジェクト関数の各エクスターナルがステップ4104に従って処理される。ステップ4104においては、実行エンジン802(第8図)が、エクスターナルが到達可能な全てのリソースに印付けを行う(mark)。そのリソースがエクスターナル若しくはそのエクスターナルを含む集群(bunch)における任意の項目に関連付けられている場合には、そのリソースはエクスターナルが到達可能なリソースである。集群については以下に詳しく説明する。説明のための例を用いると、集群型である配列の要素が関連付けられたリソースは、その配列の任意の要素から到達可能である。
リソース、即ちリソース状態構造体3100(第31図)によって表現されたリソースは、リソース状態構造体3100のフィールド“mark”3114を、それを示すように設定することによって印付けされる。各エクスターナルによって到達可能な各リソースが、ループステップ4102(第41図)及びNEXTステップ4106によって形成されたループの中でひとたび印付けされると、処理はループステップ4106からループステップ4108に進む。
ループステップ4108及びNEXTステップ4114はループを形成し、その中で各リソースが処理される。リソース状態構造体は、シングルリンクトリストに保持されて、全てのリソース状態構造体の処理を容易にする。例えば、リソース状態構造体3100(第31図)は、シングルリンクトリストにおける次のリソース状態構造体を指定するフィールド“next”3112を含む。メモリ104(第1図)内のリソース状態構造体によって表現される各リソースに対して、処理はループステップ4108(第41図)からテストステップ4110に進む。
テストステップ4110においては、実行エンジン802(第8図)がリソースが割り当てられているか否か、即ち状態A若しくは状態Qにあるか否かということと、印付けされていないことを判定する。割り当てられ印付けされていないリソース、即ちどのエクスターナルからのも到達可能でないリソースはリークとなっている。そのリソースが割り当てられており、かつ印付けされていない場合には、処理はテストステップ4110(第41図)からステップ4112進み、ここでそのリークがユーザに対して通知される。そのリソースが割り当てられていない、即ち状態A若しくは状態Qにないか、若しくは印付けされている場合には、処理はテストステップ4110から直接NEXTステップ4114に進む。
更に、処理はステップ4112からNEXTステップ4114へ進む。処理はNEXTステップ4114からループステップ4108に進み、そこで次のリソースが上述のように処理されて、最終的に全てのリソースが処理される。ひとたび各リソースが、ループステップ4108及びNEXTステップ4114が形成するループにおいて処理されると、論理流れ図2606に従って、即ちステップ2606(第26図)に従って処理は終了する。
従って、リークは検出され、論理流れ図2606に従って、即ちステップ2606(第26図)に従ってそれが通知される。
エクスターナルの構築
上述のように、ステップ2608において、各エクスターナルは論理流れ図4200(第42図)に従って構築される。論理流れ図4200に従った処理はテストステップ4202から開始されるが、このステップでは実行エンジン802(第8図)が、リソースがそのエクスターナルに関連付けられているか否かを判定する。説明のための例を用いて、エクスターナルリスト構造体1414(第15図)によって表現されたエクスターナルにリソースが関連付けられているか否かの判定について以下説明する。フィールド“first decl”1502は宣言構造体1506を指定しこの宣言構造体はフィールド“item”1608を含む。フィールド“item”1608は項目構造体、即ち項目構造体2700を指定する。項目構造体2700のフィールド“resource”2702(第27図)が、リソース状態構造体を指定している場合、エクスターナルリスト構造体1414(第15図)によって表現されたエクスターナルに、リソースは関連付けられている。そのエクスターナルが、例えばリソース状態構造体3100(第31図)によって表現されたリソースに関連付けられている場合、処理はテストステップ4202(第42図)からステップ4204に進み、ここでフィールド“state”3102(第31図)が検索される。処理はステップ4204から後に説明するステップ4210に進む。
そのエクスターナルに関連付けられたリソースが存在しない場合には、処理はテストステップ4202からテストステップ4206に進む。テストステップ4206においては、実行エンジン802(第8図)が、そのエクスターナルが無効ポインタであるか否かを判定する。エクスターナルの型は、そのエクスターナルに関連付けられた項目を表現する項目構造体のフィールド“type code”2712(第27図)によって指定され、それがVALUE TYPE NON ZEROである場合には、そのエクスターナルは無効ポインタである。型VALUE TYPE NON ZEROは、その項目がゼロ以外の値を有することを示す。そのエクスターナルに関連付けられた項目を表す項目構造体のフィールド“invalid pointer”、即ちフィールド“invalid pointer”2720がそれを示している場合、若しくはそのエクスターナルの値が0または−1である場合には、エクスターナルは無効ポインタである。
そのエクスターナルが無効ポインタである場合には、処理はテストステップ4204(第42図)からステップ4208に進み、そこでそのエクスナルのリソースの状態が状態Xに設定される。例えば、リソース状態構造体3100(第31図)によって表現されるリソースが、エクスターナル状態構造体3000(第30図)によって表現されたエクスターナルに関連付けられている場合には、状態Xを示すデータがフィールド“state”3102(第31図)に格納される。ステップ4208(第42図)から、処理はステップ4210に進む。更に、処理は、上述のようにステップ4204から、またエクスターナルが無効ポインタでない場合にはテストステップ4206からステップ4210に進む。
ステップ4210において、エクスターナルの複合RS,DK及びCP状態が更新される。状態の構成は、第3B図、第4B図、及び第5B図に関連して前に詳しく説明した。ステップ4210の終了後、処理は論理流れ図4200に従って終了する。
関数の出力モデル
上述のように、モデルマシン808(第8図)は、ステップ2412(第24図)においてサブジェクト関数のモデルを生成し、格納する。ステップ2412は、論理流れ図2412(第43図)に詳細が示されている。処理はステップ4302から開始され、ここではモデルマシン808(第8図)が関数モデル構造体1100(第11図)のような関数モデル構造体の割り当て及び初期化を行う。関数モデル構造体1100は、一実施例においては、フィールド“name”1102、フィールド“description”1108、フィールド“file”1110、フィールド“line”1112、及びフィールド“automated”1106に、(i)その挙動が関数モデル構造体1100においてモデル化されている関数の識別子、(ii)その関数のテキスト形式の記述、(iii)ソースコードファイルの名称、(iv)その関数が定義されているソースコードファイル内の行、及び(v)その関数が自動的にモデル化されたことを示すブール値を、それぞれ格納することによって初期化される。その関数のモデルがモデルマシン808(第8図)によって生成された場合、関数は自動的にモデル化される。逆に、その関数のモデルが、ユーザがコンピュータ100(第1図)においてテキストエディタ(図示せず)を用いて生成した場合、その関数は手動でモデル化される。例えば、ライブラリ関数fopen () 、malloc () 、及びfree () は、手動でモデル化される。関数モデル構造体のフィールド“automated”1116(第11図)は、上述のようにステップ904(第9図)においてモデル記述ファイル604(第6図)から呼出しを行うが、これはその関数が手動でモデル化されるということを示すように設定される。
ステップ4306(第43図)から、処理はステップ4304に進み、そこでエクスターナルモデル構造体1200(第12図)のようなエクスターナルモデル構造体が、そのサブジェクト関数の各エクスターナルに対して生成され、対応する関数モデル構造体におけるエクスターナルモデル構造体のシングルリンクトリストに挿入される。例えば、フィールド“first external”1104(第11図)及びフィールド“last external”1106は、上述のように、関数モデル構造体1100をエクスターナルモデル構造体のシングルリンクトリストに関連付けるために用いられる。1つのエクスターナルの処理は、ステップ4304(第43図)に従って行われるが、この処理は論理流れ図4400(第44図)に示されている。
論理流れ図4400に従って処理はステップ4402から開始されるが、ここではモデルマシン808(第8図)がエクスターナルの型、即ちそのエクスターナルがパラメータであるか、戻り値項目であるか、若しくは項目(即ち、グローバルに定義された項目またはスタティックな項目)であるかを判定する。そのエクスターナルがパラメータである場合は、モデルマシン808(第8図)はサブジェクト関数の定義におけるパラメータの位置を判定する。第1パラメータはパラメータ番号ゼロであって、最終パラメータの番号は、サブジェクト関数に対して定義されたパラメータ番号より1小さい。そのエクスターナルが項目である場合には、モデルマシン808(第8図)がその項目の識別子を判定する。そのエクスターナルの型は、上述のように、エクスターナルモデル構造体1200(第12図)のフィールド“type”1204に格納される。同様に、パラメータ番号が求められた場合には、そのパラメータ番号はフィールド“parameter number”1206に格納され、項目が求められた場合には、その項目の識別子がフィールド“name”1208に格納される。
処理はステップ4402からステップ4404に進み、そこではモデルマシン808(第8図)が演算の数を判定し、サブジェクト関数の実行のエミュレーション時に、エクスターナルに施される特定の演算を判定する。ステップ4404においては、演算及び施される演算の数が、そのエクスターナルの複合DK状態に従って判定される。次の表Nはエクスターナルの複合DK状態から導かれる演算及び演算の数の概要を示したものである。
従って、例えば、エクスターナルの複合DK状態が状態Aである場合には、サブジェクト関数の実行のエミュレーションにおいて、演算aがそのエクスターナルに施される。また、エクスターナルの複合DK状態が状態KQである場合には、サブジェクト関数の実行のエミュレーションにおいて、演算kが、次いで演算mがそのエクスターナルに施される。上述のように、エクスターナルの複合状態は、サブジェクト関数の実行の多重エミュレーションの包括的な効果を特定する。従って、エクスターナルの複合状態から導かれた演算は、そのエクスターナル上でのサブジェクト関数の実行の累積的な効果のエッセンス(distillation)を表現する。
処理は、ステップ4404からテストステップ4406に進み、このステップではモデルマシン808(第8図)がそのエクスターナルに施される演算の数とゼロとの比較を行う。そのエクスターナルに施される演算の数がゼロに等しい場合には、処理はステップ4408(第44図)に進み、そこでモデルマシン808(第8図)が演算の数を判定し、かつそのエクスターナルの複合CP状態に従ったサブジェクト関数のエミュレーション時にそのエクスターナルに施される特定の演算を求める。以下の表Oは、エクスターナルの複合CP状態から導かれる演算及び演算の数の概要を示したものである。
従って、エクスターナルの複合DK状態による、そのエクスターナル上でサブジェクトルーチンの実行の累積的効果に関する情報の供給が不十分である場合には、そのエクスターナルの複合CP状態が、サブジェクト関数の実行の累積的効果を判定するために用いられる。説明のための例を用いると、エクスターナルの複合CP状態が状態iである場合、サブジェクト関数のエミュレーションにより、そのエクスターナルに演算iが施される。ステップ4404(第44図)またはステップ4408の何れかにおいて、そのエクスターナルに施される演算及び演算の数は、エクスターナルモデル構造体120のフィールド“operations”1214(第12図)及び“number of operations”1212にそれぞれ格納される。処理はステップ4408(第44図)からステップ4410に進む。更に、モデルマシン808(第8図)が、テストステップ4406(第44図)において、ステップ4404において判定されたようにそのエクスターナルに施される演算の数がゼロであると判定された場合、処理はテストステップ4406から直接ステップ4410に進む。
ステップ4410において、モデルマシン808(第8図)は、エクスターナルの複合RS状態から、そのエクスターナルに関連付けられたリソースの初期状態を判定する。つまり、そのエクスターナルの複合RS状態はエクスターナルモデル構造体1200のフィールド“initial state”1220(第12図)に格納される。エクスターナルの複合RS状態は、そのエクスターナルに関連するリソースに対するサブジェクト関数の実行の累積的効果を反映する。処理はステップ4410(第44図)からテストステップ4412に進み、そこではモデルマシン808(第8図)が、そのエクスターナルの複合RS状態と、そのエクスターナルに関連付けられたリソースが存在しないことを示す状態NONEとを比較する。そのエクスターナルに関連付けられたリソースが存在しない場合、処理はテストステップ4412(第44図)からステップ4414に進み、そこではブール値“false”が、エクスターナルモデル構造体1200のフィールド“new resource”1218(第12図)に格納される。逆に、リソースがそのエクスターナルに関連付けられている場合、即ちそのエクスターナルの複合RS状態がNONE以外である場合には、処理はテストステップ4412(第44図)からステップ4416に進む。ステップ4416においては、モデルマシン808(第8図)がエクスターナルモデル構造体1200のフィールド“new resource”1218(第12図)にブール値“true”を格納し、サブジェクト関数のエミュレーションにより、そのエクスターナルに関連付けられた新たなリソースが生成されることを示すようにする。
処理はステップ4414(第44図)、若しくはステップ4416の何れかからステップ4418に進み、そこでモデルマシン808(第8図)がエクスターナルモデル構造体1200(第12図)を、関数モデル構造体1100(第11図)のエクスターナルモデル構造体のシングルリンクトリストに挿入する。ステップ4418(第44図)の終了後、処理は論理流れ図4400に従って終了する。
サブジェクト関数の各エクスターナルが、ステップ4304(第43図)において論理流れ図4400に従って処理された後、処理はステップ4306に進み、そこでサブジェクト関数を表現する関数モデル構造体1100(第11図)が、全ての関数モデル構造体を含むデータ構造体に格納される。ステップ4306の終了後、処理は論理流れ図2412に従って、即ちステップ2412(第24図)に従って終了する。次いで、格納された関数モデル構造体によって表現される関数モデルが、後に詳しく説明するようにサブジェクト関数の呼出しを行う他の関数の解析を行うときに、そのサブジェクト関数の実行をエミュレートするために用いられ得る。
1つの項目の値の他の項目への代入
上述のように、ステップ3356(第33C図)において、一方の項目、即ちrhsの値が、他の項目、即ちlhsに代入される。ステップ3356は、論理流れ図3356(第45図)に詳細に示されており、ここでは、処理がテストステップ4502から開始される。テストステップ4502においては、実行エンジン802(第8図)が、lhs及びrhsが項目によって表現されるか否かを判定する。上述のように、lhs若しくはrhs等の式は、実行エンジン802がその式を評価するための十分な情報を有している場合には1つの項目に評価され、他の場合にはNULL値に評価される。式が1つの項目に評価される場合には、その式はその項目によって表現される。lhs若しくはrhsの何れかが1つの項目によって表現されない場合には、処理はテストステップ4502(第45図)から、以下に説明するテストステップ4510に進む。逆にlhs及びrhsの双方が1つの項目によって表現される場合には、処理はテストステップ4502からステップ4504に進む。
ステップ4504においては、lhsを表現する項目、即ちlhs項目のフィールドが、それに対応するrhsを表現する項目、即ちrhs項目と等しく形成される。詳述すると、項目構造体2700フィールド“resource”2702(第27図)、“external”2704、“value”2706、“type code”2712、“initialized”2714、及び“invalid pointer”2720のそれぞれに対応するrhs項目のフィールドに、データが格納される。処理はステップ4504(第45図)からテストステップ450に進み、そこでは実行エンジン802(第8図)がrhsが初期化されたか否かを判定する。実行エンジン802は、項目構造体270のフィールド“initialized”2714(第27図)に対応するrhs項目のフィールドと、ブール値“false”とを比較することによってこのような判定を行う。
rhsが初期化されている場合、即ち項目構造体2700のフィールド”initialized”2714(第27図)に対応するrhs項目のフィールドが、ブール値“true”を有している場合には、処理は論理流れ図3356に従って、即ちステップ3356(第33E図)に従って終了する。逆に、rhsが初期化されていない場合、即ち項目構造体2700のフィールド“initialized”2714(第27図)に対応するrhs項目のフィールドがブール値“false”を有している場合には、処理はテストステップ4506(第45図)からステップ4508に進む。ステップ4508においては、上述のようにエラーメッセージがエラーログファイル及び/若しくはビデオモニタ118上のディスプレイに対して送られて、初期化されていないデータが使用されていることを警告する。フィールド“initialized”2714(第27図)に対応するrhs項目のフィールドが、上述のようにステップ4504において、lhs項目における対応するフィールドにコピーされることから、lhs項目も初期化されていない旨の印付けがなされる。ステップ4508(第45図)が終了した後、論理流れ図3356に従って、即ちステップ3356(第33C図)に従って処理を終了する。
上述のように、lhs若しくはrhsの何れかが項目によって表現されない場合、処理はテストステップ4502(第45図)からテストステップ4510に進む。テストステップ4510において、実行エンジン802(第8図)が、rhs及びlhsが個別に項目によって表現されるか否かを判定する。lhsが項目によって表現され、rhsが項目によって表現されない場合には、処理はテストステップ4510(第45図)からステップ4512に進む。そうでない場合には、処理は論理流れ図3356に従って、即ちステップ3356(第33E図)に従って終了する。
ステップ4512(第45図)においては、実行エンジン802(第8図)がlhs項目に未知なものとしての印付けを行う。例えば、項目構造体2700(第27図)がlhs項目を表現する場合、実行エンジン802(第8図)が、(i)未知のデータの型を示すデータをフィールド“type code”2712(第27図)に格納し、(ii)lhs項目が初期化されていないことを示すデータをフィールド“initialized”2714に格納し、(iii)lhsがエクスターナル若しくはリソースと関連付けられていないことを示すべくフィールド“resource”2702及び“external”2704にNULLを格納することによって、lhsが既知でないという印付けを行う。 ステップ4512(第45図)の終了後、処理は論理流れ図3356に従って、即ちステップ3356(第33E図)に従って終了する。
従って、ステップ3356において、rhsを表現する項目の値はlhsを表現する項目に代入される。上述のように、実行エンジン802(第8図)が、ステップ3408(第34図)において式の評価によって得られた項目の値を、宣言された項目に代入する。ステップ3408におけるこの代入は、論理流れ図3356(第45図)に関連して上述したrhs項目のlhs項目への代入に類似したものである。ステップ3812(第38図)に関して上述したように、実行エンジン802(第8図)は、既知の項目の値を未知の項目に代入する。ステップ3812(第38図)における既知の項目の未知の項目への代入は、論理流れ図3356に関連して上述したrhs項目のlhs項目への代入に類似したものである。上述のように、実行エンジン802(第8図)は、ステップ3906(第39図)において、式の評価によって得られた項目の値を戻り値項目に代入する。ステップ3906におけるこの代入処理は、論理流れ図3356(第45図)に関連して上述したrhs項目のlhs項目への代入に類似したものである。
関数のエミュレーション
上述のように、実行エンジン802(第8図)は、ステップ3352(第33E図)において、関数の実行をエミュレートすることにより関数の呼出しを評価する。ステップ3352は論理流れ図3352(第46図)に詳細に示されており、ここでは処理がステップ4602から始まる。関数の呼出しの実行は、呼び出される関数の挙動を表現する関数モデル構造体に従ってエミュレートされる。テストステップ4602において、実行エンジン802(第8図)は、呼び出される関数の挙動を表現する関数モデル構造体が、メモリ104(第1図)に格納されているか否かを判定する。上述のように、関数モデル構造体1100(第11図)は、関数モデル構造体1100によってその挙動が表現される関数の識別子を表現するデータを含んでいるフィールド“name”1102を有する。各関数の挙動を表現する様々な関数モデル構造体の対応するフィールドは、サブジェクト関数がその関数を呼び出すのに用いる識別子と比較されるが、これは全ての関数モデル構造体がチェックされるまでか、若しくはそのフィールド“name”が識別子と一致する関数モデル構造体が見つけられるまで続けて行われる。
その識別子と一致するフィールド“name”を有する関数モデル構造体が見つけられない場合は、処理はテストステップ4602(第46図)からステップ4604に進み、ここで呼び出される関数の実行のエミュレーションを評価することによって得られる項目としてNULLが生成される。上述のように、実行エンジン802(第8図)がその式を適切に評価するための十分な情報を有していないときには、式はNULLに評価される。ステップ4604(第46図)の実行の後、処理は論理流れ図3352に従って、即ちステップ3352(第33E図)に従って終了する。
一方、そのフィールド“name”が、サブジェクト関数が関数の呼出しに用いる識別子と一致するような関数モデル構造体、即ち呼び出される関数の挙動を表現する関数モデル構造体が見つけられた場合には、処理はテストステップ4602(第46図)からループステップ4606に進む。ループステップ4606及びNEXTステップ4630はループを形成し、このループの中で呼び出される関数の関数モデル構造体内のエクスターナルモデル構造体によって表現される各エクスターナルが処理される。第13図に関連して以前に説明したように、関数モデル構造体1100Aのような関数モデル構造体は、エクスターナルモデル構造体のシングルリンクトリストにおける第1エクスターナルモデル構造体を指定するフィールド“first external”1104Aを含む。呼び出される関数の関数モデル構造体のエクスターナルモデル構造体のシングルリンクトリストの各エクスターナルに対して、処理はループステップ4606(第46図)からテストステップ4608に進む。呼び出される関数の関数モデル構造体のエクスターナルモデル構造体のシングルリンクトリストの各エクスターナルが処理された後、処理はループステップ4606から以下に詳細に説明するステップ4632に進む。
以下のステップ4608〜4628の説明の文脈においては、ループステップ4606及びNEXTステップ4630により形成されるループに従って処理されるエクスターナルモデル構造体には、エクスターナルモデル構造体の処理の説明のための例として、エクスターナルモデル構造体1200(第12図)を用いるものとする。テストステップ4608(第46図)において、実行エンジン802(第8図)が、エクスターナルモデル構造体1200(第12図)がパラメータを表現しているか否かを、フィールド“type”1204とパラメータを示すデータとを比較することによって判定する。エクスターナルモデル構造体1200がパラメータを表現していない場合には、処理はテストステップ4608(第46図)から以下に詳細に説明するテストステップ4612に進む。逆に、エクスターナルモデル構造体1200(第12図)がパラメータを表現している場合には、処理はテストステップ4608(第46図)からステップ4610に進む。
ステップ4610においては、実行エンジン802(第8図)がそのパラメータを表現する項目を検索する。ループステップ3348(第33E図)、ステップ3349、及びNEXTステップ3350に関連して以前に説明したように、実行エンジン802(第8図)は、呼び出される関数のパラメータを表現する項目の配列を含む。エクスターナルモデル構造体1200(第12図)によって表現される特定のパラメータは、フィールド“parameter number”1206において指定される。処理はステップ4610(第46図)から、後に詳述するテストステップ4616に進む。
上述のように、エクスターナルモデル構造体1200(第12図)がパラメータを表現していない場合、処理はテストステップ4608からテストステップ4612に進む。テストステップ4612(第46図)において、実行エンジン802(第8図)が、エクスターナルモデル構造体1200(第12図)が変数を表現しているか否かを、フィールド“type”1204内に格納されたデータと変数が表現されていることを示すデータとを比較することによって判定する。エクスターナルモデル構造体1200(第12図)が変数を表現していない場合には、処理はテストステップ4612(第46図)から後に詳述するテストステップ4616に進む。逆に、エクスターナルモデル構造体1200(第12図)が変数を表現している場合には、処理はテストステップ4612からステップ4614に進む。
ステップ4614において、実行エンジン802(第8図)は、エクスターナルモデル構造体1200(第12図)によって表現される変数の評価を行う。実行エンジン802(第8図)は、その変数の項目を検索することによってその変数を評価する。サブジェクト関数を表現する関数構造体内において宣言構造体、即ち宣言構造体1506(第16図)が、エクスターナルモデル構造体1200(第12図)によって表現される変数を表現する。エクスターナルモデル構造体1200はその変数の識別子をフィールド“name”1208に格納することによって、表現された特定の変数を識別する。エクスターナルモデル構造体1200及び宣言構造体1506(第16図)が同じ変数を表現している場合には、フィールド“name”1604に格納された識別子はフィールド“name”120(第12図)に格納された識別子と同じものである。宣言構造体1506の“item”1608が指定する項目構造体2700を検索することによってその変数の評価が行われる。処理はステップ4614(第46図)からテストステップ4616に進む。
上述のように、エクスターナルモデル構造体1200(第12図)がパラメータ及びも変数の何れも表現していない場合、即ちエクスターナルモデル構造体1200が呼び出される関数の戻り値項目を表現している場合には、処理はテストステップ4616からテストステップ4612に進む。また、処理はステップ4616(第46図)の他ステップ4614からもテストステップ4616に進み得る。テストステップ4616において、実行エンジン802(第8図)は、エクスターナルモデル構造体1200(第12図)によって表現されるエクスターナルを表現する項目が定義されているか否かを判定する。項目が定義されているのは、(i)ステップ4610においてエクスターナルモデル構造体1200によって表現されるパラメータの値が定義され、既知の値に評価されて、かつ処理がステップ4616(第46図)を通って進む場合、または(ii)ステップ4614においてエクスターナルモデル構造体1200(第12図)によって表現される変数が初期化され、処理がステップ4614(第46図)を通って進む場合である。
エクスターナルモデル構造体1200(第12図)よって表現されるエクスターナルを表現する項目が定義されない場合には、処理はテストステップ4616(第46図)から後に説明するテストステップ4624に進む。逆に、このようなアイテムが定義される場合には、処理はテストステップ4616からループステップ4618に進む。
ループステップ4618及びNEXTステップ4622はループを形成し、このループの中でエクスターナルモデル構造体1200のフィールド“operations”1214(第12図)に格納された各演算が処理される。上述のように、フィールド“operations”1214に格納された演算の数は、フィールド“num operations”1212に記録される。フィールド”operations”1214に格納された各演算に対して処理はループステップ4618(第46図)からステップ4620に進み、ここでステップ2906(第29図)に関連して上述したのと同じ方法で、エクスターナルモデル構造体1200(第12図)によって表現されるエクスターナルに演算が施される。エクスターナルに演算を施すことによって何らかのエラーが検出されたときには、それが上述の方法と同様にプログラミングエラーとしてユーザに通知される。ステップ4620(第46図)から、処理はNEXTステップ4622を通してループステップ4618に進み、ここでフィールド“operations”1214(第12図)に格納された次の演算があれば、それが処理される。フィールド“operations”1214に格納された全ての演算がループステップ4618(第46図)及びNEXTステップ4622によって定義されたループに従ってひとたび処理されると、処理はテストステップ4624に進む。
テストステップ4624において、実行エンジン802(第8図)は、エクスターナルモデル構造体1200(第12図)が、エクスターナルモデル構造体1200によって表現されるエクスターナルの代わりに新たなリソースが生成されることを指定するか否かを判定する。実行エンジン802(第8図)は、フィールド“new resources”1218(第12図)とブール値“true”とを比較することによってこの判定を行う。フィールド“new resources”1218が“false”である場合には、処理はテストステップ4624(第46図)からNEXTステップ4630を通って、ループステップ4606に進み、ここで次のエクスターナルが、上述のようにループステップ4606及びNEXTステップ4630により定義されるループに従って処理される。逆に、フィールド“new resources”1218(第12図)が“true”である場合には、処理はテストステップ4624(第46図)からステップ4626に進む。
ステップ4626において、実行エンジン802(第8図)は、(i)項目構造体、即ち項目構造体2700(第27図)及びリソース状態構造体、即ちリソース状態構造体3100(第31図)を生成してメモリ104(第1図)に格納し、(ii)リソース状態構造体3100(第31図)を指定するポインタをフィールド“resource”2702に格納して、リソース状態構造体3100と項目構造体2700(第27図)とを関連付けることによって、新たなリソースを生成する。処理はステップ4626(第46図)からステップ4628に進み、ここで実行エンジン802(第8図)が、新たな項目と、エクスターナルモデル構造体1200(第12図)によって表現されるエクスターナルとを連係させる。エクスターナルリスト構造体1414(第15図)がそのエクスターナルを表現している場合には、フィールド“first decl”1502(第15図)が指定する宣言構造体1506のフィールド“item”1608(第16図)に、項目構造体2700(第27図)を指定するポインタを格納することによって、その項目がそのエクスターナルに関連付けられる。
エクスターナルが呼び出される関数の戻り値項目である場合には、論理流れ図3352(第46図)の始めにNULLに設定されていた結果レコードが新たな項目に設定される。エクスターナルモデル構造体1200のフィールド“type”1204(第12図)がそれを示している場合には、そのエクスターナルは、呼び出される関数の戻り値項目である。エクスターナルが呼び出される関数の戻り値項目でない場合には、そのエクスターナルを表現する宣言構造体1506のフィールド“item”1608(第16図)に新たな項目が形成される。
ステップ4628(第46図)から、処理はNEXTステップ4630を通ってループステップ4606に進み、ここで上述のように次のエクスターナルが処理される。呼び出される関数を表現する関数モデル構造体1100のフィールド“first external”1104(第11図)及び“last external”1106によって指定されるエクスターナル構造体のシングルリンクトリストにおけるエクスターナル構造体によって表現されるエクスターナルの全てがひとたび処理されると、処理はループステップ4606(第46図)からステップ4632に進む。ステップ4632において、実行エンジン802(第8図)は、呼び出される関数の実行のエミュレーションにより評価して得られた項目として、結果レコードを生成する。ステップ4628(第46図)に関連して上述したように、この結果レコードはNULL値に初期化され、戻り値項目の代わりに新たなリソースが生成された場合には、戻り値項目の値に設定される。
ステップ4632の終了後、処理は論理流れ図3352に従って、即ちステップ3352(第33E図)に従って終了する。従って、呼び出される関数の挙動を表現するモデルを用いることによって、サブジェクト関数内に呼び出される関数に対する呼出しを評価して、呼び出される関数の実行のリソース及びエクスターナルに対する評価を解析することができる。
メモリの集群
C言語に特有なことの1つは、あるメモリを、連続して割り当てられた配列として取り扱うことができるという点である。C言語においては、以下の型のメモリは、そのメモリが連続したブロックとして割り当てられたメモリであるかのようにアクセスすることができる。そのようなメモリの型とは、
(i)命令“struct”若しくは“array”を用いて定義された変数若しくはパラメータの何れかのデータ、即ち複合データ構造体若しくは配列、
(ii)関数にパラメータとして渡される任意のポインタ、
(iii)C言語において定義されている関数calloc () 若しくは関数malloc () の実行により割り当てられるメモリである。
本発明のここに開示する実施例では、連続して割り当てられるメモリをモデル化するためにメモリの集群(bunch)を用いている。
項目構造体は集群、即ち項目構造体の連続的な配列に割り当てられる。1つの整数、若しくは浮動小数点型変数の宣言を表現する項目構造体の場合についてのみ述べると、この集群は1つの項目構造体を含む。複合型の変数、即ち“struct”型及び配列の変数を表現する項目構造体の集群は、多重項目構造体によって表現され、その項目構造体のそれぞれは、4バイトの配列若しくは複合データの変数である。
連続して割り当てられた項目構造体を有する連続して割り当てられたメモリを表現することにより、いくつかのイリーガルな配列参照の検出が可能となる。例えば、連続して割り当てられた項目構造体の集群の範囲内の項目構造体に対する参照は、その集群によって表現される配列のイリーガルな指標(index)に対応する。更に、項目構造体を集群に形成することにより、上述のようなメモリのリークの検出も単純化される。例えば、集群とされた何らかの項目構造体が関数のエクスターナルによって到達可能な場合は、集群における各項目構造体にも、その関数のエクスターナルが到達可能である。
上述のように、項目構造体2700(第27図)は、フィールド“first in bunch”2708、フィールド“size of bunch”2710、フィールド“head in bunch”2716、及びフィールド“known bunch size”2718を含む。フィールド“first in bunch”2708は、項目構造体2700を含む項目構造体の集群における第1の項目構造体を指定するポインタである。項目構造体2700が1つの集群における第1の項目構造体である場合には、フィールド“first in bunch”2708が項目構造体2700を指定する。フィールド“size of bunch”2710は、項目構造体2700を含む集群における項目構造体の数を示す。一実施例においては、フィールド”size of bunch”2710が、所与の集群における第1の項目構造体においてのみ、そのように定義されている。
フィールド“head in bunch”2716は、項目構造体2700が、その項目構造体2700を含む集群における第1項目構造体であるか否かを示すフラグである。フィールド“known bunch size”2718は、項目構造体2700を含む集群が既知のサイズを有するか否かを示すフラグである。集群が未知のサイズを有するのは、例えば、(i)その集群が動的に割り当てられる、即ち関数malloc () を呼び出すことによって割り当てられ、かつ実行エンジン802(第8図)が、要求されるメモリの量を計算するのに十分な情報を有していないとき、(ii)その集群がサブジェクト関数に引き渡される場合、若しくは(iii)その集群がその集群の各項目の追跡が実際には無理であるようなサイズを有する場合である。フィールド“known bunch size”2718によって示されることにより集群が未知のサイズを有している場合には、実行エンジン802(第8図)は、その集群の範囲を逸脱する違反のチェックは行わない。
付録A(Appendix A)のコンピュータプログラムは、一実施例として、米国カリフォルニア州マウンテンビュー(mountain view)のサンマイクロシステムズ社(Sun Microsystems)から入手可能なSun Sparcstation(登録商標)コンピュータシステムのようなワークステーションに備えられている、UNIXオペレーティングシステムSunOS4.1.3、コンパイラ、及びリンカーを用いてコンパイルされ、リンクされた。第2実施例では、付録Aのコンピュータプログラムは、マイクロソフト社のVisual C++1.5コンパイラを用いてコンパイルされ、同社のVisual C++リンカーを用いてリンクされた。これらのコンパイラ及びリンカーは、米国ワシントン州レッドモンド(Redmond)のマイクロソフト社から発売されており、同様に同社から発売されているMS DOS6.2オペレーティングシステム、Microsoft Windows(登録商標)3.1を用いているパーソナルコンピュータ上で用いられる。使用できるパーソナルコンピュータには、米国カリフォルニア州サンフランシスコのAtman社製のArt4000Sなどがある。付録Aのコンピュータプログラムが適合する特定のコンピュータ言語や、付録Aのコンピュータプログラムにより定義されたコンピュータプロセスが実行されるコンピュータシステムは、本発明における重要な側面ではない。当業者は、本明細書の記載内容を参照することにより、異なるコンピュータ言語及び/若しくは異なるコンピュータシステムを用いて本発明を実現できよう。
付録Aには複数のソースコードファイルが含まれており、これには本発明の別の実施例に基づき複数の関数及びデータ構造を定義するソースコードファイル“readin.c”の2つの別の実施例が含まれている。ソースコードファイル“readin.c”の第1実施例は、付録Aのフレーム64〜74に現れており、ソースコードファイル“readin.c”の第2実施例は、付録Aのフレーム77〜87に現れている。ソースコードファイルの実施例の一方のみが付録Aの残りの部分とリンクされて、本発明の原理に基づくリソースチェッカを形成するということは理解されよう。
以上の記述は本発明を説明するためのものであり、本発明をこれに限定するものではない。例えば、ここに開示した実施例はC言語における関数を分析しているが、本発明の原理は、以上の記述の内容に限定されることなく、他のコンピュータ命令プロトコルにも適用され得る。更に、本発明を実現したコンピュータプログラムを何らかの媒体に格納して提供することも可能である。本発明の範囲は、請求の範囲によってのみ限定される。