Mercurial > hg > Papers > 2021 > anatofuz-master
view paper/chapter/04-interface.tex @ 44:786aea1c7cde
update
author | anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 01 Feb 2021 12:27:23 +0900 |
parents | a5e840dede1b |
children | af80bd950c79 |
line wrap: on
line source
\section{GearsOSのInterfaceの構文の改良} GearsOSのInterfaceでは、 従来はDataGearとCodeGearを分離して記述していた。 CodeGearの入出力をDataGearとして列挙する必要があった。 CodeGearの入出力として\texttt{\_\_code()}の間に記述したDataGearの一覧と、Interface上部で記述したDataGearの集合が一致している必要がある。 従来の分離している記法の場合、 このDataGearの宣言が一致していないケースが多々発生した。 またInterfaceの入力としてのDataGearではなく、 フィールド変数としてDataGearを使うようなプログラミングスタイルを取ってしまうケースも見られた。 GearsOSでは、 DataGearやフィールド変数をオブジェクトに格納したい場合、 Interface側ではなくImpl側に変数を保存する必要がある。 Interface側に記述してしまう原因は複数考えられる。 GearsOSのプログラミングスタイルに慣れていないことも考えられるが、構文によるところも考えられる。 CodeGearとDataGearはInterfaceの場合は密接な関係性にあるが、 分離して記述してしまうと「DataGearの集合」と「CodeGearの集合」を別個で捉えてしまう。 あくまでInterfaceで定義するCodeGearとDataGearはInterfaceのAPIである。 これをユーザーに強く意識させる必要がある。 golangにもInterfaceの機能が実装されている。 golangの場合はInterfaceは関数の宣言部分のみを記述するルールになっている。 変数名は含まれていても含まなくても問題ない。 \begin{lstlisting}[frame=lrbt,label=src:golang_interface,caption={golangのinterface宣言}] type geometry interface { area() float64 perim() float64 } \end{lstlisting} \section{Implementの型をいれたことによる間違ったGearsプログラミング} Implementの型を導入したが、 GearsOSのプログラミングをするにつれていくつかの間違ったパターンがあることがわかった。 自動生成されるStubCodeGearは、 goto metaから遷移するのが前提であるため、 引数をContextから取り出す必要がある。 Contextから取り出す場合は、 実装しているInterfaceに対応している置き場所からデータを取り出す。 この置き場所は\texttt{data}配列であり、 配列の添え字は\texttt{enum Data}と対応している。 また各CodeGearからgotoする際に、 遷移先のInterfaceに値を書き込みに行く。 Interfaceで定義したCodeGearと対応しているImplementのCodeGearの場合はこのデータの取り出し方で問題はない。 しかしImplementのCodeGearから内部でgotoするCodeGearの場合は事情が異なる。 内部でgotoするCodeGearは、 Javaなどのプライベートメソッドのように使うことを想定している。 このCodeGearのことをprivate CodeGearと呼ぶ。 privateCodeGearにgotoする場合、 goto元のCodeGearからは\texttt{goto meta}経由で遷移する。 goto metaが発行されるとStub Code Gearに遷移するが、現在のシステムではInterfaceから値をとってくることになってしまう。 \section{メタ計算部分の入れ替え} GearsOSでは次のCodeGearに移行する前のMetaCodeGearとして、 デフォルトでは\texttt{\_\_code meta}が使われている。 \texttt{\_\_code meta}はcontextに含まれているCodeGearの関数ポインタを、 enumからディスパッチして次のStub CodeGearに継続するものである。 例えばモデル検査をGearsOSで実行する場合、 通常のStub CodeGearのほかに状態の保存などを行う必要がある。 この状態の保存に関する一連の処理は明らかにメタ計算であるので、 ノーマルレベルのCodeGearではない箇所で行いたい。 ノーマルレベル以外のCodeGearで実行する場合は、 通常のコード生成だとStubCodeGearの中で行うことになる。 StubCodeGearは自動生成されてしまうため、 値の取り出し以外のことを行う場合は自分で実装する必要がある。 しかしモデル検査に関する処理は様々なCodeGearの後に行う必要があるため、 すべてのCodeGearのStubを静的に実装するのは煩雑である。 ノーマルレベルのCodeGearの処理の後に、StubCodeGear以外のMeta Code Gearを実行したい。 Stub Code Gearに直ちに遷移してしまう\texttt{\_\_code meta}以外のMeta CodeGearに、 特定のCodeGearの計算が終わったら遷移したい。 このためには、特定のCodeGearの遷移先のMetaCodeGearをユーザーが定義できるAPIが必要となる。 このAPIを実装すると、ユーザーが柔軟にメタ計算を選択することが可能となる。 GearsOSのビルドシステムのAPIとして\texttt{meta.pm}を作製した。 これはPerlのモジュールファイルとして実装した。 meta.pmはPerlで実装されたGearsOSのトランスコンパイラであるgenerate\_stub.plから呼び出される。 meta.pmの中のサブルーチンである\texttt{replaceMeta}に変更対象のCodeGearと変更先のMetaCodeGearへのgotoを記述する。 ユーザーはmeta.pmのPerlファイルをAPIとしてGearsOSのトランスコンパイラにアクセスすることが可能となる。 具体的な使用例をコード\ref{src:metapm}に示す。 meta.pmはサブルーチン\texttt{replaceMeta}が返すリストの中に、特定のパターンで配列を設定する。 各配列の0番目には、goto metaを置換したいCodeGearの名前を示すPerl正規表現リテラルを入れる。 コード\ref{src:metapm}の例では、\texttt{PhilsImpl}が名前に含まれるCodeGearを指定している。 すべてのCodeGearのgotoの先を切り替える場合は\texttt{qr/.*\//}などの正規表現を指定する。 \lstinputlisting[label=src:metapm, caption=meta.pm]{src/meta.pm} generate\_stub.plはGears CbCファイルの変換時に、 CbCファイルがあるディレクトリにmeta.pmがあるかを確認する。 meta.pmがある場合はモジュールロードを行う。 meta.pmがない場合はmeta Code Gearにgotoするものをデフォルト設定として使う。 各Gode Gearが\texttt{goto文}を呼び出したタイミングでreplaceMetaを呼び出し、 ルールにしたがってgoto文を書き換える。 変換するCodeGearがルールになかった場合は、 デフォルト設定が呼び出される。 \section{別Interfaceからの書き出しを取得する必要があるCodeGear} 従来のMetaCodeGearの生成では、 別のInterfaceからの入力を受け取るCodeGearのStubの生成に問題があった。 具体的なこの問題が発生する例題をソースコード\ref{src:insertTest1}に示す。 \lstinputlisting[label=src:insertTest1, caption=別Interfaceからの書き出しを取得するCodeGearの例]{src/pop2test.cbc} この例では\texttt{pop2Test}Code Gearから \texttt{stack->pop2}を呼び出し、 継続として\texttt{pop2Test1}を渡している。 \texttt{pop2Test}自体はStackTest Interfaceであり、 \texttt{stack->pop2}の\texttt{stack}はStack Interfaceである。 例題ではStack Interfaceの実装はSingleLinkedStackである。 SingleLinkedStackの\texttt{pop2}の実装をソースコード\ref{src:pop2}に示す。 \lstinputlisting[label=src:pop2, caption=SingleLinkedStackのpop2]{src/pop2.cbc} pop2はスタックから値を2つ取得するAPIである。 pop2の継続は\texttt{next}であり、 継続先に\texttt{data}と\texttt{data1}を渡している。 data、 data1は引数で受けている\texttt{union Data*}型の変数であり、 それぞれstackの中の値のポインタを代入している。 この操作でstackから値を2つ取得している。 このコードをgenerate\_stub.pl経由でメタ計算を含むコードに変換する。 変換した先のコードを\ref{src:pop2meta}に示す。 \lstinputlisting[label=src:pop2meta, caption=SingleLinkedStackのpop2のメタ計算]{src/pop2meta.cbc} 実際は\texttt{next}は\texttt{goto meta}に変換されてしまう。 data、data1は\texttt{goto meta}の前にポインタ変数\texttt{O\_data}が指す値にそれぞれ書き込まれる。 \texttt{O\_data}はpop2のStub CodeGearである\texttt{pop2SingleLinkedStack\_stub}を見るとなんであるかが分かる。 つまり\texttt{O\_data}はcontext中に含まれているStack Interfaceのデータ保管場所にある変数dataのアドレスである。 当初Perlスクリプトが生成した\texttt{pop2Test1}のstub CodeGearはソースコード\ref{src:pop2stub-origin}のものである。 CodeGear間で処理されるデータの流れの概要図を図\ref{fig:stackTest1}に示す。 \lstinputlisting[label=src:pop2stub-origin, caption=生成されたStub]{src/pop2stub-origin.cbc} \texttt{\_\_code pop2Test}で遷移する先のCodeGearはStackInterfaceであり、 呼び出しているAPIは\texttt{pop2}である。 取得したAPIはGearsOSのInterfaceの処理ルールにより、 Context中のStack Interfaceのデータ格納場所に書き込まれる。 しかしソースコード\ref{src:pop2stub-origin}の例では\texttt{Gearef(context, StackTest)}でContext中の\texttt{StackTest} Interfaceのdataの置き場所から値を取得している。 これではpop2でせっかく取り出した値を取得できない。 ここで必要となってくるのは、 呼び出し元のStack Interfaceからの値の取得である。 どのInterfaceから呼び出されているかは、 コンパイルタイムには確定できるのでPerlのトランスコンパイラでStub Codeを生成したい。 \begin{figure}[h] \begin{center} \includegraphics[width=130mm]{drawio/stackTest1.pdf} \end{center} \caption{stackTest1のstubの概要} \label{fig:stackTest1} \end{figure} 別Interfaceから値を取得するには別の出力があるCodeGearの継続で渡されたCodeGearをまず確定させる。 今回の例では\texttt{pop2Test1}が該当する。 このCodeGearの入力の値と、 出力があるCodeGearの出力を見比べ、 出力をマッピングすれば良い。 Stack Interfaceのpop2はdataとdata1に値を書き込む。 pop2Test1の引数はdata, data1, stackであるので、前2つにpop2の出力を代入したい。 Contextから値を取り出すのはメタ計算であるStub CodeGearで行われる。 別Interfaceから値を取り出そうとする場合、 すでにPerlトランスコンパイラが生成しているStubを書き換えてしまう方法も取れる。 しかしStubCodeGearそのものを、 別Interfaceから値を取り出すように書き換えてはいけない。 これは別Interfaceの継続として渡されるケースと、 次のgoto先として遷移するケースがあるためである。 前者のみの場合は書き換えで問題ないが、 後者のケースで書き換えを行ってしまうとStubで値を取り出す先が異なってしまう。 どのような呼び出し方をしても対応できるようにするには工夫が必要となる。 GearsOSでは継続として渡す場合や、 次のgoto文で遷移する先のCodeGearはノーマルレベルではenumの番号として表現されていた。 enumが降られるCodeGearは、厳密にはCodeGearそのものではなくStub CodeGearに対して降られる。 StubCodeGearを実装した分だけenumの番号が降られるため、 \texttt{goto meta}で遷移する際にenumの番号さえ合わせれば独自定義のStubに継続させることが可能である。 別Interfaceから値を取り出したいケースの場合、 取り出してくる先のInterfaceと呼び出し元のCodeGearが確定したタイミングで別のStubCodeGearを生成する。 呼び出し元のCodeGearが継続として渡すStubCodeGearのenumを、独自定義したenumに差し替えることでこの問題は解決する。 この機能をPerlのトランスコンパイラである\texttt{generate\_stub.pl}に導入した。 \subsection{実装の手法}