高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technote 1095

Object Support Library Version History


目次


OSL のはじまり

はっきりわかっていること

これまでに報告されている OSL のバグ

OSL 1.2 のダウンロード
のテクニカルノートでは、Apple の OSL (Object Support Library) のバージョン履歴について説明します。このライブラリは、アプリケーションが OSA (Open Scripting Architecture) オブジェクトモデルをサポートするために使用することのできるルーチンを提供します。

OSL はもともと 68K のスタティックライブラリとしてリリースされました。Power Macintosh システムの導入とともに、OSL は共有ライブラリとして再パッケージ化されました。Code Fragment Manager 68K Runtime Enabler (CFM-68K) がリリースされたとき、OSL の Power PC および CFM-68K バージョンの両方を含むファットライブラリになりました。

このテクニカルノートでは、現在使用可能な OSL のすべてのバージョンをあげ、それぞれのバージョンについて簡単な履歴と説明を示します。また、どのバージョンを使用すればいいか (または使用すべきではないか) も示します。

OSA オブジェクトモデルのサポートを提供する Macintosh アプリケーションを開発している場合や、そうしたアプリケーションを使用する必要がある場合は、この テクニカルノートを参考にしてください。


OSL のはじまり
OSL が最初にリリースされたとき、それは、OSA オブジェクトモデルをサポートするアプリケーションに静的にリンクされる 68K スタティックライブラリ (AEObjectSupportLib.o) でした。このことは、クラシック 68K アプリケーションについては現在でも同じです。

Power Macintosh とそれに伴う CFM 共有ライブラリアプリケーションモデルのリリースとともに、'.o' ファイルではなく、OSL の共有ライブラリバージョンを Power PC ネイティブアプリケーション用に提供することが決定されました。これにより、ネイティブアプリケーションは共有ライブラリアプリケーションモデルを利用できるようになりました。

OSL PowerPC 共有ライブラリはバージョン 1.0.2 としてリリースされました。これは、最初の Power Macintosh システムソフトウェア (バージョン 7.1.2) と AppleScript SDK バージョン 1.1 に添付されていました。このバージョンは、現在でも、Mac OS SDK CD に収録されています。OSL のこのバージョンでは、whose クローズの取り扱いに関していくつかのバグが報告されています (これらのバグの詳細については、このテクニカルノートの「これまでに報告されている OSL のバグ」を参照してください)。

この最初の共有ライブラリにより、OSL 共有ライブラリの最初の問題が明らかになりました。OSL は Pascal で書かれていましたが、PowerPC 対応の Pascal コンパイラは存在しませんでした。このため、このバージョンには、OSL を含む 68K コードリソースをロードする小さな PowerPC ネイティブライブラリが含まれていました。

しかし、このロードは適切に実行されませんでした。ObjectSupportLib のリソースファイルは、しばしばアプリケーションのリソースチェーンの中に入ってしまいました。この問題は、スタートアップ時に Finder に OSL をロードさせることによって、System 7.5.2 で解消されました。その結果、OSL のリソースファイルは、強制的にシステムにロードされることになります。OSL のリソースファイルはシステムには障害を与えません。こうした対処方法により、7.5.2 以降のすべてのシステムバージョンで、この問題は解消されました。ただし、それ以前のシステムバージョンではこの問題は今でも存在します。

Power Macintosh の導入後、OSL のソースコードを Pascal から C に書き換える作業が開始されました。この書き換えは、PowerPC および CFM-68K コードの両方を含む共有ライブラリを提供するためにはどうしても必要な作業でした。書き換えられたファット OSL は、バージョン 1.0.4 としてリリースされました。これは、つい最近まで、E.T.O. の CD-ROM リリースに収録されていました。

しかしながら、OSL のこのバージョンにはいくつかの問題がありました。その中でも最悪な問題は、バージョン 1.0.2 ではインストールされた Gestalt セレクタがバージョン 1.0.4 ではインストールされなかったことです。このため、この Gestalt セレクタをテストしているアプリケーションが OSL 共有ライブラリの存在を検出できなくなってしまいました。

注意: Gestalt セレクタは、OSL の存在を検出するための最善の方法ではありませんが、この判定方法は、コードフラグメントモデルの開発における初期段階から採用されていたものであり、当時はまだ Gestalt を使用することの信頼性が十分には理解されていませんでした。より望ましい方法は、ライブラリに含まれるシンボルを kUnresolvedCFragSymbolAddress と比較することです (このプロセスの詳細については、「Technote 1083: Code Fragment Manager ベースの共有ライブラリへの弱いリンク (Weak-link)」を参照してください)。

OSL のこのバージョンにかかわるその他の主要な問題といえば、whose クローズを取り扱うコードの中にいくつかのバグがあったことです (前述のバグに加えて)。これらのバグは、有効なリクエストを提示されても、アプリケーションが不正な結果や不正なエラーメッセージを返す原因になっていました。Gestalt セレクタがなくて、このバージョンを使用できなかったため、whose クローズの取り扱いに関するバグは、かなり後になるまでその存在が明らかではありませんでした。


コードの変更ではなく、単なるバージョン番号の変更
バージョン 1.0.2 から 1.0.4 の間のどこかで、AppleScript 1.1 SDK に対応するビルドプロセスに誤りがあり、OSL のバージョン 1.0.2 がバージョン 1.1 としてリリースされることになってしまいました。これら 2 つのバージョンでは実際のコードの違いはありません。新しいバージョン番号が付けられているだけです。

これにより、実際にはバージョン 1.0.4 よりも古いステータスにある 1.1 バージョンが登場したことになります。1.1 の上に 1.0.4 をインストールしようとすると、インストーラから古いバージョンで新しいバージョンを上書きしようとしているというメッセージが表示されてしまいます。このため、Apple では、OSL の Apple Telecom ソフトウェアバージョンを出荷しました。これは、1.0.4 を 1.1.1 に変更したものです。その結果、インストーラでは新しいバージョンを使って古いバージョンを上書きできるようになりました。ただし、この場合もコードに変更はなく、バージョン番号を変更したにすぎませんでした (もちろん、1.0.4 の問題点はすべてそのまま残されていました)。

ここが間違いの始まりでした。つまり、(一般には) うまく動作するバージョン 1.1 が不良なバージョンである 1.1.1 に置き換えられる状況になってしまいました。


バグをフィックスするための勇敢な試み
この時点で、AppleScript チームは、事態を前進させ、問題をフィックスさせる好機であると判断しました。彼らは、Gestalt セレクタの問題を解決したバージョンをリリースし、これを 1.1.1 と呼びました。しかし、誰も Apple Telecom OSL バージョン 1.1.1 のことを彼らに知らせておらず、その結果、彼らはこのバージョンを 1.1.2 に変更しました。しかしまたしても、誰か別の人間が OSL バージョン 1.1.2 の限定リリースをすでに行っていました。

これは不幸なことでしたが、それほど大きな問題ではありませんでした。AppleScript チームは、バージョンをすぐに 1.1.3 に上げました。このバージョンは、修正された Gestalt セレクタを含んでおり、デベロッパ向けの限定バージョンとしてリリースされました。これでようやく、Gestalt セレクタのバグのために OSL をロードできなかったアプリケーションが OSL を使用できるようになりました。しかし、アプリケーションが OSL をロードできるようになると、OSL が whose クローズを適切に取り扱うことができないことに起因するさまざまな不具合が明るみに出ることになりました (Gestalt のバグがフィックスされるまで、このコードを実際に実行した人はいなかったためです)。


計画段階への逆戻り
AppleScript チームは、1.0.4/1.1.1 のコードストリームを詳細に見直すことを開始し、修正が既存のアプリケーションの安定性に与える影響を検討した結果、将来トラブルが発生するリスクが極めて大きいと判断しました。この決定により、OSL の次のリリースを開発するために、より古い 1.0.2/1.1 のコードストリームに戻ることになりました。

その結果、バージョン 1.0.2 のリソースをロードするソースストリームが、ネイティブ PowerPC および CFM-68K ライブラリとしてコンパイルできるように修正されました。リソースロードのバグも問題の所在が明らかになりました。この改訂されたライブラリはテストされ、さらにテストを続けるために、ごく一部のデベロッパのグループに送られました。そして、最終的なビルドがバージョン 1.1.4 として承認されました。

そうこうするうちに、このストーリーに追加すべき新しい動きがありました。Mac OS のリファレンスリリース戦略に合わせて、ファイルのクリエータタイプに変更が加えられ、同時にバージョン番号も 1.1.6 に変更されました。

注意:
バージョン 1.1.4 は、Developer CD Series と Mac OS SDK CD に収録されており、一方、バージョン 1.1.6 は一部の E.T.O. CD に収録されています。



しかし、このストーリーにはさらに付け加えるべきことがあります。OSL を検証するために使用されたテストスイートは Gestalt セレクタの存在をテストしましたが、それが適切にインストールされているかどうかを判定しませんでした。基本的に、ネイティブ OSL リソースローディングコードは、その Gestalt セレクタをインストールしたときに適切な動作をせず、その結果、一定の状況でクラッシュの原因になっていました。

その間に、バージョン 1.1.6 は、デベロッパに配布された Harmony (Mac OS 7.6) f3 ビルドの一部として組み込まれました。この最新の OSL の問題が明らかになったとき、OSL のバージョン 1.0.2 を採用し、それを改訂してバージョン 1.1.8 とすること (またしても) が決定されました。これが、Mac OS 7.6 の GM バージョンに組み込まれた OSL のバージョンです。

このバージョンの最大の問題は、それがファットではなく、同時に CFM-68K コードも含んでいないことでした (1.0.2 がそうであったことを思い出してください)。しかし、このことがすぐに問題になることはありませんでした。というのも、Mac OS 7.6 は、CFM-68K に対するサポートなしに出荷されたためです。実際、7.6 は現在の CFM-68K 機能拡張の存在を明示的にチェックして無効にし、システムのこのバージョンとともに CFM-68K がロードされるのを妨げます。CFM-68K を実行できない場合、ファット OSL は必要ありません。


新たな希望
数々のエンジニアの努力の結果、OSL はより徹底的に書き換えられ、Gestalt セレクタとリソースファイルのバグが両方ともフィックスされました。テストも完璧に終了し、問題は解消されました。このとき、OSL はバージョン 1.2 としてリリースされました。このバージョンは、現在のところほぼ予想通りに動作しています。現在でも whose クローズの解決に関連するバグが残されていますが、それらは致命的なものではなく、デベロッパが容易に回避できるものです (「これまでに報告されているバグ」を参照してください)。


はっきりわかっていること
ここまでの説明を読んでくると、頭が混乱して、"そうすると、一体どのバージョンを使えばいいのだ" という疑問がわいてくるはずです。現時点(1998年10月)で、この質問に対する答えは次のようになります。

Object Support Library のバージョン 1.2 を使用するようにしてください。

Mac OS 8.0 では、Object Support Library バージョン 1.2 がシステムファイルに組み込まれています。OSL は今後もシステムファイルに組み込まれ、機能拡張フォルダに OSL の古いバージョンが存在しても、それはロードされません。 Mac OS 8.0 以前でバージョン 1.2 が入手できない場合は、1.1.8(またはその三つ子の弟であるバージョン 1.0.2 または 1.1)を使ってください。ただし、これらの古いバージョンがファットではない点に注意してください。このため、これらのバージョンは CFM-68K アプリケーションとともに正常に動作しないはずです。CFM-68K アプリケーションを実行する必要がある場合は、バージョン 1.2 を使わなければなりません。

そして、当然のことですが、OSL のより新しいバージョンがリリースされた場合は、それをインストールするようにしてください。

OSL の履歴
バージョン ステータス
1.0.2 最初の PowerPC 共有ライブラリ。リソースファイルのバグあり。
1.0.4 新しいコードベース。Gestalt セレクタなし。その他のバグあり。
1.1 実際には 1.0.2 とまったく同様。新しいクリエータコードを含むだけ。
1.1.1 1.0.4 の双子の兄 (うり二つ)
1.1.2/1.1.3 一般にはリリースされなかったバージョン。このため無視してよい。
1.1.4 正常に動作しない。ただし、OS SDK CD に収録されてリリースされた。
1.1.6 Harmony f3 とともに出荷されたバージョン
1.1.8 Mac OS 7.6 とともに出荷されたバージョン (1.0.2 のもう一つのクローン)
1.2 CFM-68K 4.0 とともに出荷されたバージョン。クラッシュの原因となるすべてのバグがフィックスされている。

これまでに報告されている OSL のバグ
OSL には現在でもいくつかのバグが残されています。ただし、これらのバグには回避方法があります。


比較関数に渡されるアンロックハンドル
whose クローズを解決するときに使用するため、オブジェクト比較関数をインストールすることができます。比較関数を呼び出すとき、OSL は 2 つの AEDescs にポインタを渡します。一つは比較されるオブジェクトに対するもので、もう一つはそのオブジェクトと比較するオブジェクトまたは記述子に対するものです。

ここで、パラメータによってポイントされる記述子レコードを含むメモリブロックがロックされていない再配置可能なブロックに配置され、比較関数が呼び出された後で移動する可能性があるという問題が発生します。

この問題を回避するには、オブジェクト比較ルーチンに入った直後に、パラメータによってポイントされる記述子レコードをローカル変数にコピーします。

クラシック 68K アプリケーションとセグメントローダが対象となると、この問題はより複雑になります。アプリケーションをビルドするときに、比較関数と AEObjectSupportLib.o ライブラリが同じセグメント内にない場合、OSL を含むセグメントがすでにロードされていないと、記述子レコードがコピーされる前に移動してしまう可能性があります。

この問題を回避するには、アプリケーションをビルドするときに、比較関数と AEObjectSupportLib.o ライブラリが同じセグメント内にあることを確認します。


whose クローズの解決中に発生するオブジェクト比較コールバック関数のメモリリーク
オブジェクト指定子に含まれる whose クローズを解決している間に、OSL がアプリケーション供給の比較コールバック関数を呼び出すとき、メモリリークが発生します。OSL が比較コールバック関数にオブジェクトを渡すとき、それらのオブジェクトはしばしば、オブジェクトアクセッサ関数によって作成されるアプリケーション定義のトークンとなります。

問題は、OSL が AEDisposeToken ではなく、これらのトークンオブジェクト上の AEDisposeDesc を呼び出すことです。これがメモリリークの原因になり、アプリケーションがトークンにアタッチしたデータが適切に処理されなくなります。

この問題は、前述のアンロックハンドルのバグによってさらに複雑なものになります。この問題を回避するには、アンロックハンドルのバグフィックスをトークンの処理と組み合わせます。次の疑似コードは、この問題および前述のアンロックハンドルの問題に対する対処方法を示しています。

Pascal 言語バージョン
function  MyCompareObjects (comparisonOperator:             DescType;
                            (CONST) VAR theObject:          AEDesc;
                            (CONST) VAR objOrDescToCompare: AEDesc;
                            VAR compareResult:              boolean): OSErr;
var
    theObjectCopy             : AEDesc;
    objOrDescToCompareCopy    : AEDesc;

begin
    { OSL が記述子を再配置可能なブロックにポイントしているため、
     まず記述子のコピーを行う。この再配置可能なブロックは、以下の
     コードによって移動される可能性がある }
    theObjectCopy := theObject;
    objOrDescToCompareCopy := objOrDescToCompare;

    { 以下のオブジェクトを処理する必要があるため、
    オリジナルの記述子を null 記述子に設定する }
    SetToNullDesc (theObject);
    SetToNullDesc (objOrDescToCompare);

    { 比較を行うコードはここに置く }
    MyCompareObjects := DoTheComparison (comparisonOperator, theObjectCopy,
                                   objOrDescToCompareCopy, compareResult);

    { これらの記述子は定数であると前提されるが、OSL は決して
    トークンコールバック関数を呼び出さない。このため、記述子のいずれかが
    アプリケーション定義のトークンである場合、それらはここで処理する }
    MyDisposeToken (theObjectCopy);
    MyDisposeToken (objOrDescToCompareCopy);
end;



C 言語バージョン
OSErr MyCompareObjects (DescType        comparisonOperator,
                        const AEDesc    *theObject,
                        const AEDesc    *objOrDescToCompare,
                        Boolean         *compareResult)
{
    OSErr anErr;
    AEDesc theObjectCopy;
    AEDesc objOrDescToCompareCopy;

    theObjectCopy = *theObject;
    objOrDescToCompareCopy = *objOrDescToCompare;

    SetToNullDesc (const_cast (theObject));
    SetToNullDesc (const_cast (objOrDescToCompare));

    anErr = DoTheComparison (comparisonOperator, &theObjectCopy,
                             &objOrDescToCompareCopy, &compareResult);

    MyDisposeToken (&theObjectCopy);
    MyDisposeToken (&objOrDescToCompareCopy);
}



whose クローズの解決中にマーキングコールバック関数で発生するメモリリーク
オブジェクト指定子に含まれる whose クローズを処理している間に、OSL がアプリケーション供給のマーキング関数を呼び出すとき、メモリリークが発生します。

オブジェクト指定子を解決するプロセスで、記述子レコードが作成され、mark token コールバック関数 (containerToken パラメータとして) とオブジェクトマーキングコールバック関数 (theToken パラメータとして) に渡されます。問題は、OSL が、AEDisposeToken または AEDisposeDesc のいずれによっても、この記述子を処理しないことです。

この問題は、アプリケーションが受け取ったすべての whose クローズをアプリケーション自身に解決させることで回避することができます。この方法により、アプリケーションは AEResolve の呼び出しを避けることができます。これは、特に一般的なタイプのオブジェクト指定子にとっては好都合です。しかし、コールバック関数を使用する OSL によってよりよく処理される "一般的でない" オブジェクト指定子を受け取る可能性もあります。このようなオブジェクト指定子を自分自身で取り扱う場合は、大部分の OSL を複製することで終わってしまいます。

幸いにも、この問題の回避方法は比較的単純です。記述子レコードをオブジェクトマーキング関数の末尾で処理し、それを null 記述子に設定します。このバグが OSL の将来のリリースでフィックスされても、この回避方法により特に問題が発生することはないはずです。というのも、null 記述子レコードの処理は常に安全であるためです。

次に、記述子レコードを null 記述子に設定する関数を示します。
void SetToNullDesc (AEDesc *theObject)
{
        theObject->descriptorType = typeNull;
        theObject->dataHandle = nil;
}


参考文献
  • Technote 1083: Code Fragment Manager ベースの共有ライブラリへの弱いリンク (Weak-link)


このテクニカルノートのバージョン履歴
1998年2月 - テクニカルノート 1095 として発行
1998年10月 - OSL のバージョン履歴を更新、C のサンプルコードのバグを修正