オブジェクトは、 struct...end-struct
で記述されたデータ構造体の 1 つである、 メモリーの一片です。 これは、
そのオブジェクトのクラスのメソッド・マップ(the method map) を指すフィールド object-map
を持っています。
「メソッド・マップ」(method map)33は、 そのオブジェクトのクラスのメソッドの実行トークン(xt)を含む配列です。 各セレクターには、 メソッド・マップへのオフセットが含まれています。
selector
は、CREATE
と DOES>
を使用する定義ワードです。 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]
の場合)。
著者は this
を value
として実装しました。 m:...;m
メソッドの開始時に、 古い
this
がリターン・スタックに保存され、 最後に復元されます。 TOS 上のオブジェクトは TO this
で保存します。
この手法には欠点が 1 つあります。 ユーザーが ;m
経由ではなく throw
または exit
経由でメソッドを終了した場合、 this
は復元されません(そして exit
がクラッシュする可能性があります)。 著者は
throw
の問題に対処するために、 this
を保存および復元するために catch
を再定義しました(訳注: "objects.fs" をインクルードした時に "redefined catch" と警告が出るが、
意図的に再定義してあるので無視してください、 ということ)。 例外をキャッチできるワードについても全く同様に行うべきです。 exit
については、 単純に使用を禁止します(代わりに exitm
を用意しました)。
inst-var
は field
とまったく同じですが、 DOES>
アクションが異なります:
@ this +
inst-value
も同様です。
各クラスは、 inst-var
と inst-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-map
と selector-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 に図示されています。