ソースコードのレビュー
■この章で解説するモジュール
O
B
B::Deparse
B::Lint
■この章でできること
・Perlコンパイラバックエンドを使う
・Perlソースコードを再生成する
・Perlソースコードをチェックする
・Perlソースコードのレビュー

 ここでは、Perl付属のコンパイラを使って、Perlソースコードのレビューをする方法を解説します。まず、PerlコンパイラはBというモジュールにまとめられています。Bモジュールのサブクラスとして、各種のコンパイラ関連のバックエンド機能が含まれています。

 Bモジュールを使うためのユーザフロントエンドとして、Oモジュールがあります。これを使うと、コマンドラインから簡単にBモジュールの機能を使うことができます。Oモジュールを使った書式は次のようになります。

O
Perlコンパイラバックエンド用の汎用インターフェース
書式
perl -MO=backend[,options] perlscript
引数
backend:
Bモジュール名
options:
モジュールに渡すオプション
perlscript:
Perlソーススクリプト

 B::Deparseモジュールは、Perlソースコードをコンパイルし、その内部情報を元にソースコードを再生成します。生成させるソースは、元のソースと全く同じにはなりません。これを使うと、自分で書いたソースと、B::Deparseモジュールが生成したコードを比較して、別の表記方法や効率的な表記方法など、いろいろな発見をすることができます。

 B::Deparseモジュールのオプションには次のものがあります。

オプション
説明
-l
元のコードの行番号とファイル名に基づいて、出力に#line宣言を追加する。
-p
必要な場所だけでなく、文法的に許される場所にはすべて括弧を出力する。
-sletters
出力のスタイル。唯一のオプションとしてcが指定できる。これを指定すると、elseなどの前に改行をしないスタイルになる。
-uPackage
Packageで指定されたパッケージも処理の対象とする。複数の-uオプションをカンマで区切って指定できる。

 では、実際に使ってみます。低水準サーバソケットで作成したPerlのソースファイルsocket_s.plを再生成してみます。コマンドラインから次のように入力します。

perl -MO=Deparse socket_s.pl > socket_s_dp.pl

 元のソースは次のようになっています。

#!/usr/bin/perl

#############################################
# 低水準サーバソケットのサンプル
# Author: "Perl Programming Tips"
#############################################

use Socket;

# TCPのプロトコル番号を取得
$tcpProtoNo = getprotobyname('tcp');

# SOCKET作成
socket(HSOCK, PF_INET, SOCK_STREAM,
    $tcpProtoNo) || die("socket() fail:$!\n");

# パックされたネットワークアドレスを作成
$address = sockaddr_in(3000, INADDR_ANY);

# ローカルマシン上のポートにバインド
bind(HSOCK, $address) || die("bind() fail:$!\n");

# OSへの通知と待ち行列の最大数設定
listen(HSOCK, 1);

# クライアントの接続待ち
accept(HCNCT, HSOCK) || die("accept() fail:$!\n");

# メッセージ受信
if (recv(HCNCT, $message, 30, MSG_WAITALL) == undef)
{
    die("recv() fail\n");
}
print "$message\n";

# メッセージ送信
if (send(HCNCT, 'Message from server to client.', 0) == undef)
{
    die("send() fail:$!\n");
}

# SOCKETクローズ
close(HSOCK);

 これに対して、Deparseが生成したソースは次のようになりました。

use Socket;
$tcpProtoNo = getprotobyname 'tcp';
die "socket() fail:$!\n" unless socket HSOCK, 2, 1, $tcpProtoNo;
$address = sockaddr_in(3000, "\000\000\000\000");
die "bind() fail:$!\n" unless bind HSOCK, $address;
listen HSOCK, 1;
die "accept() fail:$!\n" unless accept HCNCT, HSOCK;
if (recv(HCNCT, $message, 30, 8) == undef) {
    die "recv() fail\n";
}
print "$message\n";
if (send(HCNCT, 'Message from server to client.', 0) == undef) {
    die "send() fail:$!\n";
}
close HSOCK;

 だいぶ元のソースとは変わっています。もちろんDeparseが生成したソースが最適であるとは限りませんが、我流ではわからないような自分の癖などがわかってくるので参考になると思います。

 次は、ソースコードをチェックする方法について解説します。まず、通常のチェックには、perlコマンドに-wオプションをつけて実行することで、warningを出力することができます。このチェック方法では、未使用の変数などがあった場合に警告が出力されます。

 -wオプションではチェックされない、さらに高度なチェックをする場合は、B::Lintモジュールを使います。B::Lintモジュールのオプションには次のものがあります。

オプション
説明
all
すべての警告をオンにする。
context
暗黙のうちにスカラーコンテキストで配列が使用されると、警告を出力する。
dollar-underscore
$_が明示的に使用されるか、print文の暗黙の引数として使用されると警告を出力する。
implicit-read
何らかの操作によって、暗黙のうちにPerlの特殊変数からの読み出しが行われると警告を出力する。
implicit-write
何らかの操作によって、暗黙のうちにPerlの特殊変数への書き込みが行われると警告を出力する。
none
すべての警告をオフにする。
private-names
下線(_)で始まる名前を持ち、現在のパッケージ以外に所属するような変数、サブルーチン、メソッドを使用すると警告を出力する。
regexp-variables
正規表現に関連する特殊変数$'、$&、$`のいずれかが使用されると警告を出力する。
-uPackage
Packageで指定した他のパッケージをチェックする。
undefined-subs
未定義のサブルーチンが呼ばれると警告を出力する。

 では、実際にB:Lintを使って警告を出力してみます。サンプルで作成した次のようなソース(compiler01_01.pl)をチェックしてみます。

#!/usr/bin/perl

#############################################
# ソースコードチェックのサンプル
# Author: "Perl Programming Tips"
#############################################

@array = ("aaa", "bbb", "ccc");
$string = "abcdefghijklmn";

# スカラーコンテンキストに配列を使用
print ref(@array) . "\n";

# $_の明示的な使用
print "$_\n";

# 正規表現の後方参照用の特殊変数を使用
$string =~ s/(def)/$`/g;
print "$string\n";

# 未定義のサブルーチンを使用
&sub_routine;

 コマンドラインから次のように入力します。

perl -MO=Lint,all compiler01_01.pl

 出力結果は次のようになります。

$perl -MO=Lint,all compiler01_ 01.pl

Implicit scalar context for array in reference-type operator at compiler01_01.pl line 12
Use of $_ at compiler01_01.pl line 15
Use of regexp variable $` at compiler01_01.pl line 18
Nonexistant subroutine 'sub_routine' called at compiler01_01.pl line 21
compiler01_01.pl syntax OK
$

 -wオプションではチェックできない警告が出力されているのがわかります。