[インデックス 1043] ファイルの概要
このコミットは、Go言語のリンカ 6l における型情報(特に型シグネチャ gotypesigs)の生成方法の改善と、それを利用した新しいランタイム関数 sys.unreflect の追加に関するものです。これにより、Goのランタイムがより柔軟に型情報を扱い、リフレクション機能が拡張されました。
コミット
commit 47caf6424c9bcaedb100feae83032b5afcb1bcc0
Author: Russ Cox <rsc@golang.org>
Date: Mon Nov 3 16:03:12 2008 -0800
6l: generate gotypesigs on demand.
add sys.unreflect, which uses gotypesigs.
R=r
DELTA=170 (152 added, 12 deleted, 6 changed)
OCL=18396
CL=18404
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/47caf6424c9bcaedb100feae83032b5afcb1bcc0
元コミット内容
6l: generate gotypesigs on demand.
add sys.unreflect, which uses gotypesigs.
R=r
DELTA=170 (152 added, 12 deleted, 6 changed)
OCL=18396
CL=18404
変更の背景
このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の初期開発段階でした。Go言語は静的型付け言語でありながら、動的な型情報へのアクセス(リフレクション)を可能にすることを目指していました。初期のGo言語では、型情報はコンパイル時に生成され、ランタイムで利用できるようにリンカによって処理される必要がありました。
このコミットの主な背景は以下の2点と考えられます。
- 型情報の効率的な管理:
gotypesigs(Go Type Signatures) は、Goの型システムにおける重要な要素であり、インターフェースの実装チェックやリフレクション操作に利用されます。初期の実装では、これらの型情報が常に生成されていた可能性がありますが、「on demand (オンデマンド)」での生成に切り替えることで、リンカの処理効率や生成されるバイナリのサイズを最適化しようとしたと考えられます。これは、必要な時に必要な型情報だけを生成することで、無駄を省くアプローチです。 - リフレクション機能の拡張: Go言語のリフレクションは、実行時に型情報を検査し、操作する強力な機能です。
sys.reflectのような関数は、Goの値をその型情報(uint64とstringで表現される)に変換するために存在していました。しかし、その逆の操作、つまり型情報と生のデータからGoのインターフェース値を再構築する機能 (unreflect) が求められていたと考えられます。これは、例えばシリアライズされたデータや外部からの入力に基づいて、動的にGoの型を再構築するような高度なユースケースに対応するために必要でした。
この変更は、Go言語のランタイムにおける型システムの柔軟性と効率性を向上させ、将来のリフレクション機能の基盤を強化することを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびコンパイラ/リンカに関する基本的な知識が必要です。
-
Go言語のコンパイルとリンク:
- Goのソースコード (
.goファイル) は、まずコンパイラ (gcなど) によってオブジェクトファイル (.6ファイルなど、アーキテクチャによって異なる) にコンパイルされます。 - これらのオブジェクトファイルは、リンカ (
6lなど) によって結合され、実行可能なバイナリが生成されます。リンカは、シンボルの解決、コードの配置、ランタイムに必要なデータの準備などを行います。 6lは、当時のGo言語のx86-64アーキテクチャ向けリンカの名称です。
- Goのソースコード (
-
Go言語のリフレクション:
- Goのリフレクションは、
reflectパッケージを通じて提供される機能で、プログラムの実行中に変数や関数の型情報を検査したり、値を動的に操作したりすることを可能にします。 - Goのインターフェースは、リフレクションの基盤となる重要な概念です。インターフェース値は、内部的に「型情報」と「値データ」のペアとして表現されます。
sys.reflect(当時の内部関数名) は、Goのインターフェース値からその型情報と値データを抽出するような機能を提供していたと考えられます。
- Goのリフレクションは、
-
型シグネチャ (Type Signatures):
- Goのランタイムは、プログラム内で使用されるすべての型に関する情報を保持しています。これには、型の名前、構造、メソッドセットなどが含まれます。
gotypesigsは、これらの型情報をリンカが生成し、ランタイムがアクセスできるようにするためのデータ構造またはセクションを指します。これは、インターフェースの動的な型チェックやリフレクション操作の際に参照されます。Sigtは "Signature Type" の略で、個々の型シグネチャを表す構造体であると推測されます。
-
リンカにおけるデータセクション:
- リンカは、コンパイルされたコードだけでなく、プログラムが実行時に必要とする静的なデータもバイナリに埋め込みます。これには、文字列定数、グローバル変数、そして型情報のようなランタイムデータが含まれます。
gotypestringsは、Goの型名を文字列として格納するセクションであると推測されます。gotypesigsは、より詳細な型構造に関する情報を格納するセクションであると考えられます。
-
Prog構造体とnewdata関数:Progは、リンカが扱う命令やデータのエントリを表す内部構造体です。newdata関数は、リンカが新しいデータエントリを生成し、それを特定のシンボルに関連付けるために使用されるユーティリティ関数です。
技術的詳細
このコミットの技術的な詳細は、主にGoリンカ 6l とランタイム src/runtime/iface.c における型情報の管理とリフレクション機能の拡張に焦点を当てています。
1. gotypesigs のオンデマンド生成
src/cmd/6l/go.cの変更:definetypesigsという新しい関数が追加されました。この関数は、リンカがgotypesigsというシンボルへの未定義参照を検出した場合に呼び出されます。これは、「オンデマンド」生成のメカニズムを示しています。つまり、プログラムがgotypesigsを必要としない限り、このデータは生成されません。definetypesigsは、リンカが認識しているすべての型シグネチャ (sigt·プレフィックスを持つシンボル) を収集し、それらを名前でソートします。- ソートされた型シグネチャへのポインタの配列 (
gotypesigs) が、リンカによってデータセクションに書き込まれます。各ポインタはPtrSize(8バイト、x86-64の場合) のサイズを持ちます。 - 型シグネチャの総数 (
ngotypesigs) もint32型の変数としてデータセクションに書き込まれます。 - 既存の
definetypestrings関数も、newdata関数の呼び出し方法が変更されています。これは、リンカのデータ生成メカニズムの統一化を示唆しています。
src/cmd/6l/obj.cの変更:- リンカのメイン関数
mainで、definetypestrings()の後にdefinetypesigs()が呼び出されるようになりました。これにより、リンカの処理フローに型シグネチャの生成が組み込まれます。
- リンカのメイン関数
2. sys.unreflect の追加
src/cmd/gc/sys.goの変更:export func unreflect(uint64, string) (ret interface { });という新しい関数が追加されました。これは、Goのソースコードからsys.unreflectを呼び出すための宣言です。uint64は値データ、stringは型名を表すと推測されます。
src/cmd/gc/sysimport.cの変更:- コンパイラの内部で
sys.unreflectが認識されるように、そのシグネチャがsysimport文字列に追加されました。
- コンパイラの内部で
src/runtime/iface.cの変更:sys·unreflectというC言語の関数が実装されました。これがsys.unreflectの実際のランタイム実装です。- この関数は、
uint64の値データとstringの型名を受け取ります。 findtypeヘルパー関数が導入されました。この関数は、与えられた型名に対応するSigt(型シグネチャ) をgotypesigs配列から検索します。cmpstringcharsは、Goの文字列とCスタイルの文字列を比較するためのヘルパー関数です。fakesigtヘルパー関数は、gotypesigsに見つからない型名に対して、一時的なSigtを生成する役割を担っています。これは、未知の型を扱う際のエラーハンドリングや、動的な型生成の初期段階で利用される可能性があります。ただし、コメントにTODO(rsc): What to do here?とあることから、この部分の設計はまだ初期段階であったことが伺えます。sys·unreflectは、findtypeで取得したSigtと入力された値データ (it) を使用して、新しいインターフェース値 (retim,retit) を構築します。hashmap(sigi·inter, findtype(type), 0)の部分は、型シグネチャに基づいてインターフェースの内部表現を構築していることを示唆しています。
3. newdata 関数の変更
src/cmd/6l/pass.cの変更:newdata関数の宣言がstatic Prog*からProg*に変更されました。これは、この関数が他のファイルからもアクセス可能になったことを意味し、リンカのデータ生成ロジックがよりモジュール化されたことを示唆しています。
これらの変更は、Go言語のランタイムが、コンパイル時に生成された型情報をより効率的に利用し、動的な型操作(リフレクション)の能力を向上させるための重要なステップでした。特に sys.unreflect の導入は、Goのインターフェースが単なる抽象化のツールではなく、実行時の型変換と操作のための強力なメカニズムであることを示しています。
コアとなるコードの変更箇所
このコミットのコアとなるコードの変更は、主に以下のファイルに集中しています。
-
src/cmd/6l/go.c:definetypesigs関数の追加 (約50行の新規コード)。definetypestrings関数内のnewdata呼び出しの変更。symcmp関数の追加。
-
src/runtime/iface.c:sys·unreflect関数の追加 (約40行の新規コード)。fakesigt関数の追加。cmpstringchars関数の追加。findtype関数の追加。gotypesigsとngotypesigsの外部宣言。
-
src/cmd/gc/sys.go:export func unreflect(uint64, string) (ret interface { });の追加。
-
src/cmd/gc/sysimport.c:"export func sys.unreflect (? uint64, ? string) (ret interface { })\\n"の追加。
コアとなるコードの解説
src/cmd/6l/go.c における definetypesigs
void
definetypesigs(void)
{
int i, j, n;
Sym **all, *s, *x;
Prog *prog;
if(debug['g'])
return;
if(debug['v'])
Bprint(&bso, "%5.2f definetypesigs\\n", cputime());
s = lookup("gotypesigs", 0);
if(s->type == 0) // gotypesigsシンボルが参照されていない場合は何もしない
return;
if(s->type != SXREF) { // 既に定義されている場合はエラー
diag("gotypesigs already defined");
return;
}
s->type = SDATA; // シンボルをデータとしてマーク
// すべてのsigt·プレフィックスを持つシンボルをリストアップ
n = 0;
for(i=0; i<NHASH; i++)
for(x = hash[i]; x; x=x->link)
if(memcmp(x->name, "sigt·", 6) == 0)
n++;
all = mal(n*sizeof all[0]);
j = 0;
for(i=0; i<NHASH; i++)
for(x = hash[i]; x; x=x->link)
if(memcmp(x->name, "sigt·", 6) == 0)
all[j++] = x;
// 名前でソート
qsort(all, n, sizeof all[0], symcmp);
// ポインタの配列としてデータセクションに出力
enum { PtrSize = 8 }; // x86-64の場合
for(i=0; i<n; i++) {
prog = newdata(s, PtrSize*i, PtrSize, D_EXTERN);
prog->to.type = D_ADDR;
prog->to.index = D_EXTERN;
prog->to.sym = all[i]; // 各sigt·シンボルへのアドレス
}
s->value = PtrSize*n; // gotypesigsのサイズ
// ngotypesigs (型シグネチャの数) を出力
s = lookup("ngotypesigs", 0);
s->type = SDATA;
s->value = sizeof(int32);
prog = newdata(s, 0, sizeof(int32), D_EXTERN);
prog->to.offset = n; // 型シグネチャの総数
if(debug['v'])
Bprint(&bso, "%5.2f typestrings %d\\n", cputime(), n);
}
この関数は、リンカが gotypesigs シンボルへの参照を検出した場合に、プログラムで使用されるすべての型シグネチャ (sigt· で始まるシンボル) を収集し、それらをソートして、実行時にアクセス可能なデータ構造としてバイナリに埋め込みます。これにより、ランタイムは動的に型情報を参照できるようになります。
src/runtime/iface.c における sys·unreflect
extern Sigt *gotypesigs[]; // リンカが生成した型シグネチャの配列
extern int32 ngotypesigs; // 型シグネチャの数
// ... (fakesigt, cmpstringchars, findtype の実装) ...
void
sys·unreflect(uint64 it, string type, Map *retim, void *retit)
{
if(cmpstring(type, emptystring) == 0) { // 型名が空の場合はnilインターフェースを返す
retim = 0;
retit = 0;
} else {
// 型名に対応するSigt (型シグネチャ) を検索
retim = hashmap(sigi·inter, findtype(type), 0);
retit = (void*)it; // 値データを設定
}
FLUSH(&retim); // レジスタの値をメモリにフラッシュ
FLUSH(&retit);
}
sys·unreflect は、Goのランタイムが提供する内部関数で、sys.unreflect Go関数から呼び出されます。この関数は、生のデータ (it、uint64 として渡される) と型名 (type、string として渡される) を受け取り、それらからGoのインターフェース値を再構築します。findtype 関数を使って型名に対応する Sigt を gotypesigs 配列から探し、その型情報と値データを組み合わせて新しいインターフェース値を生成します。これにより、Goのリフレクション機能が、型情報とデータからインターフェースを「再構築」する能力を獲得しました。
関連リンク
- Go言語のリフレクションに関する公式ドキュメント (現代のGo): https://pkg.go.dev/reflect
- Go言語のインターフェースに関する公式ドキュメント: https://go.dev/tour/methods/10
参考にした情報源リンク
- Go言語の初期開発に関する情報 (Goの歴史): https://go.dev/doc/history
- Go言語のコンパイラとリンカの内部構造に関する一般的な情報 (Goのツールチェーン): https://go.dev/doc/articles/go_command.html (これは現代のドキュメントですが、当時の概念を理解するのに役立ちます)
- Go言語のインターフェースの内部表現に関する議論 (Stack Overflow, ブログ記事など): "Go interface internal representation" で検索すると多くの情報が見つかります。
- Go言語のリンカ
6lに関する情報 (初期のGoのドキュメントやソースコードコメント): "Go 6l linker" で検索すると、当時のリンカの役割に関する情報が見つかることがあります。 - Go言語の型システムに関する議論: "Go type system" で検索すると、型シグネチャの概念を理解するのに役立つ情報が見つかります。
[インデックス 1043] ファイルの概要
このコミットは、Go言語のリンカ 6l における型情報(特に型シグネチャ gotypesigs)の生成方法の改善と、それを利用した新しいランタイム関数 sys.unreflect の追加に関するものです。これにより、Goのランタイムがより柔軟に型情報を扱い、リフレクション機能が拡張されました。
コミット
commit 47caf6424c9bcaedb100feae83032b5afcb1bcc0
Author: Russ Cox <rsc@golang.org>
Date: Mon Nov 3 16:03:12 2008 -0800
6l: generate gotypesigs on demand.
add sys.unreflect, which uses gotypesigs.
R=r
DELTA=170 (152 added, 12 deleted, 6 changed)
OCL=18396
CL=18404
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/47caf6424c9bcaedb100feae83032b5afcb1bcc0
元コミット内容
6l: generate gotypesigs on demand.
add sys.unreflect, which uses gotypesigs.
R=r
DELTA=170 (152 added, 12 deleted, 6 changed)
OCL=18396
CL=18404
変更の背景
このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の初期開発段階でした。Go言語は静的型付け言語でありながら、動的な型情報へのアクセス(リフレクション)を可能にすることを目指していました。初期のGo言語では、型情報はコンパイル時に生成され、ランタイムで利用できるようにリンカによって処理される必要がありました。
このコミットの主な背景は以下の2点と考えられます。
- 型情報の効率的な管理:
gotypesigs(Go Type Signatures) は、Goの型システムにおける重要な要素であり、インターフェースの実装チェックやリフレクション操作に利用されます。初期の実装では、これらの型情報が常に生成されていた可能性がありますが、「on demand (オンデマンド)」での生成に切り替えることで、リンカの処理効率や生成されるバイナリのサイズを最適化しようとしたと考えられます。これは、必要な時に必要な型情報だけを生成することで、無駄を省くアプローチです。 - リフレクション機能の拡張: Go言語のリフレクションは、実行時に型情報を検査し、操作する強力な機能です。
sys.reflectのような関数は、Goの値をその型情報(uint64とstringで表現される)に変換するために存在していました。しかし、その逆の操作、つまり型情報と生のデータからGoのインターフェース値を再構築する機能 (unreflect) が求められていたと考えられます。これは、例えばシリアライズされたデータや外部からの入力に基づいて、動的にGoの型を再構築するような高度なユースケースに対応するために必要でした。
この変更は、Go言語のランタイムにおける型システムの柔軟性と効率性を向上させ、将来のリフレクション機能の基盤を強化することを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびコンパイラ/リンカに関する基本的な知識が必要です。
-
Go言語のコンパイルとリンク:
- Goのソースコード (
.goファイル) は、まずコンパイラ (gcなど) によってオブジェクトファイル (.6ファイルなど、アーキテクチャによって異なる) にコンパイルされます。 - これらのオブジェクトファイルは、リンカ (
6lなど) によって結合され、実行可能なバイナリが生成されます。リンカは、シンボルの解決、コードの配置、ランタイムに必要なデータの準備などを行います。 6lは、当時のGo言語のx86-64アーキテクチャ向けリンカの名称です。Go 1.5以降、6lのようなアーキテクチャ固有のリンカはgo tool linkに統合され、GOARCH環境変数によってターゲットアーキテクチャが決定されるようになりました。しかし、このコミット当時は6lが直接使用されていました。
- Goのソースコード (
-
Go言語のリフレクション:
- Goのリフレクションは、
reflectパッケージを通じて提供される機能で、プログラムの実行中に変数や関数の型情報を検査したり、値を動的に操作したりすることを可能にします。 - Goのインターフェースは、リフレクションの基盤となる重要な概念です。インターフェース値は、内部的に「型情報」と「値データ」のペアとして表現されます。
sys.reflect(当時の内部関数名) は、Goのインターフェース値からその型情報と値データを抽出するような機能を提供していたと考えられます。
- Goのリフレクションは、
-
型シグネチャ (Type Signatures):
- Goのランタイムは、プログラム内で使用されるすべての型に関する情報を保持しています。これには、型の名前、構造、メソッドセットなどが含まれます。
gotypesigsは、これらの型情報をリンカが生成し、ランタイムがアクセスできるようにするためのデータ構造またはセクションを指します。これは、インターフェースの動的な型チェックやリフレクション操作の際に参照されます。Sigtは "Signature Type" の略で、個々の型シグネチャを表す構造体であると推測されます。
-
リンカにおけるデータセクション:
- リンカは、コンパイルされたコードだけでなく、プログラムが実行時に必要とする静的なデータもバイナリに埋め込みます。これには、文字列定数、グローバル変数、そして型情報のようなランタイムデータが含まれます。
gotypestringsは、Goの型名を文字列として格納するセクションであると推測されます。gotypesigsは、より詳細な型構造に関する情報を格納するセクションであると考えられます。
-
Prog構造体とnewdata関数:Progは、リンカが扱う命令やデータのエントリを表す内部構造体です。newdata関数は、リンカが新しいデータエントリを生成し、それを特定のシンボルに関連付けるために使用されるユーティリティ関数です。
技術的詳細
このコミットの技術的な詳細は、主にGoリンカ 6l とランタイム src/runtime/iface.c における型情報の管理とリフレクション機能の拡張に焦点を当てています。
1. gotypesigs のオンデマンド生成
src/cmd/6l/go.cの変更:definetypesigsという新しい関数が追加されました。この関数は、リンカがgotypesigsというシンボルへの未定義参照を検出した場合に呼び出されます。これは、「オンデマンド」生成のメカニズムを示しています。つまり、プログラムがgotypesigsを必要としない限り、このデータは生成されません。definetypesigsは、リンカが認識しているすべての型シグネチャ (sigt·プレフィックスを持つシンボル) を収集し、それらを名前でソートします。- ソートされた型シグネチャへのポインタの配列 (
gotypesigs) が、リンカによってデータセクションに書き込まれます。各ポインタはPtrSize(8バイト、x86-64の場合) のサイズを持ちます。 - 型シグネチャの総数 (
ngotypesigs) もint32型の変数としてデータセクションに書き込まれます。 - 既存の
definetypestrings関数も、newdata関数の呼び出し方法が変更されています。これは、リンカのデータ生成メカニズムの統一化を示唆しています。
src/cmd/6l/obj.cの変更:- リンカのメイン関数
mainで、definetypestrings()の後にdefinetypesigs()が呼び出されるようになりました。これにより、リンカの処理フローに型シグネチャの生成が組み込まれます。
- リンカのメイン関数
2. sys.unreflect の追加
src/cmd/gc/sys.goの変更:export func unreflect(uint64, string) (ret interface { });という新しい関数が追加されました。これは、Goのソースコードからsys.unreflectを呼び出すための宣言です。uint64は値データ、stringは型名を表すと推測されます。
src/cmd/gc/sysimport.cの変更:- コンパイラの内部で
sys.unreflectが認識されるように、そのシグネチャがsysimport文字列に追加されました。
- コンパイラの内部で
src/runtime/iface.cの変更:sys·unreflectというC言語の関数が実装されました。これがsys.unreflectの実際のランタイム実装です。- この関数は、
uint64の値データとstringの型名を受け取ります。 findtypeヘルパー関数が導入されました。この関数は、与えられた型名に対応するSigt(型シグネチャ) をgotypesigs配列から検索します。cmpstringcharsは、Goの文字列とCスタイルの文字列を比較するためのヘルパー関数です。fakesigtヘルパー関数は、gotypesigsに見つからない型名に対して、一時的なSigtを生成する役割を担っています。これは、未知の型を扱う際のエラーハンドリングや、動的な型生成の初期段階で利用される可能性があります。ただし、コメントにTODO(rsc): What to do here?とあることから、この部分の設計はまだ初期段階であったことが伺えます。sys·unreflectは、findtypeで取得したSigtと入力された値データ (it) を使用して、新しいインターフェース値 (retim,retit) を構築します。hashmap(sigi·inter, findtype(type), 0)の部分は、型シグネチャに基づいてインターフェースの内部表現を構築していることを示唆しています。
3. newdata 関数の変更
src/cmd/6l/pass.cの変更:newdata関数の宣言がstatic Prog*からProg*に変更されました。これは、この関数が他のファイルからもアクセス可能になったことを意味し、リンカのデータ生成ロジックがよりモジュール化されたことを示唆しています。
これらの変更は、Go言語のランタイムが、コンパイル時に生成された型情報をより効率的に利用し、動的な型操作(リフレクション)の能力を向上させるための重要なステップでした。特に sys.unreflect の導入は、Goのインターフェースが単なる抽象化のツールではなく、実行時の型変換と操作のための強力なメカニズムであることを示しています。
コアとなるコードの変更箇所
このコミットのコアとなるコードの変更は、主に以下のファイルに集中しています。
-
src/cmd/6l/go.c:definetypesigs関数の追加 (約50行の新規コード)。definetypestrings関数内のnewdata呼び出しの変更。symcmp関数の追加。
-
src/runtime/iface.c:sys·unreflect関数の追加 (約40行の新規コード)。fakesigt関数の追加。cmpstringchars関数の追加。findtype関数の追加。gotypesigsとngotypesigsの外部宣言。
-
src/cmd/gc/sys.go:export func unreflect(uint64, string) (ret interface { });の追加。
-
src/cmd/gc/sysimport.c:"export func sys.unreflect (? uint64, ? string) (ret interface { })\\n"の追加。
コアとなるコードの解説
src/cmd/6l/go.c における definetypesigs
void
definetypesigs(void)
{
int i, j, n;
Sym **all, *s, *x;
Prog *prog;
if(debug['g'])
return;
if(debug['v'])
Bprint(&bso, "%5.2f definetypesigs\\n", cputime());
s = lookup("gotypesigs", 0);
if(s->type == 0) // gotypesigsシンボルが参照されていない場合は何もしない
return;
if(s->type != SXREF) { // 既に定義されている場合はエラー
diag("gotypesigs already defined");
return;
}
s->type = SDATA; // シンボルをデータとしてマーク
// すべてのsigt·プレフィックスを持つシンボルをリストアップ
n = 0;
for(i=0; i<NHASH; i++)
for(x = hash[i]; x; x=x->link)
if(memcmp(x->name, "sigt·", 6) == 0)
n++;
all = mal(n*sizeof all[0]);
j = 0;
for(i=0; i<NHASH; i++)
for(x = hash[i]; x; x=x->link)
if(memcmp(x->name, "sigt·", 6) == 0)
all[j++] = x;
// 名前でソート
qsort(all, n, sizeof all[0], symcmp);
// ポインタの配列としてデータセクションに出力
enum { PtrSize = 8 }; // x86-64の場合
for(i=0; i<n; i++) {
prog = newdata(s, PtrSize*i, PtrSize, D_EXTERN);
prog->to.type = D_ADDR;
prog->to.index = D_EXTERN;
prog->to.sym = all[i]; // 各sigt·シンボルへのアドレス
}
s->value = PtrSize*n; // gotypesigsのサイズ
// ngotypesigs (型シグネチャの数) を出力
s = lookup("ngotypesigs", 0);
s->type = SDATA;
s->value = sizeof(int32);
prog = newdata(s, 0, sizeof(int32), D_EXTERN);
prog->to.offset = n; // 型シグネチャの総数
if(debug['v'])
Bprint(&bso, "%5.2f typestrings %d\\n", cputime(), n);
}
この関数は、リンカが gotypesigs シンボルへの参照を検出した場合に、プログラムで使用されるすべての型シグネチャ (sigt· で始まるシンボル) を収集し、それらをソートして、実行時にアクセス可能なデータ構造としてバイナリに埋め込みます。これにより、ランタイムは動的に型情報を参照できるようになります。
src/runtime/iface.c における sys·unreflect
extern Sigt *gotypesigs[]; // リンカが生成した型シグネチャの配列
extern int32 ngotypesigs; // 型シグネチャの数
// ... (fakesigt, cmpstringchars, findtype の実装) ...
void
sys·unreflect(uint64 it, string type, Map *retim, void *retit)
{
if(cmpstring(type, emptystring) == 0) { // 型名が空の場合はnilインターフェースを返す
retim = 0;
retit = 0;
} else {
// 型名に対応するSigt (型シグネチャ) を検索
retim = hashmap(sigi·inter, findtype(type), 0);
retit = (void*)it; // 値データを設定
}
FLUSH(&retim); // レジスタの値をメモリにフラッシュ
FLUSH(&retit);
}
sys·unreflect は、Goのランタイムが提供する内部関数で、sys.unreflect Go関数から呼び出されます。この関数は、生のデータ (it、uint64 として渡される) と型名 (type、string として渡される) を受け取り、それらからGoのインターフェース値を再構築します。findtype 関数を使って型名に対応する Sigt を gotypesigs 配列から探し、その型情報と値データを組み合わせて新しいインターフェース値を生成します。これにより、Goのリフレクション機能が、型情報とデータからインターフェースを「再構築」する能力を獲得しました。
関連リンク
- Go言語のリフレクションに関する公式ドキュメント (現代のGo): https://pkg.go.dev/reflect
- Go言語のインターフェースに関する公式ドキュメント: https://go.dev/tour/methods/10
参考にした情報源リンク
- Go言語の初期開発に関する情報 (Goの歴史): https://go.dev/doc/history
- Go言語のコンパイラとリンカの内部構造に関する一般的な情報 (Goのツールチェーン): https://go.dev/doc/articles/go_command.html (これは現代のドキュメントですが、当時の概念を理解するのに役立ちます)
- Go言語のインターフェースの内部表現に関する議論 (Stack Overflow, ブログ記事など): "Go interface internal representation" で検索すると多くの情報が見つかります。
- Go言語のリンカ
6lに関する情報 (初期のGoのドキュメントやソースコードコメント): "Go 6l linker" で検索すると、当時のリンカの役割に関する情報が見つかることがあります。 - Go言語の型システムに関する議論: "Go type system" で検索すると、型シグネチャの概念を理解するのに役立つ情報が見つかります。