Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13954] ファイルの概要

このコミットは、Go言語の実験的な型システム (exp/types/staging) における gcimporter パッケージの更新に関するものです。具体的には、types.go および const.go で定義された新しい型定義に合わせるための、主に軽微な変更が含まれています。

変更されたファイルは以下の通りです。

  • src/pkg/exp/types/staging/exportdata.go: 新規追加されたファイルで、Goコンパイラが生成するオブジェクトファイルやアーカイブファイルからエクスポートデータセクションを見つけるためのロジックを実装しています。
  • src/pkg/exp/types/staging/gcimporter.go: 新規追加されたファイルで、gc (Goコンパイラ) が生成したオブジェクトファイルからパッケージをインポートするための ast.Importer の実装を含んでいます。
  • src/pkg/exp/types/staging/gcimporter_test.go: 新規追加されたテストファイルで、gcimporter の機能、特にパッケージのインポートと型情報の正確な読み込みを検証します。
  • src/pkg/exp/types/staging/testdata/exports.go: 新規追加されたテストデータファイルで、gcimporter_test.go が使用するオブジェクトファイルを生成するためのGoソースコードです。様々なGoの型、定数、変数、関数、メソッドの定義が含まれています。

コミット

commit 1785bfca6b0bbf84c44b438968f328c02aeee73d
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Sep 25 17:39:02 2012 -0700

    exp/types/staging: updated gcimporter
    
    Mostly minor changes to match the new
    definitions in types.go and const.go.
    
    R=rsc, r
    CC=golang-dev
    https://golang.org/cl/6506101

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/1785bfca6b0bbf84c44b438968f328c02aeee73d

元コミット内容

    exp/types/staging: updated gcimporter
    
    Mostly minor changes to match the new
    definitions in types.go and const.go.
    
    R=rsc, r
    CC=golang-dev
    https://golang.org/cl/6506101

変更の背景

このコミットの主な背景は、Go言語の型システム (exp/types) が進化する中で、その内部表現や定義が変更されたことにあります。特に、types.goconst.go というGo言語の型システムの中核をなすファイルで新しい定義が導入されたため、それらの変更に合わせて、コンパイラが生成するエクスポートデータを読み込む gcimporter も更新する必要がありました。

exp/types は、Go言語のコンパイラやツールがコードの型チェックを行う際に使用する、型情報の抽象的な表現を提供するパッケージです。gcimporter は、Goコンパイラ (gc) がコンパイル時に生成するパッケージのエクスポートデータ(他のパッケージから参照可能な型、関数、変数などの情報)を読み込み、それを exp/types の内部表現に変換する役割を担っています。

したがって、types.goconst.go の変更は、エクスポートデータのフォーマットや内容に影響を与える可能性があり、gcimporter がその新しいフォーマットを正しく解釈できるように更新される必要がありました。このコミットは、その整合性を保つためのメンテナンス作業の一環として行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念と関連パッケージに関する知識が必要です。

  • Go言語のパッケージシステム: Go言語のコードはパッケージに分割され、他のパッケージの公開された(エクスポートされた)エンティティ(型、関数、変数など)を利用できます。コンパイラは、パッケージをコンパイルする際に、そのパッケージのエクスポート情報を生成します。
  • go/ast パッケージ: Go言語のソースコードの抽象構文木 (AST: Abstract Syntax Tree) を表現するためのパッケージです。コンパイラやリンターなどのツールがGoコードを解析する際に使用します。ast.Object はAST内の名前付きエンティティ(変数、関数、型など)を表し、ast.Scope は識別子のスコープを管理します。
  • go/token パッケージ: Go言語のソースコードのトークン(識別子、キーワード、演算子など)とソースコード上の位置情報を扱うパッケージです。
  • go/build パッケージ: Go言語のパッケージのビルドプロセスに関する情報(パッケージの検索、依存関係の解決など)を提供するパッケージです。build.Import 関数は、インポートパスからパッケージの情報を解決するために使用されます。
  • bufio パッケージ: バッファリングされたI/Oを提供するパッケージです。ファイルからの効率的な読み込みに使用されます。
  • text/scanner パッケージ: テキストをトークンに分割するためのスキャナーを提供します。gcimporter は、エクスポートデータを解析するためにこれを使用します。
  • math/big パッケージ: 任意精度の整数、有理数、浮動小数点数を扱うためのパッケージです。Goのエクスポートデータには、任意精度の定数も含まれるため、これらを正確に表現するために使用されます。
  • os パッケージ: オペレーティングシステムとのインタフェースを提供するパッケージです。ファイル操作(ファイルのオープン、ディレクトリの取得など)に使用されます。
  • path/filepath パッケージ: ファイルパスを操作するためのユーティリティを提供するパッケージです。プラットフォームに依存しないパスの結合などに使用されます。
  • strconv パッケージ: 文字列と数値の変換を行うパッケージです。エクスポートデータ内の数値リテラルを解析する際に使用されます。
  • strings パッケージ: 文字列操作のためのユーティリティを提供するパッケージです。
  • exp/types パッケージ: Go言語の型システムを表現するための実験的なパッケージです。このパッケージは、Goの型チェックやセマンティック分析の基盤となります。gcimporter は、このパッケージの型表現にエクスポートデータをマッピングします。
  • Goコンパイラ (gc) のエクスポートデータ: Goコンパイラは、コンパイルされたパッケージの公開されたAPI(型、関数、変数など)を記述したエクスポートデータを生成します。このデータは、他のパッケージがそのパッケージをインポートする際に使用されます。通常、.a (アーカイブ) ファイルや、特定のアーキテクチャに特化したファイル (.5, .6, .8 など) の中に埋め込まれています。

技術的詳細

このコミットで追加・変更された gcimporter は、Goコンパイラが生成するエクスポートデータを解析し、それを go/ast パッケージの ast.Objectexp/types パッケージの型表現に変換する役割を担っています。

主要なコンポーネントと機能は以下の通りです。

  1. FindGcExportData 関数 (exportdata.go):

    • この関数は、Goコンパイラが生成したオブジェクトファイルまたはアーカイブファイルの中から、エクスポートデータが格納されているセクションの開始位置を見つけます。
    • Goのアーカイブファイル (.a ファイル) は、UNIXの ar 形式に似た構造を持っています。この関数は、アーカイブヘッダを読み込み、__.SYMDEF (または __.GOSYMDEF) と __.PKGDEF という特殊なエントリを検索します。__.PKGDEF がエクスポートデータを含んでいます。
    • オブジェクトファイルの場合、"go object " で始まる行を読み込み、"$$" で区切られたエクスポートデータの開始位置までスキップします。
    • この関数は、bufio.Reader を使用して効率的にファイルを読み進めます。
  2. gcParser 構造体 (gcimporter.go):

    • gcParser は、エクスポートデータを実際に解析するためのパーサーです。
    • text/scanner.Scanner を内部に持ち、エクスポートデータをトークンに分割します。
    • init メソッドで初期化され、ファイル名、パッケージID、入力ストリーム、および既にインポートされたパッケージのマップを受け取ります。
    • next メソッドで次のトークンを読み込みます。
    • declare メソッドは、新しい ast.Object を作成し、現在のスコープに挿入します。これにより、インポートされたエンティティが型システム内で利用可能になります。
    • エラーハンドリングは、importError 型のパニックとリカバリによって行われます。
  3. エクスポートデータの解析ロジック (gcimporter.go):

    • gcParser は、エクスポートデータの様々なセクションを解析するためのメソッド群を持っています。
    • parsePkgId(): パッケージのインポートパスを解析し、対応する ast.Object (パッケージオブジェクト) を返します。unsafe パッケージは特別扱いされます。
    • parseExportedName(): @ プレフィックスを持つエクスポートされた名前(例: @\"math\".Pi)を解析し、そのパッケージと名前を抽出します。
    • 型解析 (parseType(), parseBasicType(), parseArrayType(), parseMapType(), parseStructType(), parseInterfaceType(), parseChanType(), parseSignature()):
      • Goの様々な型(基本型、配列、スライス、構造体、インターフェース、マップ、チャネル、関数シグネチャ、ポインタ)のエクスポート表現を解析し、exp/types パッケージの対応する型構造体 (Array, Map, Struct, Interface, Chan, Signature, Pointer など) に変換します。
      • 構造体のフィールドやインターフェースのメソッドも再帰的に解析されます。
      • 匿名フィールドや可変長引数 (...) も適切に処理されます。
    • 宣言解析 (parseImportDecl(), parseConstDecl(), parseTypeDecl(), parseVarDecl(), parseFuncDecl(), parseMethodDecl()):
      • エクスポートデータ内のインポート宣言、定数宣言、型宣言、変数宣言、関数宣言、メソッド宣言を解析します。
      • 定数宣言では、math/big パッケージを使用して任意精度の数値リテラルを正確に解析します。
      • 型宣言では、NamedType を使用して、前方参照(型が定義される前に参照されるケース)を適切に処理します。
      • 関数やメソッドの本体 (FuncBody) は、エクスポートデータには含まれないため、単にスキップされます。
    • parseExport(): エクスポートデータの最上位の解析エントリポイントです。package 句を読み込み、その後に続くすべての宣言を $$ 終端記号まで解析します。

この gcimporter の実装は、Goコンパイラが生成するバイナリ形式のエクスポートデータを、Goのツールが利用できる抽象構文木や型システム表現に変換するための重要なブリッジとして機能します。これにより、Goのツールは、ソースコードを再解析することなく、コンパイル済みのパッケージのAPI情報を効率的に取得できます。

コアとなるコードの変更箇所

このコミットでは、以下の4つのファイルが新規追加されています。

  1. src/pkg/exp/types/staging/exportdata.go:

    • FindGcExportData 関数が追加されています。この関数は、Goのオブジェクトファイルやアーカイブファイルからエクスポートデータセクションを見つけるためのロジックを実装しています。
    • readGopackHeader ヘルパー関数も含まれており、Goのアーカイブファイルのヘッダを解析します。
  2. src/pkg/exp/types/staging/gcimporter.go:

    • FindPkg 関数が追加されています。これは、インポートパスに基づいてパッケージのファイル名とユニークなIDを検索します。
    • GcImportData 関数が追加されています。これは、FindGcExportData で見つけられたエクスポートデータのリーダーを受け取り、パッケージをインポートします。
    • GcImport 関数が追加されています。これは ast.Importer インターフェースを満たし、インポートパスからパッケージ全体をインポートする主要なエントリポイントです。
    • gcParser 構造体とその多数のメソッドが追加されています。これらは、エクスポートデータの各要素(型、定数、変数、関数、メソッドなど)を解析するための中心的なロジックを構成します。
      • init, next, declare, エラーハンドリング関連のメソッド。
      • parsePkgId, parseDotIdent, parseExportedName など、名前解決に関するメソッド。
      • parseBasicType, parseArrayType, parseMapType, parseName, parseField, parseStructType, parseParameter, parseParameters, parseSignature, parseInterfaceType, parseChanType, parseType など、Goの型を解析するメソッド群。
      • parseImportDecl, parseInt, parseNumber, parseConstDecl, parseTypeDecl, parseVarDecl, parseFuncBody, parseFuncDecl, parseMethodDecl, parseDecl など、Goの宣言を解析するメソッド群。
      • parseExport は、エクスポートデータ全体の解析を開始するメソッドです。
  3. src/pkg/exp/types/staging/gcimporter_test.go:

    • gcPath 変数と init 関数が追加され、Goコンパイラのパスを特定します。
    • compile ヘルパー関数が追加され、テスト用のGoファイルをコンパイルします。
    • imports グローバルマップが定義され、インポートされたパッケージを保持します。
    • testPathtestDir 関数が追加され、特定のパスやディレクトリ内のパッケージのインポートをテストします。
    • TestGcImport 関数が追加され、gcimporter を使用してパッケージをインポートする基本的なテストを行います。
    • importedObjectTests スライスと TestGcImportedTypes 関数が追加され、unsafe.Pointer, math.Pi, io.Reader などの特定のインポートされたオブジェクトの型と種類が正しいことを検証します。
  4. src/pkg/exp/types/staging/testdata/exports.go:

    • exports パッケージとして定義されたGoソースファイルです。
    • 様々な種類の定数 (C0 から C7)、型 (T1 から T28)、変数 (V0, V1)、関数 (F1 から F5) が定義されています。これらは、gcimporter が正しくエクスポートデータを解析できることを確認するためのテストケースとして機能します。
    • 特に、複雑な型(構造体、インターフェース、チャネル、関数シグネチャ、再帰的な型定義)や、ドット付き識別子 (Issue 3682) のテストが含まれています。

これらのファイルは、Go言語の型システムがコンパイル済みパッケージの情報をどのように読み込み、内部表現に変換するかを示す、gcimporter の初期実装を構成しています。

コアとなるコードの解説

このコミットのコアとなるコードは、src/pkg/exp/types/staging/gcimporter.gosrc/pkg/exp/types/staging/exportdata.go にあります。

exportdata.go の解説

  • FindGcExportData(r *bufio.Reader) (err error):
    • この関数は、Goコンパイラが生成したバイナリファイル(オブジェクトファイルまたはアーカイブファイル)から、エクスポートデータが始まる位置を特定します。
    • Goのアーカイブファイル (.a ファイル) の場合、!<arch>\n で始まるシグネチャをチェックし、その後 __.SYMDEF (または __.GOSYMDEF) と __.PKGDEF という特殊なアーカイブエントリを検索します。__.PKGDEF が実際のGoのエクスポートデータを含んでいます。
    • オブジェクトファイルの場合、ファイルが go object で始まることを確認し、$$ というマーカーが現れるまで行を読み飛ばします。この $$ の後にエクスポートデータが続きます。
    • この関数は、bufio.Reader を使用して効率的にファイルを読み込み、エクスポートデータの開始位置にリーダーを配置します。これにより、後続のパーサーが直接エクスポートデータを読み取れるようになります。

gcimporter.go の解説

  • FindPkg(path, srcDir string) (filename, id string):

    • Goの build パッケージを利用して、与えられたインポートパス (path) とソースディレクトリ (srcDir) から、対応するコンパイル済みパッケージのファイル名とユニークなパッケージIDを特定します。
    • Goのパッケージファイルは通常、.a.5.6.8 などの拡張子を持ちます。この関数はこれらの拡張子を試行してファイルを見つけます。
  • GcImportData(imports map[string]*ast.Object, filename, id string, data *bufio.Reader) (pkg *ast.Object, err error):

    • この関数は、既にエクスポートデータの開始位置に配置された bufio.Reader (data) を受け取り、実際のパッケージインポート処理を行います。
    • 内部で gcParser を初期化し、parseExport() メソッドを呼び出してエクスポートデータを解析します。
    • エラーハンドリングのために panic/recover メカニズムを使用し、importError 型のパニックを捕捉します。
  • GcImport(imports map[string]*ast.Object, path string) (pkg *ast.Object, err error):

    • ast.Importer インターフェースを満たす主要な関数です。
    • インポートパス (path) を受け取り、FindPkg を使用してパッケージファイルを見つけます。
    • ファイルを開き、FindGcExportData を呼び出してエクスポートデータの開始位置に移動します。
    • 最終的に GcImportData を呼び出してパッケージをインポートし、結果の ast.Object (パッケージオブジェクト) を返します。
    • unsafe パッケージは特別に処理され、組み込みの Unsafe オブジェクトが返されます。
  • gcParser 構造体と解析メソッド群:

    • gcParser は、エクスポートデータの構文解析を行う中心的なコンポーネントです。
    • scanner.Scanner: text/scanner パッケージの Scanner を使用して、エクスポートデータをトークン(識別子、数値、文字列、記号など)に分割します。
    • declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast.Object:
      • 新しい名前付きオブジェクト(定数、型、変数、関数など)を現在のスコープに宣言します。
      • 既に同じ名前のオブジェクトがスコープに存在する場合は、既存のオブジェクトを返します。これは、前方参照や複数回インポートされるケースを処理するために重要です。
      • 型オブジェクトの場合、NamedType を使用して、基底型が後で解決される可能性のある名前付き型を表現します。
    • 型解析メソッド (parseType, parseStructType, parseInterfaceType, parseSignature など):
      • エクスポートデータ内のGoの型表現(例: struct { a int; b float32 }, interface { m1() }, func(x int) float32)を読み込み、exp/types パッケージの対応する型構造体(Struct, Interface, Signature など)に変換します。
      • これらのメソッドは再帰的に呼び出され、複雑なネストされた型も処理できます。
    • 宣言解析メソッド (parseConstDecl, parseTypeDecl, parseVarDecl, parseFuncDecl, parseMethodDecl など):
      • エクスポートデータ内の各種宣言(定数、型、変数、関数、メソッド)を解析し、対応する ast.Object を作成し、その型情報を設定します。
      • 定数については、math/big を使用して任意精度の数値(整数、浮動小数点数、複素数)を正確に表現します。
    • parseExport() *ast.Object:
      • エクスポートデータの最上位の解析メソッドです。
      • package 句を読み込み、その後 $$ 終端記号が現れるまで、すべての宣言 (parseDecl()) を繰り返し解析します。
      • これにより、パッケージ全体のエクスポートされたAPIが ast.Objectexp/types の型表現として構築されます。

これらのコードは、Go言語のコンパイラと型システムの間で、コンパイル済みパッケージの型情報をやり取りするための基盤を提供します。これにより、Goのツールは、ソースコードを再解析することなく、型チェックやコード補完などの機能を実現できます。

関連リンク

参考にした情報源リンク

Web検索では、この特定のコミットに関する追加の情報は見つかりませんでした。解説は、コミットメッセージ、変更されたコード、およびGo言語の一般的な知識に基づいて生成されています。