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

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

コミット

コミット: e8a426aebe4968d5a27068e5aed2970a4c38f686
作成者: Robert Griesemer gri@golang.org
日付: 2011年10月20日 12:37:13 -0700
コミットメッセージ: go/ast: use single-element map in test - Avoids test failure due to undefined map iteration order.

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

https://github.com/golang/go/commit/e8a426aebe4968d5a27068e5aed2970a4c38f686

元コミット内容

go/ast: use single-element map in test

Avoids test failure due to undefined map iteration order.

R=rsc, iant, iant
CC=golang-dev
https://golang.org/cl/5297048

変更ファイル: src/pkg/go/ast/print_test.go (3行追加、4行削除)

差分内容:

// maps
-{map[string]int{"a": 1, "b": 2},
-  `0  map[string] int (len = 2) {
+{map[string]int{"a": 1},
+  `0  map[string] int (len = 1) {
   1  .  "a": 1
-  2  .  "b": 2
-  3  }`},
+  2  }`},

変更の背景

このコミットは、Go言語のマップ反復順序の非決定性によって引き起こされるテストの失敗を修正するために実施されました。2011年10月という時期は、Go言語の安定版リリース(Go 1.0)の直前であり、言語仕様の確定とテストスイートの安定化が重要な課題となっていました。

具体的には、2011年10月にコードレビュー5285042として提出された「runtime: random offset for map iteration」の変更により、マップの反復順序が意図的にランダム化されました。この変更によって、既存のテストケースで複数要素を持つマップを使用している箇所において、期待される出力順序が保証されなくなり、テストが不安定になるという問題が発生しました。

前提知識の解説

Go言語のマップ反復順序について

Go言語において、マップの反復順序は仕様上「未定義」とされており、同じマップに対して複数回の反復を行っても、毎回同じ順序が保証されません。この設計は以下の理由によります:

  1. セキュリティ上の考慮: ハッシュ衝突攻撃(Hash Collision Attack)の防止
  2. プログラムの堅牢性: 反復順序に依存するコードの早期発見と修正
  3. 実装の自由度: 異なるプラットフォーム間での実装の違いを許容

Go 1.0以前の状況

Go 1.0リリース以前は、マップの反復順序は比較的予測可能でした。開発者は無意識のうちにこの順序に依存したコードを書いていましたが、これは以下の問題を引き起こしていました:

  • プラットフォーム間の互換性問題: 異なるアーキテクチャで異なる結果
  • 将来の実装変更への脆弱性: 内部実装の変更でコードが破綻する可能性
  • セキュリティリスク: 予測可能なハッシュ順序による攻撃リスク

go/astパッケージとは

go/astパッケージは、Go言語の抽象構文木(Abstract Syntax Tree)を操作するための標準ライブラリです。このパッケージは以下の機能を提供します:

  • 構文解析: Go言語のソースコードを構文木に変換
  • AST操作: 構文木の検査、変換、操作
  • デバッグ支援: 構文木の可視化とデバッグ機能

技術的詳細

マップ反復順序のランダム化メカニズム

Go言語では、マップの反復順序をランダム化するために以下の技術が使用されています:

  1. 開始オフセットのランダム化: 反復開始位置を疑似乱数で決定
  2. ハッシュ関数のシード化: ハッシュ計算に疑似乱数シードを使用
  3. バケット内順序の変更: 小さなマップでも順序を変更

テストの安定化手法

この問題に対する一般的な解決策として、以下の手法が使用されます:

  1. 単一要素マップの使用: 順序が問題にならない最小構成
  2. 期待値の柔軟化: 複数の可能な出力パターンを許容
  3. 順序に依存しない検証: 順序を考慮しない等価性チェック

go/ast/print.goの役割

go/ast/print.goファイルは、AST(抽象構文木)の内容を人間が読める形式で出力する機能を提供します。このファイルの主な機能は:

  • 構造体の再帰的表示: ネストした構造体を階層的に表示
  • 型情報の表示: 各フィールドの型情報を含む詳細表示
  • デバッグ支援: 開発者がASTの内容を理解しやすくする

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

変更されたのはsrc/pkg/go/ast/print_test.goの23-31行目です:

// 変更前
{map[string]int{"a": 1, "b": 2},
  `0  map[string] int (len = 2) {
  1  .  "a": 1
  2  .  "b": 2
  3  }`},

// 変更後
{map[string]int{"a": 1},
  `0  map[string] int (len = 1) {
  1  .  "a": 1
  2  }`},

この変更により、テストケースで使用されるマップの要素数が2から1に減少し、期待される出力の行数も対応して調整されました。

コアとなるコードの解説

テストデータ構造

このテストケースは、tests変数に定義されたテストデータの一部です。各テストケースは以下の構造を持ちます:

var tests = []struct {
    input    interface{}  // テスト対象のデータ
    expected string       // 期待される出力文字列
}{
    // ... テストケースの定義
}

マップの表示形式

go/ast/print.goでは、マップを以下の形式で表示します:

0  map[string] int (len = N) {
1  .  "key1": value1
2  .  "key2": value2
...
N  }

この形式では:

  • 0は階層レベルを示す
  • len = Nはマップの要素数
  • 各要素は"key": valueの形式で表示
  • 数字は表示行の連番

単一要素マップの利点

単一要素マップを使用することで:

  1. 順序の問題が解消: 要素が1つなので反復順序は常に同じ
  2. テストの安定化: 実行環境に関係なく同じ結果
  3. テストの意図の明確化: マップの表示機能をテストする本来の目的に集中

テストの目的

このテストケースの主な目的は:

  • マップ型のデータ構造が正しく表示されることの確認
  • 階層表示機能の動作確認
  • 型情報(map[string]int)の正確な表示確認

順序に依存しない形でこれらの目的を達成するために、単一要素マップが最適な選択となります。

関連リンク

参考にした情報源リンク