Next: , Previous: , Up: The objects.fs model   [Contents][Index]


6.24.3.11 objects.fs Implementation

オブジェクトは、 struct...end-struct で記述されたデータ構造体の 1 つである、 メモリの一片です。 これは、 そのオブジェクトのクラスのメソッド・マップ(the method map) を指すフィールド object-map を持っています。

「メソッド・マップ」(method map)33は、 そのオブジェクトのクラスのメソッドの実行トークン(xt)を含む配列です。 各セレクターには、 メソッド・マップへのオフセットが含まれています。

selector は、CREATEDOES> を使用する定義ワードです。 selector の本体の内容はメソッド・マップのオフセット値です。 クラスのセレクターの DOES> アクションは、 基本的には以下のとおりです(訳注: メソッド・マップの実装上のフィールド名は "object-map"):

( object addr ) @ over object-map @ + @ execute

なお、 object-map はオブジェクトの最初のフィールドであるため、 コードは生成されません。 ご覧のとおり、 セレクターの呼び出しには小さいけれども一定のコストがかかります。

クラスは基本的に struct とメソッド・マップを組み合わせたものです。 struct の場合と同様に、 クラス定義中に、 クラスのアライメントとサイズがスタックに渡されるため、 field をクラスのフィールドの定義にも使用できます。 ただし、 スタックにさらに多くの項目を渡すと不便になるため、 class はメモリ内にデータ構造を構築し、 変数 current-interface を通じてアクセスします。 定義が完了すると、 クラスはポインタ(たとえば、 子クラス定義のパラメータとして利用したりする)としてスタックに置かれます。

新しいクラスは、 その親のアライメントとサイズ、 およびその親のメソッド・マップのコピーから始まります。 新しいフィールドを定義すると、 サイズとアライメントが拡張されます。 同様に、 新しいセレクターを定義すると、 メソッド・マップが拡張されます。 overrides は、 セレクターによって指定されたオフセットでメソッド・マップに新しい xt を保存するだけです。

クラス結び付け(class binding)は、 そのクラスのメソッド・マップからセレクターによって指定されたオフセットで xt を取得し、 それをコンパイル(compile,)するだけです([bind] の場合)。

著者は thisvalue として実装しました。 m:...;m メソッドの開始時に、 古い this がリターン・スタックに保存され、 最後に復元されます。 TOS 上のオブジェクトは TO this で保存します。 この手法には欠点が 1 つあります。 ユーザーが ;m 経由ではなく throw または exit 経由でメソッドを終了した場合、 this は復元されません(そして exit がクラッシュする可能性があります)。 著者は throw の問題に対処するために、 this を保存および復元するために catch を再定義しました(訳注: "objects.fs" をインクルードした時に "redefined catch" と警告が出るが、 意図的に再定義してあるので無視してください、 ということ)。 例外をキャッチできるワードについても全く同様に行うべきです。 exit については、 単純に使用を禁止します(代わりに exitm を用意しました)。

inst-varfield とまったく同じですが、 DOES> アクションが異なります:

@ this +

inst-value も同様です。

各クラスは、 inst-varinst-value で定義されたワードや、 それらの protected されたワードを含む、 ワードリストを持っています。 また、 その親へのポインタも持っています。 class は、 クラスとそのすべての祖先のワードリストを検索順序スタック(the search order)にプッシュし、 end-class はそれらをスタックから drop します。

インターフェイスは、 フィールドと親と protected されたワードのないクラスに似ています。 つまり、 メソッド・マップがあるだけです。 クラスがインターフェイスを実装する場合、 そのメソッド・マップにはインターフェイスのメソッド・マップへのポインターが含まれます。 マップ内の正のオフセットはクラス・メソッド用に予約されているため、 インターフェイス・マップ・ポインターは負のオフセットを持ちます。 クラス・セレクターとは異なり、 インターフェイスにはシステム全体で一意のオフセットがあります。 クラス・セレクターのオフセットは、 セレクターが利用可能な(呼び出し可能な)クラスに対してのみ一意です。

この構造は、 インターフェイス・セレクターがメソッドを見つけるために、 クラス・セレクターよりも 1 つ多い間接参照を実行する必要があることを意味します。 その本体には、 クラス・メソッド・マップ内のインターフェイス・マップ・ポインター・オフセットと、 インターフェイス・メソッド・マップ内のメソッド・オフセットが含まれています。 インターフェイス・セレクターの does> アクションは、 基本的には以下のとおりです:

( object selector-body )
2dup selector-interface @ ( object selector-body object interface-offset )
swap object-map @ + @ ( object selector-body map )
swap selector-offset @ + @ execute

ここで、 object-mapselector-offset は最初のフィールドであり、 コードは生成されません。

具体的な例として、 以下のコードについて考えてみましょう:

interface
  selector if1sel1
  selector if1sel2
end-interface if1

object class
  if1 implementation
  selector cl1sel1
  cell% inst-var cl1iv1

  ' m1 overrides construct
  ' m2 overrides if1sel1
  ' m3 overrides if1sel2
  ' m4 overrides cl1sel2
end-class cl1

create obj1 object dict-new drop
create obj2 cl1    dict-new drop

このコードで作成されたデータ構造 (object のデータ構造を含む) は、セル・サイズ 4 を想定して figure に図示されています。


Footnotes

(33)

「メソッド・マップ」(method map)は著者自作用語です。 C++ 用語では、 仮想関数テーブル と言います。


Next: objects.fs Glossary, Previous: Object Interfaces, Up: The objects.fs model   [Contents][Index]