今日の技術
CRIScriptの更新を地味に続け中。下記は若干補足情報。
●最近の更新
トピックとしては、
・ECMA4仕様のRestArgsに対応
function func( a, ...arg )
・Objectプロパティをいくつか実装
・バグフィクス
等だ。
当面のゴールはECMA3の互換性テストを通すことだ。
今のところテストベッドとして1700個余りのECMA3互換性プログラムを使っている。(今後Tamarin、Mozilla等の互換性テストも加えると更に増加傾向)
そのうち約850テストをパス済みで、Nightlyビルドの一環としてテストを通している。
通っているのは
・基本オペテータ
・例外処理
・コントロールフロー
・String
等で、主な残りは
・Array
・Math
・Regex
等の組み込みコンポーネントの互換性テストだ。言語部分はそれなりに動いているので、組み込みメソッドの実装と、演算誤差や独自仕様などについてテストプログラム側を修正するというよくわからない状態。
これらが全て通ればECMA3互換と言っても罰当たりではないかと思われる。
ECMA4に関しては良いテストベッドが今のところ見つかっていない。Tamarinのテストコードが最右翼だが、ECMA4の仕様自体まだDraftであることもあって、後回しに。
●リリースプロセス
CRIScriptのリリースプロセスについてはSVNにチェックイン後下記の経路を辿ってサーバー上に反映される
・ビルドサーバー内で一日一回真夜中に更新をチェックして、更新があればビルドプロセス開始
・テストパス起動
・テストパスに全て合格したら、ビルド結果を、バイナリアーカイブ、ソースアーカイブにまとめてサーバ上にコピーして一件落着。ついでにヘッダ上のビルドナンバを更新(次回ビルド用)する。
こうした手順を踏むため、ソースチェックインからバイナリ更新までは最大一日程度の時間差が発生している。
あまり居ないと思うけれど最新ソースをいち早く取得したい場合には、SVN://CRISCRIPT.COMにSVNクライアントから接続するのが良いだろう。
また、メーリングリストに入っておくと更新情報が(手動で)流れてくるので良いかと思う。
●最近の更新
トピックとしては、
・ECMA4仕様のRestArgsに対応
function func( a, ...arg )
・Objectプロパティをいくつか実装
・バグフィクス
等だ。
当面のゴールはECMA3の互換性テストを通すことだ。
今のところテストベッドとして1700個余りのECMA3互換性プログラムを使っている。(今後Tamarin、Mozilla等の互換性テストも加えると更に増加傾向)
そのうち約850テストをパス済みで、Nightlyビルドの一環としてテストを通している。
通っているのは
・基本オペテータ
・例外処理
・コントロールフロー
・String
等で、主な残りは
・Array
・Math
・Regex
等の組み込みコンポーネントの互換性テストだ。言語部分はそれなりに動いているので、組み込みメソッドの実装と、演算誤差や独自仕様などについてテストプログラム側を修正するというよくわからない状態。
これらが全て通ればECMA3互換と言っても罰当たりではないかと思われる。
ECMA4に関しては良いテストベッドが今のところ見つかっていない。Tamarinのテストコードが最右翼だが、ECMA4の仕様自体まだDraftであることもあって、後回しに。
●リリースプロセス
CRIScriptのリリースプロセスについてはSVNにチェックイン後下記の経路を辿ってサーバー上に反映される
・ビルドサーバー内で一日一回真夜中に更新をチェックして、更新があればビルドプロセス開始
・テストパス起動
・テストパスに全て合格したら、ビルド結果を、バイナリアーカイブ、ソースアーカイブにまとめてサーバ上にコピーして一件落着。ついでにヘッダ上のビルドナンバを更新(次回ビルド用)する。
こうした手順を踏むため、ソースチェックインからバイナリ更新までは最大一日程度の時間差が発生している。
あまり居ないと思うけれど最新ソースをいち早く取得したい場合には、SVN://CRISCRIPT.COMにSVNクライアントから接続するのが良いだろう。
また、メーリングリストに入っておくと更新情報が(手動で)流れてくるので良いかと思う。
前回のサンプルの描画サイズを変更して16x9(640x360ピクセル)にしてみた。
描画面積が減ってその分パフォーマンスが稼げると同時に、次世代機っぽくなって良い。AS3のパフォーマンスが予想よりやや厳しいため描画サイズを減らしたが、もしかすると320x180ピクセルくらいでも適当かもしれない。往年のメガデモ?
さて、AS3環境でのパフォーマンスについて若干調べてみた。
●プロファイリング
むかーしのCEDECでも講演したことがあったが、パフォーマンス最適化の基本はプロファイリング。よいプロファイラ環境があればチューニングはほぼ終わったといっても過言ではない。過言か。
ActionScript環境ではFLEX用のプロファイラが提供されている。FLEX環境が必要とのことで、来るFLEX移行時に試すことにする。
http://www.adobe.com/devnet/flex/articles/profiler.html
また↓はprototypeにプロファイリング用の仕掛けを入れるタイプのプロファイラらしい。
http://www.nochump.com/asprof/
今回の計測に当たっては
flash.utils.GetTimer();
を使った。
コード規模が少なくて助かったが、上記プロファイラの存在を知っていればもっと楽だったかもしれない。
GetTimer()は解像度が1msecと荒いため詳細のプロファイリングには向いてないが残念ながらAS3環境ではこれ以上の解像度のタイマがなさそうだ。
●最初の計測結果
1フレームあたりの総処理時間と更新/描画処理の処理時間を計測したものが下のグラフ(ここでGoogleChartAPIを小一時間ほど触ってみて挫折。値を正規化しないといけないのがめんどい。サービスを作れという思し召しだろうか)
グラフ上で、青線が総実行時間、赤線が更新/描画処理の負荷(単位msec)。
殆どの実行時間が更新、描画処理に喰われているのが分かる。
また、1フレームあたり60msec~120msecと処理時間は安定せず。且つ邪悪なスパイク(処理負荷の増大)のようなものが見て取れる。
現状で秒間10~12フレームというところだろう。
●更新処理、描画処理の内訳
次に、更新/描画処理内での負荷傾向を調べるために、更新処理だけを計測したもの(単位msec)。
演算を全て倍精度の'Number'で行なっており(ECMAScript仕様では80bit精度、ActionScriptでは64bit精度とされている)結構処理を喰っているのでは、と想定したが以外と低負荷で処理されている。
一見して分かるとおり、定期的に12msec前後のスパイクが見られる。
●スパイクは許されざるものbyクリント・イーストウッド
解説:
ゲームプログラムではこうした一時的に処理負荷が高まる現象は風水的に良くないとされて忌み嫌われる。
ゲーム画面はテレビモニタのリフレッシュレートに併せて毎16.6msec(または33msec等16.6mecの整数倍)毎の定期的な感覚で画面更新を行なっている。その際に1フレームでも画面更新が遅れるとユーザにも分かる画面の引きつりとなってゲームへの没入が阻害されてしまうのだ。このため処理負荷が一時的に増大するスパイクはドライバやアプリケーションの実装では徹底的に避けるのだ。
●ガベージコレクタ
マルチプロセス/プリエンプティブなWindows環境ではスパイクを含めて何が起きても不思議ではないのだが、とりあえずその原因を追ってみることにする。
VM環境での実行時間のスパイクについてまず疑わしいのはAVM2が行なっているガベージコレクション処理だ。
これを確認するため、確保済みメモリサイズと先ほどの更新処理負荷を併せて計測してみる。
#なお、その他にはスレッドのタイムスライスによるタイムアウト、ドライバのへぼい処理、変な同期処理、IO待ち、などが考えられる
青線が使用メモリサイズ(左側単位byte)、赤線が更新処理負荷(単位msec)。
スパイクとメモリサイズの変動が一致しているため、ガベージコレクションによる負荷がスパイクを招いていると言ってよいだろう。
一般的にガベージコレクション中は全ての処理を停止させるため、納得のいく現象だ。
図よりAVM2のガベージコレクタKickOffThresholdが1MB前後なのも見て取れる。
AVM2のガベージコレクタのアルゴリズムは↓の資料が参考になる
http://www.onflex.org/ACDS/AS3TuningInsideAVM2JIT.pdf
資料によるとAVM2では
・Deferred Reference Counting
・incremental conservative mark/sweep
の2手法を使っているようだ。
リファレンス・カウンタは効率的だが、オブジェクトの循環参照が発生したときに永久に開放されないゾンビオブジェクトが発生してしまう。
この弱点を補うために、全ノードの参照カウンタをトップノードから洗いなおすMark&Sweep方式を定期的に発動させていると思われる。
またスタック上のオブジェクトに対するリファレンスカウンタ操作を行なわない点をDefferredと呼んでいるようで、これにより20%の性能向上が観られるとは興味深い結果だ。
#なお.Netでは上記に加えて生き残ったオブジェクトを世代別に管理する世代別GCを採用して更に実行効率を高めている。
Mark&Sweepの欠点はGC発動中にリファレンスカウンタが飛んでしまうため他の処理の動作をとめる必要がある点だ。この点はリファレンスカウンタを別領域にDUPしてその時点で参照されていないオブジェクトだけを抽出する方式(且つオブジェクトの動的リロケーションを伴わない条件で)回避できるような気もする。
また、実際オブジェクトの循環参照がどれくらい発生するかは個人的には不思議な点。リファレンスカウンタを基本にリクエストに応じた場合だけMark&Sweepという実装ではどうだろうか、と考えてみる今日この頃だ。
●ガベージコレクタ攻略戦
状態更新処理中にガベージコレクタによるスパイクが発生しているため、これを回避することにする。
ガベージコレクタの発動を抑えるためには、基本的には下記の手法が有効だ。XNA(.Net)やJAVAのゲームプログラミングでもガベージコレクションを抑制として同様の手法が有効だ。
・ダイナミックなアロケーションを避ける
-関数内で一時オブジェクトをnewしている場合は、メンバ関数としてオブジェクトをstaticに確保。メモリ喰ってもガベージコレクタよりはマシだ。
-戻り値を確保している場合は関数シグネチャを変更して引数に戻り値の参照を持たせる
-万難排してnewを避ける
・BOXINGにも気をつける
-Boxingによる暗黙の型変換用オブジェクトの生成も排除する。Boxing用の静的オブジェクトを確保しておいてそれを使用すると良い
・どうしてもアロケーションされてしまうときは、気休めにdispose()/destroy()などしてみる
API等で確保されてしまうオブジェクトについては止むを得ず、神に祈る。disposeしたとしても廃棄リストへ追加されるだけでガベージコレクタ発動抑制の役には立たないことが殆どだろう。
●ガベージコレクタ・もう一点の謎
大抵上記のような手順でガベージコレクタの挙動は大分制御できたが、AVM2の場合もう一点不可解な挙動が見られた。
どうも特定の演算で一時オブジェクトが生成されているような気配がある。かなり基本的な演算であることと、ABC(アクションスクリプト・バイト・コード)ダンプを確認してもそれらしいコード生成は行なわれていない点もあり、トンデモ学説かもしれない。この点についてはも少し検証したい。
上記のような改修の結果
状態更新において不機嫌なスパイクは解消した。ところにより処理時間が2msecとスパイクのように見えるが、タイマ解像度が1msecしかないための誤差範囲と考えてよいだろう。また、メモリ使用量がなだらかに上昇しているためもう少しガベージコレクタ対応の余地があるが、とりあえずは許容範囲とする(してください)。
●最適化途中結果
状態更新ループ中でのガベージコレクション発動を抑えた結果
まだまだフレームレート安定には遠い道のりだが、変動は若干抑制された、ような気がする。
つづいて(続けば)描画周りなどを検証してみる予定だ。
描画面積が減ってその分パフォーマンスが稼げると同時に、次世代機っぽくなって良い。AS3のパフォーマンスが予想よりやや厳しいため描画サイズを減らしたが、もしかすると320x180ピクセルくらいでも適当かもしれない。往年のメガデモ?
さて、AS3環境でのパフォーマンスについて若干調べてみた。
●プロファイリング
むかーしのCEDECでも講演したことがあったが、パフォーマンス最適化の基本はプロファイリング。よいプロファイラ環境があればチューニングはほぼ終わったといっても過言ではない。過言か。
ActionScript環境ではFLEX用のプロファイラが提供されている。FLEX環境が必要とのことで、来るFLEX移行時に試すことにする。
http://www.adobe.com/devnet/flex/articles/profiler.html
また↓はprototypeにプロファイリング用の仕掛けを入れるタイプのプロファイラらしい。
http://www.nochump.com/asprof/
今回の計測に当たっては
flash.utils.GetTimer();
を使った。
コード規模が少なくて助かったが、上記プロファイラの存在を知っていればもっと楽だったかもしれない。
GetTimer()は解像度が1msecと荒いため詳細のプロファイリングには向いてないが残念ながらAS3環境ではこれ以上の解像度のタイマがなさそうだ。
●最初の計測結果
1フレームあたりの総処理時間と更新/描画処理の処理時間を計測したものが下のグラフ(ここでGoogleChartAPIを小一時間ほど触ってみて挫折。値を正規化しないといけないのがめんどい。サービスを作れという思し召しだろうか)
グラフ上で、青線が総実行時間、赤線が更新/描画処理の負荷(単位msec)。
殆どの実行時間が更新、描画処理に喰われているのが分かる。
また、1フレームあたり60msec~120msecと処理時間は安定せず。且つ邪悪なスパイク(処理負荷の増大)のようなものが見て取れる。
現状で秒間10~12フレームというところだろう。
●更新処理、描画処理の内訳
次に、更新/描画処理内での負荷傾向を調べるために、更新処理だけを計測したもの(単位msec)。
演算を全て倍精度の'Number'で行なっており(ECMAScript仕様では80bit精度、ActionScriptでは64bit精度とされている)結構処理を喰っているのでは、と想定したが以外と低負荷で処理されている。
一見して分かるとおり、定期的に12msec前後のスパイクが見られる。
●スパイクは許されざるものbyクリント・イーストウッド
解説:
ゲームプログラムではこうした一時的に処理負荷が高まる現象は風水的に良くないとされて忌み嫌われる。
ゲーム画面はテレビモニタのリフレッシュレートに併せて毎16.6msec(または33msec等16.6mecの整数倍)毎の定期的な感覚で画面更新を行なっている。その際に1フレームでも画面更新が遅れるとユーザにも分かる画面の引きつりとなってゲームへの没入が阻害されてしまうのだ。このため処理負荷が一時的に増大するスパイクはドライバやアプリケーションの実装では徹底的に避けるのだ。
●ガベージコレクタ
マルチプロセス/プリエンプティブなWindows環境ではスパイクを含めて何が起きても不思議ではないのだが、とりあえずその原因を追ってみることにする。
VM環境での実行時間のスパイクについてまず疑わしいのはAVM2が行なっているガベージコレクション処理だ。
これを確認するため、確保済みメモリサイズと先ほどの更新処理負荷を併せて計測してみる。
#なお、その他にはスレッドのタイムスライスによるタイムアウト、ドライバのへぼい処理、変な同期処理、IO待ち、などが考えられる
青線が使用メモリサイズ(左側単位byte)、赤線が更新処理負荷(単位msec)。
スパイクとメモリサイズの変動が一致しているため、ガベージコレクションによる負荷がスパイクを招いていると言ってよいだろう。
一般的にガベージコレクション中は全ての処理を停止させるため、納得のいく現象だ。
図よりAVM2のガベージコレクタKickOffThresholdが1MB前後なのも見て取れる。
AVM2のガベージコレクタのアルゴリズムは↓の資料が参考になる
http://www.onflex.org/ACDS/AS3TuningInsideAVM2JIT.pdf
資料によるとAVM2では
・Deferred Reference Counting
・incremental conservative mark/sweep
の2手法を使っているようだ。
リファレンス・カウンタは効率的だが、オブジェクトの循環参照が発生したときに永久に開放されないゾンビオブジェクトが発生してしまう。
この弱点を補うために、全ノードの参照カウンタをトップノードから洗いなおすMark&Sweep方式を定期的に発動させていると思われる。
またスタック上のオブジェクトに対するリファレンスカウンタ操作を行なわない点をDefferredと呼んでいるようで、これにより20%の性能向上が観られるとは興味深い結果だ。
#なお.Netでは上記に加えて生き残ったオブジェクトを世代別に管理する世代別GCを採用して更に実行効率を高めている。
Mark&Sweepの欠点はGC発動中にリファレンスカウンタが飛んでしまうため他の処理の動作をとめる必要がある点だ。この点はリファレンスカウンタを別領域にDUPしてその時点で参照されていないオブジェクトだけを抽出する方式(且つオブジェクトの動的リロケーションを伴わない条件で)回避できるような気もする。
また、実際オブジェクトの循環参照がどれくらい発生するかは個人的には不思議な点。リファレンスカウンタを基本にリクエストに応じた場合だけMark&Sweepという実装ではどうだろうか、と考えてみる今日この頃だ。
●ガベージコレクタ攻略戦
状態更新処理中にガベージコレクタによるスパイクが発生しているため、これを回避することにする。
ガベージコレクタの発動を抑えるためには、基本的には下記の手法が有効だ。XNA(.Net)やJAVAのゲームプログラミングでもガベージコレクションを抑制として同様の手法が有効だ。
・ダイナミックなアロケーションを避ける
-関数内で一時オブジェクトをnewしている場合は、メンバ関数としてオブジェクトをstaticに確保。メモリ喰ってもガベージコレクタよりはマシだ。
-戻り値を確保している場合は関数シグネチャを変更して引数に戻り値の参照を持たせる
-万難排してnewを避ける
・BOXINGにも気をつける
-Boxingによる暗黙の型変換用オブジェクトの生成も排除する。Boxing用の静的オブジェクトを確保しておいてそれを使用すると良い
・どうしてもアロケーションされてしまうときは、気休めにdispose()/destroy()などしてみる
API等で確保されてしまうオブジェクトについては止むを得ず、神に祈る。disposeしたとしても廃棄リストへ追加されるだけでガベージコレクタ発動抑制の役には立たないことが殆どだろう。
●ガベージコレクタ・もう一点の謎
大抵上記のような手順でガベージコレクタの挙動は大分制御できたが、AVM2の場合もう一点不可解な挙動が見られた。
どうも特定の演算で一時オブジェクトが生成されているような気配がある。かなり基本的な演算であることと、ABC(アクションスクリプト・バイト・コード)ダンプを確認してもそれらしいコード生成は行なわれていない点もあり、トンデモ学説かもしれない。この点についてはも少し検証したい。
上記のような改修の結果
状態更新において不機嫌なスパイクは解消した。ところにより処理時間が2msecとスパイクのように見えるが、タイマ解像度が1msecしかないための誤差範囲と考えてよいだろう。また、メモリ使用量がなだらかに上昇しているためもう少しガベージコレクタ対応の余地があるが、とりあえずは許容範囲とする(してください)。
●最適化途中結果
状態更新ループ中でのガベージコレクション発動を抑えた結果
まだまだフレームレート安定には遠い道のりだが、変動は若干抑制された、ような気がする。
つづいて(続けば)描画周りなどを検証してみる予定だ。
新年明けましておめでとう御座います、と言いそびれて早一月、節分の候となりましたがいかがお過ごしでしょうか。本年もよろしくお願いします。
最近はTwitterで近況を更新していますので日常が気になる方はそちらも併せてご覧ください:)
ちょっとした機会があって10数年振りにFlashを触ってみた。
10数年間というと当時はMacromediaがAdobeに買収される前で、製品名はMarcromediaDirector。スクリプトもLingoScriptというものだった。時代もインターネットの夜明け前、Directorはマルチメディア(死語)向けオーサリングツールという位置づけだった。当時Directorを使ってCD-ROMコンテンツを量産していたのは青春の一ページだが、それはまた別の話だ。
で時は流れてFlashCs3を触ってみる。ツールとしてはあまり進化していない様子だ。なんというか使いにくい。自分の使い方が悪い可能性も高いが、コーディング・デバッグ環境としては実用的ではなさそうだ。FLEX環境に気を見て乗り換えるのがよいだろう。
また、ActionScript3ではJIT化されてパフォーマンス向上、とのことでパフォーマンステスト用に適当な3Dエンジンを組んでみたのが↓
ちょっと重いな~、という感想。
ビルボード1000枚のジオメトリ変換+表示で大分限界。オフスクリーンサーフェスへの描画が予想以上に重い印象だ。
このうえでソフトウェアでラスタライズまですると、感覚的にはSEGASaturnくらかな~ と思われる。次世代Flash10ではGPUレンダリングがサポートされるとのことで、大いに期待。
追記:画面比16x9にして次世代機対応:)
最近はTwitterで近況を更新していますので日常が気になる方はそちらも併せてご覧ください:)
ちょっとした機会があって10数年振りにFlashを触ってみた。
10数年間というと当時はMacromediaがAdobeに買収される前で、製品名はMarcromediaDirector。スクリプトもLingoScriptというものだった。時代もインターネットの夜明け前、Directorはマルチメディア(死語)向けオーサリングツールという位置づけだった。当時Directorを使ってCD-ROMコンテンツを量産していたのは青春の一ページだが、それはまた別の話だ。
で時は流れてFlashCs3を触ってみる。ツールとしてはあまり進化していない様子だ。なんというか使いにくい。自分の使い方が悪い可能性も高いが、コーディング・デバッグ環境としては実用的ではなさそうだ。FLEX環境に気を見て乗り換えるのがよいだろう。
また、ActionScript3ではJIT化されてパフォーマンス向上、とのことでパフォーマンステスト用に適当な3Dエンジンを組んでみたのが↓
ちょっと重いな~、という感想。
ビルボード1000枚のジオメトリ変換+表示で大分限界。オフスクリーンサーフェスへの描画が予想以上に重い印象だ。
このうえでソフトウェアでラスタライズまですると、感覚的にはSEGASaturnくらかな~ と思われる。次世代Flash10ではGPUレンダリングがサポートされるとのことで、大いに期待。
追記:画面比16x9にして次世代機対応:)
ECMAScriptの奇妙な仕様のひとつに’Auto Semicolon Insertion’がある。要するに行末の';’が有っても良い、無くても良い、という地球に優しく、パーサにめんどくさい仕様だ。
例)
foo = bar + 1; //あっても良い
foo = bar + 1 //無くても良いよ
この機能が発動する条件は、EMCA262の7.9節でこう定義される
トークン(文字のかたまり)を左から順番に見てって、文法的な悪玉トークンがあらわれたとき、
1)悪玉トークンの前に改行がある
2)悪玉トークンが'}'
3)悪玉トークンでないけど、改行不許可の場所に悪玉改行がある
のいずれかのときに';'が挿入される
ただし、for文の';'の代わりとしては挿入されない
また3)の改行が許されない場所とは
・後置の++/--
・ラベル付きのcontinue
・ラベル付きのbreak
・パラメタ付きのreturn
・パラメタ付きのthrow
の5つ、改行すると別の構文として解釈できてしまう場合だ。
例えば
{
foo
++
bar
}
この場合
{
foo++;
bar;
}
となる。
なんというかC++に似た構文に自由度を一滴垂らしたら色々収まりが付かない仕様になってしまっている。
●ラベル+式+{}の怪しい関係
上記の条件によると
{
foo:
bar = 0
}
なんかは、普通のラベル+式ステートメントではなくて、
プロパティ初期化つきのオブジェクト・リテラルとして還元される。
(ただしオブジェクト生成後の代入なし)
なぜなら、最後に出てくる'}'は(オブジェクトリテラルの構文として)悪玉トークンではなく、';'は挿入されないからだ。
#オブジェクトリテラルの構文は
#{ プロパティ名:値の式 [,プロパティ名:値の式] }
この場合でも、プロパティの初期化子の式は普通に実行されるので動作上の影響は無い。また(大抵は)インタプリタ/コンパイラが代入の無いオブジェクト生成は省略してくれる。
#ただし、ラベルと思って定義したfoo:がラベルとしては定義されないため、頑張ればラベル付きbreak等の文で奇妙なエラーを起こすこともできそうだ(が、そもそもラベルを使ったECMAScriptはみたことない)。
#なにより、変な風にコードが解釈されてるのは不気味
ためしにJScript.Net8.0でJscriptをコンパイルし、CIL(バイトコード)に変換してみる。JScript.netの環境では、Jscriptのバイトコード変換後の結果が調べられるので、何かと役に立つ。
{
foo: bar = 3
}
IL_0003: ldc.i4.3
IL_0004: conv.r8
IL_0005: stloc.0
ためしに
{
foo: bar = 3,
foo2: bar2 = 3
} //プロパティふたつのオブジェクトリテラル
だとコンパイルエラー。
Jscript.Netではこうした場合、普通にラベル+式として解釈されているようだ。なんだか仕様と違う気がしないでもないが、見なかったことにする。
今日も平和だ。
つづく
例)
foo = bar + 1; //あっても良い
foo = bar + 1 //無くても良いよ
この機能が発動する条件は、EMCA262の7.9節でこう定義される
トークン(文字のかたまり)を左から順番に見てって、文法的な悪玉トークンがあらわれたとき、
1)悪玉トークンの前に改行がある
2)悪玉トークンが'}'
3)悪玉トークンでないけど、改行不許可の場所に悪玉改行がある
のいずれかのときに';'が挿入される
ただし、for文の';'の代わりとしては挿入されない
また3)の改行が許されない場所とは
・後置の++/--
・ラベル付きのcontinue
・ラベル付きのbreak
・パラメタ付きのreturn
・パラメタ付きのthrow
の5つ、改行すると別の構文として解釈できてしまう場合だ。
例えば
{
foo
++
bar
}
この場合
{
foo++;
bar;
}
となる。
なんというかC++に似た構文に自由度を一滴垂らしたら色々収まりが付かない仕様になってしまっている。
●ラベル+式+{}の怪しい関係
上記の条件によると
{
foo:
bar = 0
}
なんかは、普通のラベル+式ステートメントではなくて、
プロパティ初期化つきのオブジェクト・リテラルとして還元される。
(ただしオブジェクト生成後の代入なし)
なぜなら、最後に出てくる'}'は(オブジェクトリテラルの構文として)悪玉トークンではなく、';'は挿入されないからだ。
#オブジェクトリテラルの構文は
#{ プロパティ名:値の式 [,プロパティ名:値の式] }
この場合でも、プロパティの初期化子の式は普通に実行されるので動作上の影響は無い。また(大抵は)インタプリタ/コンパイラが代入の無いオブジェクト生成は省略してくれる。
#ただし、ラベルと思って定義したfoo:がラベルとしては定義されないため、頑張ればラベル付きbreak等の文で奇妙なエラーを起こすこともできそうだ(が、そもそもラベルを使ったECMAScriptはみたことない)。
#なにより、変な風にコードが解釈されてるのは不気味
ためしにJScript.Net8.0でJscriptをコンパイルし、CIL(バイトコード)に変換してみる。JScript.netの環境では、Jscriptのバイトコード変換後の結果が調べられるので、何かと役に立つ。
{
foo: bar = 3
}
IL_0003: ldc.i4.3
IL_0004: conv.r8
IL_0005: stloc.0
ためしに
{
foo: bar = 3,
foo2: bar2 = 3
} //プロパティふたつのオブジェクトリテラル
だとコンパイルエラー。
Jscript.Netではこうした場合、普通にラベル+式として解釈されているようだ。なんだか仕様と違う気がしないでもないが、見なかったことにする。
今日も平和だ。
つづく
最近何かとECMAScript(エ熊スクリプト)の仕様を触る機会が多い。仕様書を吟味してみると、C++的パラダイムからやってきた異邦人には何かと驚きのことも(JScripterな方々には当たり前のことも多いだろうので、コメントがあれば教えてください)。
ECMAScriptの仕様は一連のECMA仕様書によって定義されている。
ECMA262 第3版ベース仕様書
http://www.ecma-international.org/publications/standards/Ecma-262.htm
ECMA290 コンポーネント仕様書 XMLのインタフェースなど定義
http://www.ecma-international.org/publications/standards/Ecma-290.htm
ECMA327 コンパクトプロファイル定義 組み込み向け軽量実装
http://www.ecma-international.org/publications/standards/Ecma-327.htm
ECMA357 XML拡張 って実装されているのだろうか
http://www.ecma-international.org/publications/standards/Ecma-357.htm
ECMAScript 第4版Wiki 正式仕様化希望
http://wiki.ecmascript.org/doku.php
これらの仕様書、とくにベース仕様書は、仕様書としては(控えめに言って)かなり読みにくい部類だ。例えば言語の挙動が自然言語(ここでは英語)で定義されているのだが、それが一層解りにくくしている。
また、Closureなどの重要な概念が明記されておらず、変数スコープの仕様をじっくり300回ほどは読み込んだうえで拡大解釈しないといけない。腐ってると言ってもよい、多分。
言語仕様そのものも(再び、ライトウェイト言語としては普通なのかもしれないが)なかなか驚かされることが多い。仕様の成り立ちからしてすでに実装済みのJScriptとJavaScriptの共通部分を定義という、実装ありきの涙ぐましい状態からスタートしていることもあるだろう。苦労している様が伺われる。ガンバレECMA!!
その1 switch文はif文の羅列と同じ
ECMAScriptのswitch文ではcaseブロックの条件に式が指定できる。定数ではなくて、式だ。そもそもECMAScript(3rd)では定数の概念が(組み込みオブジェクトのReadOnlyプロパティを除いて)存在しない。
式が記述できることで何ができるかというと、下記のようにswitch文を変幻自在に書くことができる。
/*見難いのでbreak;は省略*/
switch( foo ) {
case 0: //まぁ普通
case bar: //まだ普通
case bar+2: //まだまだ普通
case bar+foo: //あんまり見たこと無い
case "bar": //これは良いものだ
case func(): //何がしたいのかよくわからない
case { bar: func() }: //さらにわからないことに
case 0: //デジャビュ?
case 0: //マトリックスの中に居るのか?
default:
}
といった風に条件式を重複もなんも気にせず記述できる世界だ。
自由だ・・・
また仕様上、条件式は実行時に、律儀に記述の順番どおりに評価されなくてはならないため、上記の文はif文での下記の記述と等しくなる。
if( foo == 0) { }
else if ( foo == bar ) {}
else if ( foo == bar + 2 ) {}
else if ( foo == bar + foo ) {}
else if ( foo == "bar" ) {}
else if ( foo == func () ) {}
else if ( foo == { bar: func() } ) {}
else if ( foo == 0 ) {}
else if ( foo == 0 ) {}
else {}
//ギャー
caseブロックが50個くらい並んでいたりすると、後になればなるほど実行速度が遅くなり、途中で条件式が重複してたりするとよくわからないことになりそうだ。
また、式の評価をまじめにしないといけないので、テーブル参照生成等のコード生成最適化がしにくい。
#重複する式を削除、警告の出力等は可能だろう。
#追記:コンパイラ最適化としては、全てのノードがLiteralのときだけ
#hash参照にするという特殊ケースが考えられるが、
#どれくらい頻繁なケースかよく解らない。もしかするとこれが主流?
#また、第4版仕様のconstを使うとよいだろう
割と恐怖の仕様で、これは「switch」とは言わないのではないか~、とか、IEの適当な実装が既に出回っていて修正不可能だったのではないか、などなどの想いが胸をよぎる。
つづく
ECMAScriptの仕様は一連のECMA仕様書によって定義されている。
ECMA262 第3版ベース仕様書
http://www.ecma-international.org/publications/standards/Ecma-262.htm
ECMA290 コンポーネント仕様書 XMLのインタフェースなど定義
http://www.ecma-international.org/publications/standards/Ecma-290.htm
ECMA327 コンパクトプロファイル定義 組み込み向け軽量実装
http://www.ecma-international.org/publications/standards/Ecma-327.htm
ECMA357 XML拡張 って実装されているのだろうか
http://www.ecma-international.org/publications/standards/Ecma-357.htm
ECMAScript 第4版Wiki 正式仕様化希望
http://wiki.ecmascript.org/doku.php
これらの仕様書、とくにベース仕様書は、仕様書としては(控えめに言って)かなり読みにくい部類だ。例えば言語の挙動が自然言語(ここでは英語)で定義されているのだが、それが一層解りにくくしている。
また、Closureなどの重要な概念が明記されておらず、変数スコープの仕様をじっくり300回ほどは読み込んだうえで拡大解釈しないといけない。腐ってると言ってもよい、多分。
言語仕様そのものも(再び、ライトウェイト言語としては普通なのかもしれないが)なかなか驚かされることが多い。仕様の成り立ちからしてすでに実装済みのJScriptとJavaScriptの共通部分を定義という、実装ありきの涙ぐましい状態からスタートしていることもあるだろう。苦労している様が伺われる。ガンバレECMA!!
その1 switch文はif文の羅列と同じ
ECMAScriptのswitch文ではcaseブロックの条件に式が指定できる。定数ではなくて、式だ。そもそもECMAScript(3rd)では定数の概念が(組み込みオブジェクトのReadOnlyプロパティを除いて)存在しない。
式が記述できることで何ができるかというと、下記のようにswitch文を変幻自在に書くことができる。
/*見難いのでbreak;は省略*/
switch( foo ) {
case 0: //まぁ普通
case bar: //まだ普通
case bar+2: //まだまだ普通
case bar+foo: //あんまり見たこと無い
case "bar": //これは良いものだ
case func(): //何がしたいのかよくわからない
case { bar: func() }: //さらにわからないことに
case 0: //デジャビュ?
case 0: //マトリックスの中に居るのか?
default:
}
といった風に条件式を重複もなんも気にせず記述できる世界だ。
自由だ・・・
また仕様上、条件式は実行時に、律儀に記述の順番どおりに評価されなくてはならないため、上記の文はif文での下記の記述と等しくなる。
if( foo == 0) { }
else if ( foo == bar ) {}
else if ( foo == bar + 2 ) {}
else if ( foo == bar + foo ) {}
else if ( foo == "bar" ) {}
else if ( foo == func () ) {}
else if ( foo == { bar: func() } ) {}
else if ( foo == 0 ) {}
else if ( foo == 0 ) {}
else {}
//ギャー
caseブロックが50個くらい並んでいたりすると、後になればなるほど実行速度が遅くなり、途中で条件式が重複してたりするとよくわからないことになりそうだ。
また、式の評価をまじめにしないといけないので、テーブル参照生成等のコード生成最適化がしにくい。
#重複する式を削除、警告の出力等は可能だろう。
#追記:コンパイラ最適化としては、全てのノードがLiteralのときだけ
#hash参照にするという特殊ケースが考えられるが、
#どれくらい頻繁なケースかよく解らない。もしかするとこれが主流?
#また、第4版仕様のconstを使うとよいだろう
割と恐怖の仕様で、これは「switch」とは言わないのではないか~、とか、IEの適当な実装が既に出回っていて修正不可能だったのではないか、などなどの想いが胸をよぎる。
つづく
ゲーム業界誌"GameDeveloper"April 2007号にゲーム業界給与調査(2006年版)が掲載されている。この調査は2004年から毎年一回実施しているもので、ゲーム業界内での職種(プログラマ、アーティスト、ゲームデザイン、プロダクション、QA、オーディオ、ビジネスの7業種)、経験年数ごとに平均給与を調べている。
北米のプログラマの2006年平均年収はこんな感じとでた。
平均$80,886
| 経験3年以下 | 3-6年 | 6年以上 | ||||||
|---|---|---|---|---|---|---|---|---|
| Staff | Lead | Director | Staff | Lead | Director | Staff | Lead | Director |
| 57,913 | 73,311 | N/A | 74,707 | 80,132 | 91,944 | 88,841 | 98,152 | 119,141 |
傾向としては去年より微増。他業種のプログラマと比べても平均的な水準で、他職種と比較するとかなり良いほうだ。
なお、代表的な250職種の総合ランキングをつける"Jobs Rated Almanac"では、ソフトウェアエンジニアは総合4位につけている。
http://www.egguevara.com/shopping/articles/jobsrated.html
他リージョンとの比較としては、
アメリカ:$80,866
カナダ:$62,596
ヨーロッパ:$53,703
との数字。税制や物価の違いはあって一概には言えないが、アメリカのプログラマ給与が突出している。
日本は(チームにも依るが)感覚的にはヨーロッパに近いあたりの水準だろうか。
日本での問題点としては、プログラマの職種が他業種に比較して相対的にツライポジションにあるところだろう。
個人的に精神的な開発論云々よりも、こうした即物的な点を解消することが明るい未来に直結するんでないかと、そこはかとなく思われる。
こうした情報は、無意識か意図的か、なんとなく避けられているような雰囲気もあり、敢えて触れてみる。
写真は、いつもの駐車場の脇にさり気無く停泊中の軍艦。
東京の本屋で驚いたのが、平積みで並ぶGoogle本の盛り上がりようだ。日本市場でのGoogle脅威論については、やや違和感があるのでメモしておく。
米国内でのGoogleのポジションについては殆ど鉄板で異論ない。今のところGoogleを凌ぐWeb系企業は見当たらないし、検索エンジンとしてのシェアも圧倒的だ。同僚のできるエンジニアの面々はGoogleに移籍、仕事をエンジョイしていて満足そうだ。懸念点は、今後更に会社規模が拡大したときに開発スピードを維持できるかどうかがだが、現状の数人体制の小規模チーム体制を維持できる限りは問題ないだろう。
転じて日本国内でのGoogleのポジションだが、今後数年以内のYahooJapanとの検索エンジンシェアor広告売り上げ逆転はないのでは、という話だ。
・現状について
YahooJapanとGoogleJapanのトラフィックの比較は下記のとおり。
http://www.alexa.com/data/details/traffic_details?&range=3m&size=medium&compare_sites=google.co.jp&y=r&url=www.yahoo.co.jp#top
上記では大体倍程度の開きだが、体感的にはもっと差は大きい。
とある関連で零細WEBSHOPの広告出稿に携わっているが、自分の関わる分野ではYahooからのトラフィックはGoogleでの同一ランキングで10倍以上の開きがある。たとえばあるキーワードでYahoo、Google上で共に1ページ目に表示されている場合、一日あたりのトラフィック数が10:1以上、ということだ。
トラフィックに対するコンバージョンレートも、Yahoo経由のほうが良質のトラフィックが多い。WEB広告の費用対効果も同様である程度の広告費をかけるのであれば、迷わずOvertureに全額出稿すべきだと思う。
・今後のサービス展開について
コンシューマ向けの商品、サービスを日本国内で展開するには、あらゆる意味でのローカライズが重要な点かと思う。土着化と言い換えても良い。商材となるサービスそのものだけではなく、営業部隊、リテール、マーケティング、社風に至るまで、日本企業としての体裁を整えずして、日本国内で成功はおぼつかない。広い意味での参入障壁だ。
この点で、Googleは日本への土着化の道は歩めないだろうし、そもそもする気が無いのでは、と思われる。
・日本市場の現状で、YahooJapanにすでに大分水をあけられている
・今後、GoogleJapanがYahooJapanをキャッチアップするためのローカライズ戦略をとることはないだろう
この二点から、Googleは日本市場ではそれほど成功しないのでは、と予感する。と、いうのを5年後に見て外れていたらゴメンナサイだ。
米国内でのGoogleのポジションについては殆ど鉄板で異論ない。今のところGoogleを凌ぐWeb系企業は見当たらないし、検索エンジンとしてのシェアも圧倒的だ。同僚のできるエンジニアの面々はGoogleに移籍、仕事をエンジョイしていて満足そうだ。懸念点は、今後更に会社規模が拡大したときに開発スピードを維持できるかどうかがだが、現状の数人体制の小規模チーム体制を維持できる限りは問題ないだろう。
転じて日本国内でのGoogleのポジションだが、今後数年以内のYahooJapanとの検索エンジンシェアor広告売り上げ逆転はないのでは、という話だ。
・現状について
YahooJapanとGoogleJapanのトラフィックの比較は下記のとおり。
http://www.alexa.com/data/details/traffic_details?&range=3m&size=medium&compare_sites=google.co.jp&y=r&url=www.yahoo.co.jp#top
上記では大体倍程度の開きだが、体感的にはもっと差は大きい。
とある関連で零細WEBSHOPの広告出稿に携わっているが、自分の関わる分野ではYahooからのトラフィックはGoogleでの同一ランキングで10倍以上の開きがある。たとえばあるキーワードでYahoo、Google上で共に1ページ目に表示されている場合、一日あたりのトラフィック数が10:1以上、ということだ。
トラフィックに対するコンバージョンレートも、Yahoo経由のほうが良質のトラフィックが多い。WEB広告の費用対効果も同様である程度の広告費をかけるのであれば、迷わずOvertureに全額出稿すべきだと思う。
・今後のサービス展開について
コンシューマ向けの商品、サービスを日本国内で展開するには、あらゆる意味でのローカライズが重要な点かと思う。土着化と言い換えても良い。商材となるサービスそのものだけではなく、営業部隊、リテール、マーケティング、社風に至るまで、日本企業としての体裁を整えずして、日本国内で成功はおぼつかない。広い意味での参入障壁だ。
この点で、Googleは日本への土着化の道は歩めないだろうし、そもそもする気が無いのでは、と思われる。
・日本市場の現状で、YahooJapanにすでに大分水をあけられている
・今後、GoogleJapanがYahooJapanをキャッチアップするためのローカライズ戦略をとることはないだろう
この二点から、Googleは日本市場ではそれほど成功しないのでは、と予感する。と、いうのを5年後に見て外れていたらゴメンナサイだ。
HTTPS(HTTP over SSL)を実装するときに役に立つリンク集。
HTTPSのコーディング自体は、仕様が明確に規定されているので、頭をひねる点はあまり無い。ただ粛々と、淡々と、そしてキリキリ実装すれば良い。面倒だけど。
実装時に気をつけることは、当たり前だがセキュリティホールを作らないことだ。
ハカーな方々は色々とチャレンジしてくるので、基本的には入力されてくるデータは全て仕組まれていることがあると考えたほうがいい。クライアント側のデータ、コードセクションさえも改変されている可能性がある。
デカルトというおフランスの方は、全てを疑った果てに「自分の存在だけは否定できない」という哀しい結論に達したらしい。セキュリティに関するコーディングを行なう場合にも、誰を信じることができて、誰を信じてはいけないのかをはっきりとさせておくことが大切だ。
推理小説で言うと、アリバイをしっかり裏付ける。型破りな推理小説では、犯人がクローン人間だったとか、生き別れの双子の兄だったとか、宇宙人だったとか、そういう前衛的な結末も考えられる。全ての可能性を考えて、アリバイをじっくり検証することが必要だ。
SSLの登場人物で、アリバイがはっきりしているのは彼らだ。
ルート証明書の発行元
日本銀行みたいなもので、信じるしかないらしい
鍵交換アルゴリズム
暗号化アルゴリズム
ハッシュアルゴリズム
一応信じても良いらしい
自分の書いたコードはもちろん、信じてはいけない方だ。
実装に当たっては、参照すべき仕様がやや多く、仕様書&資料を集めるだけでもちょっとした手間だ。そんな時のために役立つリンク集
SSL3.0/TLS2.0
SSL & TLS Specifications
The SSL Protocol Version 3.0
SSL 2.0 PROTOCOL
SPECIFICATION
TLS/SSL Technical Reference
Introducing SSL and Certificates
SSL 3.0 Implementation Assistance 状態遷移が判りやすく解説されている
X509
X.509 : Information technology - Open Systems Interconnection - The Directory: Public-key and attribute certificate frameworks 有料
Certificate(電子証明書)とは?
X509 証明書の詳細
BER/DER
Recommendation X.690 有料
ASN.1 バイナリ変換規則 (BER)
ASN.1 および BER の簡単な紹介
ASN.1
Introduction to ASN.1
ASN.1 encoder/decoder on JavaScript (v0.3)
OID
オブジェクトIDは各ベンダがおおらかに使っているので、同等の暗号アルゴリズムでも違ったOIDが割り当てられていたりする。みなれないOIDがきたら、即Google。
OID Converter
OIDs for X.509 Certificate Library Modules
RSA
RSAのキー長は、RootCertでも2048bitが多いが、時々4096bit超の証明書があるので注意が必要
RSA Algorithm
Differences between ANSI X9.31 and RSA PKCS#1
MD2/MD5/RC4/SHA1
Other Cryptographic Techniques RSA本家
HTTP
RFC-Translations related HTTP RFC邦訳
HTTP入門
SSL tunneling
Tunneling TCP based protocols through Web proxy servers
Tunneling SSL Through a WWW Proxy
OpenSSL
テスト用証明書の作成にはX509ツールが断然お勧め
SSLクライアントのデバッグ用サーバに、WindowsSDKのSCHANNELサンプルのサーバと並行して使用する。どちらも微妙に挙動に怪しい点がある。
How to use OpenSSL
OpenSSL Command-Line HOWTO
HTTPSのコーディング自体は、仕様が明確に規定されているので、頭をひねる点はあまり無い。ただ粛々と、淡々と、そしてキリキリ実装すれば良い。面倒だけど。
実装時に気をつけることは、当たり前だがセキュリティホールを作らないことだ。
ハカーな方々は色々とチャレンジしてくるので、基本的には入力されてくるデータは全て仕組まれていることがあると考えたほうがいい。クライアント側のデータ、コードセクションさえも改変されている可能性がある。
デカルトというおフランスの方は、全てを疑った果てに「自分の存在だけは否定できない」という哀しい結論に達したらしい。セキュリティに関するコーディングを行なう場合にも、誰を信じることができて、誰を信じてはいけないのかをはっきりとさせておくことが大切だ。
推理小説で言うと、アリバイをしっかり裏付ける。型破りな推理小説では、犯人がクローン人間だったとか、生き別れの双子の兄だったとか、宇宙人だったとか、そういう前衛的な結末も考えられる。全ての可能性を考えて、アリバイをじっくり検証することが必要だ。
SSLの登場人物で、アリバイがはっきりしているのは彼らだ。
日本銀行みたいなもので、信じるしかないらしい
一応信じても良いらしい
自分の書いたコードはもちろん、信じてはいけない方だ。
実装に当たっては、参照すべき仕様がやや多く、仕様書&資料を集めるだけでもちょっとした手間だ。そんな時のために役立つリンク集
SSL3.0/TLS2.0
SSL & TLS Specifications
The SSL Protocol Version 3.0
SSL 2.0 PROTOCOL
SPECIFICATION
TLS/SSL Technical Reference
Introducing SSL and Certificates
SSL 3.0 Implementation Assistance 状態遷移が判りやすく解説されている
X509
X.509 : Information technology - Open Systems Interconnection - The Directory: Public-key and attribute certificate frameworks 有料
Certificate(電子証明書)とは?
X509 証明書の詳細
BER/DER
Recommendation X.690 有料
ASN.1 バイナリ変換規則 (BER)
ASN.1 および BER の簡単な紹介
ASN.1
Introduction to ASN.1
ASN.1 encoder/decoder on JavaScript (v0.3)
OID
オブジェクトIDは各ベンダがおおらかに使っているので、同等の暗号アルゴリズムでも違ったOIDが割り当てられていたりする。みなれないOIDがきたら、即Google。
OID Converter
OIDs for X.509 Certificate Library Modules
RSA
RSAのキー長は、RootCertでも2048bitが多いが、時々4096bit超の証明書があるので注意が必要
RSA Algorithm
Differences between ANSI X9.31 and RSA PKCS#1
MD2/MD5/RC4/SHA1
Other Cryptographic Techniques RSA本家
HTTP
RFC-Translations related HTTP RFC邦訳
HTTP入門
SSL tunneling
Tunneling TCP based protocols through Web proxy servers
Tunneling SSL Through a WWW Proxy
OpenSSL
テスト用証明書の作成にはX509ツールが断然お勧め
SSLクライアントのデバッグ用サーバに、WindowsSDKのSCHANNELサンプルのサーバと並行して使用する。どちらも微妙に挙動に怪しい点がある。
How to use OpenSSL
OpenSSL Command-Line HOWTO






