バイトコンパイルされた関数は、特殊なデータ型であるクロージャを使用します。クロージャは、バイトコンパイルされたLisp関数とインタプリタ型のLisp関数の両方で使用されます。このようなオブジェクトが呼び出し対象の関数として現れるたびに、Emacsは適切なインタプリタを使用して、バイトコードまたは非コンパイルのLispコードのいずれかを実行します。
内部的に、クロージャはベクトルに非常によく似ており、その要素には aref
を用いてアクセスできます。出力形式はベクトルと同様ですが、先頭の ‘[’
の前にさらに ‘#’
が付加されます。要素数は少なくとも3つ必要です。最大数の制限はありませんが、実際に利用されるのは最初の6つの要素のみです。それらは以下の通りです:
引数の記述子。これは、引数リストの特徴で説明されている引数のリストか、あるいは必要な引数数をエンコードした整数のいずれかとなります。後者の場合、記述子の値は、ビット0から6で最小引数数を、ビット8から14で最大引数数を指定します。引数リストで&restが使用されている場合はビット7がセットされ、そうでない場合はクリアされます。
クロージャがバイトコード関数であり、argdesc がリストである場合、引数はバイトコードの実行前に動的にバインドされます。argdesc が整数である場合は、代わりに引数がバイトコードインタプリタのスタックにプッシュされ、その後コードが実行されます。
インタプリタ関数の場合、この要素は関数の本体を構成するLispフォームの(空でない)リストとなります。バイトコンパイル済み関数の場合は、バイトコード命令を含む文字列となります。
バイトコンパイルされた関数の場合、ここにはバイトコードが参照するLispオブジェクトのベクトルが格納されます。これには、関数名や変数名として使用されるシンボルが含まれます。インタプリタ型関数の場合、その関数がEmacs
Lispの旧来の動的スコープ方言を使用しているなら nil
となり、そうでなければ関数の字句環境が格納されます。
この関数が必要とする最大スタックサイズ。インタプリタ関数では、この要素は未使用のままとなります。
ドキュメント文字列(存在する場合)。存在しない場合は nil。ドキュメント文字列がファイルに保存されている場合、その値は数値またはリストになります。実際のドキュメント文字列を取得するには、関数
documentation を使用してください(ドキュメント文字列へのアクセス を参照)。
インタラクティブ仕様(ある場合)。これは文字列またはLisp式となります。インタラクティブではない関数の場合は nil となります。
以下に、バイトコード関数オブジェクトの出力形式の例を示します。これはコマンド backward-sexp の定義です。
#[256 "\211\204^G^@\300\262^A\301^A[!\207" [1 forward-sexp] 3 1793299 "^p"]
バイトコードオブジェクトを作成する原始的な方法は make-byte-code を用いることです:
この関数は、elementsを要素とするバイトコード関数オブジェクトを表すクロージャを生成して返します。
バイトコード関数の要素を自前で用意しようとすべきではありません。要素に不整合があると、関数呼び出し時にEmacsがクラッシュする可能性があるためです。これらのオブジェクトの生成は常にバイトコンパイラに任せてください。そうすることで、要素の整合性が保たれます(はずです)。
インタプリタ関数を作成する原始的な方法は make-interpreted-closure
を用いることです:
この関数は、引数 args を持ち、本体が body(非
nil の Lisp
フォームのリストである必要があります)で構成される、解釈済み関数を表すクロージャを構築して返します。env
は、eval で使用されるものと同じ形式のレキシカル環境です(Eval を参照)。ドキュメント docstring は非
nil であれば文字列である必要があり、インタラクティブフォーム iform は非 nil であれば (interactive arg-descriptor)
の形式である必要があります(Using interactive を参照)。