Layout of a Solidity Source File — Solidity 0.4.3-develop documentation
Solidityのソースファイルのレイアウト仕様について↑ざっとみておく。
ソースファイルには任意の数のcontractの定義を記述できる。
Version Pragma
ソースファイルにはversion pragmaと呼ばれる注釈を付けることができる。これは将来のコンパイラがバージョンアップした際に互換性の無い変更が導入された際、そのコンパイラでソースがコンパイルされるのを防ぐための機能。基本的に互換性のある形で変更はされるだろうけど、シンタックスの変更などが発生することもある。そのため重大な変更が含まれている場合は、リリースノートのをよく読むようにと。そういったリリースではバージョン番号のx.0.0
もしくは0.x.0
のxの部分が更新される。
version pragmaは以下のように記載する。
pragma solidity ^0.4.0;
↑のように記載されたソースファイルは、コンパイラのバージョンが0.4.0 よりも小さいバージョンのコンパイラではコンパイルされず、^
が付いているので0.5.0以上のコンパイラでも動作しない。こういった指定によって、バージョン0.5.0までは破壊的な変化は起きず、自分が書いたコードが意図した方法でコンパイラされることが保証できる。
ただbugfixのリリースなどがあるのでバージョンを完全に固定はしない(なのでbugfixリリースによってバージョンがあがった0.4.1とかは↑の定義のまま利用可能)。
また、↑よりも複雑なルールを指定することもでき、その方式はnpmで使われるのと同様(package.jsonで指定する書き方)。
他のソースファイルのインポート
シンタックスとセマンティクス
SolidityはJavaScript(ECMAScript6から)のdefault export
のコンセプトと似たimport
文をサポートする。
グローバルレベルでは以下の形式でimport
文が使える。
import "filename";
このimport
文は“filename”
から全てのグローバルシンボルをインポートし、現在のグローバルスコープに入れる。(ES6の仕様とは異なる)
import * as symbolName from "filename";
↑は“filename”
の全てのグローバルシンボルを持つ、新しいグローバルシンボルsymbolName
を作っている。
import {symbol1 as alias, symbol2} from "filename";
↑は新しいグローバルシンボルalias
とsymbol2
を作成し、それぞれ“filename”
からsymbol1
とsymbol2
を参照する。
以下の構文はES6の一部ではないが、おそらく便利
import "filename" as symbolName;
↑はimport * as symbolName from "filename";
と同じ意味になる。
パス
↑でfilename
と記載したパス名の記載内容について、/
はディレクトリセパレータとして、.
は現在のディレクトリ、..
は1つ上のディレクトリとして扱われる。パス名が.
から始まらない場合は絶対パスとして扱われる。
現在のソースファイルと同じディレクトリにあるファイルx
をインポートしたい場合は、import "./x" as x;
と書く。もしimport "x" as x;
と書いたら、グローバル “include directory”内の別のファイルを参照することになる。
実際のパスの解決をどうするかはコンパイラに依存する。一般的に、ディレクトリ階層を厳密にローカルのファイルシステムにマップする必要はなく、ipfsやhttp、git等を経由して検出されたリソースにマップすることもできる。
実際のコンパイラでの使用
コンパイラが実行される際、パスの最初の要素を見つける方法を指定するだけでなく、パスのprefixのリマッピングが可能で、例えばgithub.com/ethereum/dapp-bin/library
を/usr/local/dapp-bin/library
にリマップすることができ、コンパイラはそこからファイルを読み込む。さらにこのリマッピングは、コンテキストに依存することができるため、名前は同じライブラリでバージョンが異なるような場合などにおいて、インポートするパッケージを設定できるようになる。
solcの場合
solcでは、このリマッピングはcontext:prefix=target
という形で引数で渡される。context:
と=target
の2つはオプション。全てのリマッピングする値は通常のファイルを指し、その依存関係も含めてコンパイルされる。この仕組みは完全な下位互換を持つ。全てのインポートしたファイルは、引数に記述したcontext以下の
prefixで始まるファイルを
targetで指定した
prefix`に置換しリダイレクトする。
まぁ実際の例で見たほうが分かりやすい。例えばgithub.com/ethereum/dapp-bin/ locallyをローカルの/usr/local/dapp-binにクローンしている場合、ソースファイル内に以下のように書いておく。
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
そしてコンパイル実行時に
$ solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol
と実行する。
もっと複雑な例であれば、いくつかのモジュールが dapp-binのとても古いバージョンに依存していたとする。dapp-binの古いバージョンは/usr/local/dapp-bin_oldにチェックアウトされていて、それを参照するには以下のように指定する。
$ solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \ module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \ source.sol
こうすることで古いバージョンは全てmodule2にインポートされ、module1には新しいバージョンをインポートされる。
solcは特定のディレクトリからのみファイルをインクルードできることに注意すること。明示的に指定したソースファイルのディレクトリ及びそのサブディレクトリか、リマッピング対象のディレクトリ及びそのサブディレクトリ。直接絶対パスで指定したい場合は=/
のりマッピングを追加する。
browser-solidityの場合
ブラウザベースのコンパイラbrowser-solidityでは、githubのりマッピングは自動的に行われ、ファイルも自動的にネットワーク経由で取得してくれる。以下のようにiterable mappingをインポートできる。
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
将来的にgithub以外からもインポートできるようになると思われる。
コメント
単一行のコメントは//
で、複数行は/*...*/
が使える。
// This is a single-line comment. /* This is a multi-line comment. */
まだnatspecコメントと呼ばれるコメントが書ける。これはトリプルスラッシュ///
かダブルアスタリスクブロック/** ... */
で記述するコメントで、関数宣言やステートメントの上に記述する。このコメント内ではドキュメントを生成する際に使用するDoxygenスタイルのタグが使え、フォーマット検証条件の注釈などが記述できる。
以下の例では、コントラクトのタイトルと2つの入力パラメータ、2つの戻り値についてドキュメント化している。
pragma solidity ^0.4.0; /** @title Shape calculator.*/ contract shapeCalculator{ /**@dev Calculates a rectangle's surface and perimeter. * @param w Width of the rectangle. * @param h Height of the rectangle. * @return s The calculated surface. * @return p The calculated perimeter. */ function rectangle(uint w, uint h) returns (uint s, uint p) { s = w * h; p = 2 * (w + h); } }