コンパイラはどうしてそんなに信頼できるのでしょうか?


63

コンパイラは、その正確性が与えられているかのように日常的に使用しますが、コンパイラもプログラムであり、潜在的にバグを含む可能性があります。私はいつもこの絶対的な堅牢性について疑問に思っていました。コンパイラ自体のバグに遭遇したことはありますか?コンパイラ自体に問題があることをどのように認識しましたか?

...そして、どのようにてコンパイラを非常に信頼できるものにしますか?


16
まあ、それは...それでコンパイラをコンパイル
マイケル・K

31
彼らは絶対ではありません。コンパイラのバグがあります-それは非常にまれです。
ChrisF

5
バグは、コードスタックを下るにつれて少なくなります。アプリケーションのバグは、コンパイラのバグよりも一般的です。コンパイラのバグは、CPU(マイクロコード)のバグよりも一般的です。これは実際には良いニュースです。それが逆の場合は想像できますか?
-Fixee

あなたは、コンパイラどのように観察することによって何かを学ぶかもしれない多くのバグを持っている(SDCCなどが!)はるかに堅牢で信頼性が高いのgccなどのコンパイラは異なっています。
ベンジャクソン

回答:


96

それらは、数千または数百万の開発者による使用を通じて、時間をかけて徹底的にテストされます。

また、解決すべき問題は(非常に詳細な技術仕様によって)明確に定義されています。そして、タスクの性質は、ユニット/システムテストに容易に役立ちます。つまり、基本的には非常に特殊な形式のテキスト入力を別の種類の明確に定義された形式(ある種のバイトコードまたはマシンコード)に出力することです。そのため、テストケースの作成と検証は簡単です。

さらに、通常、バグも簡単に再現できます。正確なプラットフォームとコンパイラーのバージョン情報は別として、通常必要なのは入力コードです。コンパイラユーザー(開発者自身)が、平均的なコンピューターユーザーよりもはるかに正確で詳細なバグレポートを提供する傾向があることは言うまでもありません:-)


32
さらに、コンパイラコードの多くはおそらく正しいことが証明されています。
biziclop

@biziclop、良い点、これはタスクの特別な性質の別の結果です。
ペテルトレック

最初の完全なコンパイラは、ジョン・バッカスによって1957年にFORTRAN言語用に書かれました。つまり、コンパイラテクノロジは50年以上も前のことです。他の人が指摘しているように、コンパイラにはバグがありますが、それを正しくするためのかなりの時間がありました。
leed25d

実際、@ biziclopは、レクサーやパーサーなどの一部のコンポーネントを文法から自動生成することもできます。これにより、バグのリスクが再び低下します(レクサー/パーサージェネレーターが堅牢である場合-通常は上記とほぼ同じ理由で) 。
ペテルトレック

2
@Péter:レクサー/パーサージェネレーターは、より広く使用されているコンパイラーではかなり珍しいようです。ほとんどの場合、問題の言語の速度や十分なスマートパーサー/レクサージェネレーターの欠如など、さまざまな理由でレクサーとパーサーを手で記述します(C )。

61

これまでのすべての素晴らしい答えに加えて:

あなたには「オブザーバーバイアス」があります。バグを観察しないため、バグはないと仮定します。

私はあなたのように考えていました。それから、私はコンパイラを専門的に書き始めました、そして、私にあなたに言わせてください、そこには多くのバグがあります!

人々が書く残りのコードの99.999%のようなコードを書くので、バグは見えません。通常のビジネス問題を解決する通常の開発者であるため、メソッドを呼び出してループを実行し、空想や奇妙なことは何もしません。

コンパイラのバグは簡単に分析できる簡単な通常のコードシナリオにはないため、コンパイラのバグは表示されません。バグは、あなたが書いていない奇妙なコードの分析にあります。

一方、私は反対の観察者バイアスを持っています。私は毎日狂ったコードを毎日見ているので、コンパイラーはバグでぎっしり詰まっているようです。

任意の言語の言語仕様に座って、その言語のコンパイラー実装を採用し、コンパイラーが仕様を正確に実装しているかどうかを真剣に判断しようとした場合、あいまいなコーナーケースに集中して、すぐに見つけるでしょうコンパイラのバグは頻繁に発生します。例を挙げましょう。文字通り5分前に見つけたC#コンパイラのバグです。

static void N(ref int x){}
...
N(ref 123);

コンパイラーは3つのエラーを出します。

  • refまたはout引数は、割り当て可能な変数でなければなりません。
  • N(ref int x)の最適な一致には無効な引数があります。
  • 引数1に「ref」がありません。

明らかに、最初のエラーメッセージは正しく、3番目のメッセージはバグです。エラー生成アルゴリズムは、最初の引数が無効である理由を見つけようとし、それを見て、それが定数であることを確認し、「ref」としてマークされているかどうかを確認するためにソースコードに戻りません。むしろ、定数をrefとしてマークするほど愚かではないと想定し、refが欠落していると判断します。

正しい3番目のエラーメッセージが何であるかは明確ではありませんが、そうではありません。実際、2番目のエラーメッセージが正しいかどうかは不明です。オーバーロード解決が失敗する必要がありますか、または「ref 123」は正しいタイプのref引数として扱われるべきですか?正しい動作が何であるかを判断できるように、今度は考えてトリアージチームと話し合う必要があります。

このバグを見たことがないのは、おそらくrefで123を渡そうとするほど愚かなことはしないからです。また、最初のエラーメッセージが正しく、問題を診断するのに十分であるため、3番目のエラーメッセージが無意味であることに気付かないでしょう。しかし、私はコンパイラを壊そうとしているので、そのようなことをしようとしています。試した場合、バグも表示されます。


4
最初のエラーメッセージの後の適切なエラーメッセージは非常に困難です。

Sureöyコンパイラーを完全に「だまし」防止するよりも良いエネルギーを費やさなければなりません:)
Homde

2
@MKO:もちろん。多くのバグは修正されません。修正が非常に高価であり、シナリオが非常に曖昧であるため、コストがメリットによって正当化されない場合があります。そして時には、十分な人々が、あなたがそれを維持し続けなければならない「バギー」な振る舞いに依存するようになりました。
エリックリッパー

mmm ...エラーメッセージで終わるバグは「正常」です。コードを少しいじって動作させることは常に可能です。コンパイラがソースコードを受け入れ、「間違った」assmebly出力を生成するバグについてはどうでしょう。それは怖いです
GianlucaさんGhettini

7
@aij:「明らかに合法的なC#コード」の意味で正しい。たとえば、1つのインターフェイスにプロパティがあり、もう1つのインターフェイスにプロパティと同じ名前のメソッドがある2つのインターフェイスを継承するインターフェイスを含むプログラムを作成したことがありますか?仕様を見ずにすばやく:それは合法ですか?ここで、そのメソッドの呼び出しがあるとします。あいまいですか?等々。人々はいつも意味を成さないコードを書きます。しかし、合法的なC#であるかどうかを判断するために仕様の専門家である必要があるコードを記述することはほとんどありません。
エリックリッパー

51

私をからかってるの?コンパイラにもバグがあり、本当にロードされます。

GCCはおそらく地球上で最も有名なオープンソースコンパイラであり、バグデータベースを参照してくださいhttp : //gcc.gnu.org/bugzilla/buglist.cgi?product=gcc & component=c%2B%2B & resolution=-- -

GCC 3.2とGCC 3.2.3の間で、修正されたバグの数を確認してくださいhttp : //gcc.gnu.org/gcc-3.2/changes.html

Visual C ++のような他の人については、始めたくさえありません。

コンパイラの信頼性をどのように高めますか?まあ、彼らは最初から負荷と単体テストの負荷を持っています。そして、地球全体がそれらを使用しているので、テスターが不足していません。

でも真剣に、私が信じているコンパイラ開発者は優れたプログラマーであり、彼らは間違いないわけではありませんが、彼らは非常にパンチが効いています。


19

私は1日で2つまたは3つに遭遇しました。1つを検出する唯一の実際の方法は、アセンブリコードを調べることです。

コンパイラは他のポスターが指摘した理由で非常に信頼性が高いですが、コンパイラの信頼性は多くの場合自己満足の評価であると思います。プログラマは、コンパイラを標準と見なす傾向があります。何かがおかしくなった場合、あなたは自分の過ちを想定し(99.999%の確率で)、コードを変更してコンパイラーの問題を回避します。たとえば、高い最適化設定でコードがクラッシュするのは間違いなくコンパイラのバグですが、ほとんどの人はそれを少し低く設定して、バグを報告せずに先に進みます。


6
「コンパイラを標準として見る」ための+1。私は長い間、言語を本当に定義するものが2つあることを維持してきました。コンパイラと標準ライブラリです。標準文書は単なるドキュメントです。
メイソンウィーラー

8
@Mason:これは、1つの実装を持つ言語でうまく機能します。多くの言語の場合、標準が不可欠です。実際の影響として、何か不満がある場合、ベンダーはそれが標準の問題であれば真剣に受け止め、未定義の動作またはそのようなものであればあなたを追い払います。
デビッドソーンリー

2
@Mason-それは、標準に準拠している言語がほとんどないためです。それは、私見ですが、それは良いことではありません-どんな種類の深刻な開発にとっても、それは複数のOS世代に続くと予想されます。
ルーク

1
@David:より正確には、1つの主要な実装です。BorlandはPascalを定義し、MicrosoftはANSIとECMAの意見に関係なくC#を定義します。
dan04

4
C、C ++、またはFortranコードが高度な最適化の下でクラッシュすることは、多くの場合、コンパイラのバグよりも誤った入力コードです。私は非常に頻繁に、非常に新しいハードウェア向けに最近のコンパイラやプレリリースのコンパイラを使用していますが、最適化に関連する障害はかなり頻繁に見られます。これらの言語には未定義の動作の概念があり、不適合プログラムの処理を指定しないため、最終的にアセンブリに対してクラッシュをかなり慎重にチェックする必要があります。80〜90%のケースでは、コンパイラではなくアプリケーションコードが間違っています。
フィルミラー14

14

コンパイラには、正確さをもたらすいくつかのプロパティがあります。

  • ドメインは非常によく知られており、研究されています。問題は明確に定義されており、提供されるソリューションは明確に定義されています。
  • 自動テストは、コンパイラが正しく動作することを証明するのに十分です
  • コンパイラには非常に広範な、通常は公開の、自動化された単体テストがあり、他のほとんどのプログラムよりも多くのエラー領域をカバーするために時間をかけて蓄積されてきました
  • コンパイラーは結果を見る目が非常に多い

2
また、多くの場合、コードは古く、GCCは他の多くと同様に20年以上も経過しているため、多くのバグが長い時間をかけて解決されました。
ザカリーK

13

私たちは日常的にコンパイラを使用しています

...そして、どのようにしてコンパイラを非常に信頼できるものにしますか?

彼らはしません。します。ので、誰もが彼らのすべての時間を使用して、バグがすぐに発見されました。

それは数字ゲームです。コンパイラは非常に広範に使用されるため、バグ誰かによって引き起こされる可能性が非常に高いですが、ユーザーが非常に多いため、誰かが具体的にあなたになることはほとんどありません

したがって、それはあなたの視点に依存します:すべてのユーザーにわたって、コンパイラはバグが多いです。しかし、他の誰かがあなたがする前に同様のコードをコンパイルした可能性が非常に高いので、彼らバグだった場合、あなたではなく彼らにぶつかりますので、あなたの個々の観点からは、バグは決してない。

もちろん、それに加えて、他のすべての答えをここに追加できます。コンパイラーはよく研究され、よく理解されています。書くのは難しいという神話があります。つまり、非常に賢く、非常に優秀なプログラマーだけが実際にそれを書き込もうとします。通常、テストは簡単で、ストレステストやファズテストは簡単です。コンパイラーのユーザーは、それ自体が熟練したプログラマーである傾向があり、高品質のバグ報告につながります。そして、他の方法:コンパイラライターは、自分のコンパイラのユーザーである傾向があります。


11

すでにすべての答えに加えて、私は追加したいと思います:

私は信じている多くの時間は、ベンダーが自分のドッグフードを食べています。つまり、彼らはコンパイラを自分で書いています。


7

コンパイラのバグに頻繁に遭遇しました。

テスターが少ない暗いコーナーで見つけることができます。たとえば、GCCのバグを見つけるには、次のことを試してください。

  • クロスコンパイラを構築します。GCCの構成およびビルドスクリプトには、文字通り何十ものバグがあります。GCCのコンパイル中にビルドが失敗するものもあれば、クロスコンパイラが実行可能な実行可能ファイルをビルドできない場合もあります。
  • profile-bootstrapを使用して、GCCのItaniumバージョンをビルドします。GCC 4.4および4.5でこれを試した最後の数回は、動作するC ++例外ハンドラーの生成に失敗しました。最適化されていないビルドは正常に機能しました。私が報告したバグを修正することに誰も興味を示さなかったようで、GCC asmメモリ仕様の何が壊れているかを掘り下げようとした後、自分で修正するのをあきらめました。
  • ディストリビューションビルドスクリプトに従うことなく、最新のものから独自のGCJをビルドしてみてください。出来ることならどうぞ。

IA64(Itanium)には多くの問題があります。そのプラットフォームにはあまり多くの顧客がいませんので、最適化レベルを下げることは通常のバグ修正です。これは他の答えに戻ります。一般的なアーキテクチャの一般的な言語のコンパイラは通常、十分なユーザー露出と十分なサポートを十分に受けていますが、あまり人気のないアーキテクチャおよび/または言語に行くと、信頼性が損なわれることが予想されます。
オメガケンタウリ

@Omega:最適化を削減することは、誰もがしていることのようです。残念ながら、Itaniumのパフォーマンスを向上させるには、高度に最適化されたコンパイラが必要です。まあ...
斬Lynxの

私はあなたを聞く。率直に言って、このアーキテクチャは発表された時点ですでに時代遅れでしたが、幸いなことにAMDはIntelにx86-64を強制しました(多くのイボはそれほど悪くはありませんが)。ソースファイルを分割できる場合は、問題が発生している箇所を特定し、回避策を見つけることができます。それが重要なプラットフォームである場合に行うことですが、IA64ではありません。
オメガケンタウリ

@Omega:残念ながら、Itaniumは本当に好きです。素晴らしい建築物です。私はx86とx86-64は時代遅れだと考えていますが、もちろん決して死ぬことはありません。
ザンリンクス

x86は少し奇妙です。彼らは新しいものを追加し続けているので、一度に1つのいぼが成長します。しかし、アウトオブオーダー実行エンジンは非常にうまく機能し、新しいSSE => AVXスタッフは、コードを作成したい人にいくつかの本当の機能を提供します。確かに、ほとんど時代遅れのものに専念する多くのトランジスタがありますが、それはレガシー互換性のために支払う代償です。
オメガケンタウリ

5

いくつかの理由:

  • コンパイラライターは「自分のドッグフードを食べる」。
  • コンパイラは、CSのよく理解さている原則に基づいてます。
  • コンパイラは非常に明確な仕様に基づいて構築されています。
  • コンパイラーがテストされます。
  • コンパイラは常に非常に信頼できると限りません

4

通常、-O0は非常に優れています。実際、コンパイラのバグが疑われる場合、-O0と使用しようとしているレベルを比較します。最適化レベルが高いほど、リスクが高くなります。意図的にそうであるものもあり、ドキュメントでそのようにラベル付けされています。私は非常に多くの人(私の時間の間に少なくとも100人)に遭遇しましたが、それらは最近非常に稀になりつつあります。それにもかかわらず、優れたスペックマーク数(またはマーケティングにとって重要な他のベンチマーク)を追求する上で、限界を押し上げる誘惑は大きい。数年前に、ベンダーが(名前を付けずに)特別に明確にラベル付けされたコンパイルオプションではなく、括弧のデフォルトに違反することにした問題がありました。

漂遊メモリ参照などと比較してコンパイラエラーを診断するのは難しい場合があります。異なるオプションで再コンパイルすると、メモリ内のデータオブジェクトの相対的な位置が単純にスクランブルされる可能性があるため、ソースコードのハイゼンバグであるかバギーであるかはわかりませんコンパイラ。また、多くの最適化では、演算の順序が合法的に変更されたり、代数の代数的単純化が行われたりします。これらは、浮動小数点の丸めとアンダー/オーバーフローに関して異なるプロパティを持ちます。これらの効果を実際のバグから解きほぐすのは困難です。この理由から、ハードコアの浮動小数点計算は困難です。これは、バグや数値の感度を解くのが簡単ではないことが多いためです。


4

コンパイラのバグはそれほど珍しいことではありません。最も一般的なケースは、コンパイラが受け入れられるべきコードのエラーを報告するか、コンパイラが拒否されるべきコードを受け入れることです。


残念ながら、2番目のクラスのバグは見られません。コードがコンパイルされます=すべて順調です。だから、おそらく半分(2つのバグのクラス間の50-50のスプリット比を仮定して)バグの人ではなく、コンパイラのユニットテストの平均によって発見されていません
GianlucaさんGhettini

3

そう、昨日ASP.NETコンパイラのバグに遭遇しました。

ビューで厳密に型指定されたモデルを使用する場合、テンプレートに含めることができるパラメーターの数には制限があります。明らかに、4つ以上のテンプレートパラメータを取ることはできません。そのため、以下の両方の例では、コンパイラが処理するには多すぎます。

ViewUserControl<System.Tuple<type1, type2, type3, type4, type5>>

そのままコンパイルしませんtype5が、削除した場合はコンパイルします。

ViewUserControl<System.Tuple<MyModel, System.Func<type1, type2, type3, type4>>>

type4が削除されるとコンパイルされます。

これにSystem.Tupleは多くのオーバーロードがあり、最大16個のパラメーターを使用できることに注意してください(私が知っているのはクレイジーです)。


3

コンパイラ自体のバグに遭遇したことはありますか?コンパイラ自体に問題があることをどのように認識しましたか?

うん!

最も記憶に残る2つは、私が出会った最初の2つでした。それらは両方とも1985-7年頃の680x0 Mac用のLightspeed Cコンパイラに含まれていました。

1つ目は、状況によっては、整数のポストインクリメント演算子が何もしないことです。つまり、特定のコードでは、「i ++」は単に「i」に対して何もしませんでした。分解を見るまで、私は髪を引っ張っていました。それから、私はちょうど別の方法で増分を行い、バグレポートを提出しました。

2つ目はもう少し複雑で、実際には不適切な「機能」でしたが、問題が発生しました。初期のMacには、低レベルのディスク操作を行うための複雑なシステムがありました。何らかの理由で-おそらく小さな実行可能ファイルの作成に関係する-コンパイラがオブジェクトコード内でディスク操作命令をインプレースで生成するのではなく、Lightspeedコンパイラが実行時にディスク操作を生成する内部関数を呼び出しますスタック上の命令とそこにジャンプしました。

これは68000 CPUでうまく機能しましたが、68020 CPUで同じコードを実行すると、しばしば奇妙なことをします。68020の新しい機能は、プリミティブ命令の256バイト命令キャッシュであることが判明しました。これはCPUキャッシュを使用した初期の段階であり、キャッシュが「ダーティ」であり、補充が必要であるという概念はありませんでした。MotorolaのCPU設計者は、自己修正コードについて考えていなかったと思います。したがって、実行シーケンスで2つのディスク操作を十分に近づけて実行し、Lightspeedランタイムがスタック上の同じ場所に実際の命令を構築した場合、CPUは命令キャッシュがヒットしたと誤って判断し、最初のディスク操作を2回実行します。

繰り返しになりますが、それを理解するには、逆アセンブラーを掘り下げ、低レベルデバッガーで多くのシングルステップを実行する必要がありました。私の回避策は、すべてのディスク操作の前に256個の「NOP」命令を実行する関数の呼び出しを追加し、命令キャッシュをフラッディング(つまりクリア)することでした。

それから25年以上にわたり、時間の経過とともにコンパイラのバグが少なくなっています。その理由はいくつかあると思います。

  • コンパイラの検証テストのセットは増え続けています。
  • 現代のコンパイラは通常、2つ以上の部分に分かれています。1つはプラットフォームに依存しないコード(たとえば、仮想CPUとみなされるものを対象とするLLVM)を生成し、もう1つは実際のターゲットハードウェアの命令に変換します。マルチプラットフォームコンパイラでは、最初の部分があらゆる場所で使用されるため、実世界のテストが大量に行われます。

自己修正コードを避ける理由の1つ。
テクノフィル

3

5.5年前にTurbo Pascalで明白なエラーを発見しました。コンパイラの前のバージョン(5.0)と次のバージョン(6.0)のどちらにもエラーがありません。そして、テストは簡単なはずでしたが、これはまったくコーナーケースではなかったためです(一般的に使用されていない呼び出しだけです)。

一般に、確かに商用のコンパイラビルダー(趣味のプロジェクトではなく)には、非常に広範なQAとテスト手順が用意されています。彼らは彼らのコンパイラが彼らの主力プロジェクトであり、欠陥が彼らにとって非常に悪く見えることを知っています。ソフトウェア開発者は容赦ない束であり、ツールのサプライヤは、サプライヤからの修正を待つのではなく、代替品を探しに行く可能性があります。例。他の多くの業界ではそうではないので、深刻なバグの結果としてのコンパイラーメーカーへの潜在的な損失は、ビデオ編集ソフトウェアのメーカーよりもはるかに大きいです。


2

-O0と-O2でコンパイルしたときのソフトウェアの動作が異なる場合、コンパイラのバグが見つかりました。

ソフトウェアの動作が予想と異なる場合は、コードにバグがある可能性があります。


8
必ずしも。CおよびC ++では、迷惑な量の未指定および未定義の動作があり、最適化レベルまたは月の位相、またはダウジョーンズインデックスの動きに基づいて正当に変化する可能性があります。このテストは、より厳密に定義された言語で機能します。
デビッドソーンリー

2

コンパイラのバグは発生しますが、奇妙なコーナーでそれらを見つける傾向があります...

1990年代にDigital Equipment CorporationのVAX VMS Cコンパイラに奇妙なバグがありました

(当時のファッションのように、私はベルトにタマネギを着ていました)

forループの前にある無関係なセミコロンは、forループの本体としてコンパイルされます。

f(){...}
;
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++){
     puts("hello");
  }
}

問題のコンパイラでは、ループは1回だけ実行されます。

見る

f(){...}
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++) ;  /* empty statement for fun */

  {
     puts("hello");
  }
}

それには時間がかかりました。

作業経験のある学生に与えていた古いバージョンのPIC Cコンパイラは、高優先度割り込みを正しく使用するコードを生成できませんでした。2〜3年待ってからアップグレードする必要がありました。

MSVC 6コンパイラにはリンカに気の利いたバグがあり、セグメンテーションエラーが発生し、理由もなく時々死にます。通常、クリーンビルドで修正されました(ただし、常にため息があるわけではありません)。


2

アビオニクスソフトウェアなどの一部のドメインでは、コードとハードウェア、およびコンパイラに非常に高い認証要件があります。この最後の部分について、Compcertと呼ばれる正式に検証されたCコンパイラを作成することを目的としたプロジェクトがあります。理論的には、この種のコンパイラーは信頼できるものです。


1

私はいくつかのコンパイラのバグを見てきましたが、私自身(特にF#で)をいくつか報告しました。

とは言っても、コンパイラを書く人は一般に、コードの数学的な意味を本当に意識させるコンピュータサイエンスの厳密な概念に非常に満足しているため、コンパイラのバグはまれだと思います。

それらのほとんどは、ラムダ計算、形式的検証、表示的意味論などのようなものにおそらく非常に精通している-私のような平均的なプログラマーがかろうじて理解できるもの。

また、通常、コンパイラには入力から出力へのかなり簡単なマッピングがあるため、プログラミング言語のデバッグは、おそらくブログエンジンなどのデバッグよりもはるかに簡単です。


1

C#コンパイラでバグを見つけたのはそれほど前ではなく、Eric Lippert(C#デザインチームのメンバー)がどのようにバグがここにあったのかを理解できたことがわかります

すでに与えられた答えに加えて、私はさらにいくつかのことを追加したいと思います。多くの場合、コンパイラの設計者は非常に優秀なプログラマです。コンパイラは非常に重要です。ほとんどのプログラミングはコンパイラを使用して行われるため、コンパイラは高品質であることが不可欠です。したがって、コンパイラーを作成する企業にとっては、最高の人材を配置すること(または、少なくとも非常に優れたもの:コンパイラー設計が気に入らないかもしれません)にとって最大の利益になります。Microsoftは、CおよびC ++コンパイラが適切に動作することを非常に望んでいます。

また、本当に複雑なコンパイラを構築している場合、一緒にハックすることはできません。コンパイラの背後にあるロジックは非常に複雑であり、形式化が容易です。したがって、これらのプログラムは多くの場合、非常に「堅牢」で一般的な方法で構築され、バグが少なくなる傾向があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.