latest log

酩酊状態で書いたエンジニアポエムです。酩酊状態で読んでください。

続: 型情報(宣言)と、実装を分離できる仕組み prof.js を実装してみました

http://uupaa.hatenablog.com/entry/2012/10/31/221550 の続きです。

prof.js は、
既存の関数やメソッドに、引数と戻り値の型チェック,プロファイラ,トレース機能を後付します。

簡単な使い方

簡単な使い方は以下のようになります。

以下のコードで、window.addEventListener と window.removeEventListener のプロファイリングとトレースが取れます。

<script src="polyfill/es.js"></script><!-- 古いブラウザで必要 -->
<script src="prof.js"></script>
<script>
prof.add(
    "window.addEventListener(eventType:String, handler:Function/Object, capture:Boolean = false)",
    "window.removeEventListener(eventType:String, handler:Function/Object, capture:Boolean = false)"
);
</script>

コードの品質が向上しプロファイラが不要になったら、prof.js を外してください。

一時的に off にしたい場合は prof.clear() を実行します。

改善点

昨日の版からは、

  • 仕様上の引数の数と、呼び出し時に指定された引数の数が合わない場合の警告表示の改善
  • 可変長引数の型チエックを実装
  • 関数にさらに関数を追加した多段関数(lib.fn.subfn) のフックとアンフックの実装
  • prof.add で指定された関数が実在しない場合に警告を出力
  • Function#help 機能を mofmof.js から prof.js に移植
  • mofmof.js に依存せず単体で動作可能に(mm.Class や mm.Interface を使う場合は mofmof.js が必要)

などの改善を行なっています。

多段関数( lib.fn.subfn )のテスト

// lib.js
var lib;

lib || (function(global) {

lib          = Lib;          // lib()
lib.fn       = Lib_fn;       // lib.fn()
lib.fn.subfn = Lib_fn_subfn; // lib.fn.subfn()

function Lib() {
}
function Lib_fn() {
    console.log("lib.fn");
    lib.fn.subfn();
}
function Lib_fn_subfn() {
    console.log("lib.fn.subfn");
}

//{@prof
if (typeof prof !== "undefined") {
    prof.add(
        'lib.fn()',
        'lib.fn.subfn()'
    );
}
lib.help.add(
    "http://example.com/oreore-library/",
    "lib"
);
//}@prof

})(this.self || global);
// test
> lib.fn()




> lib.fn.subfn()


> lib.fn
// lib.fn関数の内容を表示

> lib.fn.subfn
// lib.fn.subfn関数の内容を表示

> prof()





> prof.clear()

> lib.fn
// lib.fn関数の内容を再び表示
// test result
▼lib.fn
│ lib.fn
│▼lib.fn.subfn
└  lib.fn.subfn

▼lib.fn.subfn
└ lib.fn.subfn

// prof.js がフックしている状態
function __prof__() { ... }

// prof.js がフックしている状態
function __prof__() { ... }

// プロファイラ
function      time  count
lib.fn        3     1
lib.fn.subfn  1     2
function      time  count

// フック済みの関数が元に戻る

// prof.js のフックが外れている状態
function Lib_fn() {
    console.log("lib.fn");
    lib.fn.subfn();
}

関数のスペックと実際に指定された引数を比較

関数のスペック( "関数名 + 引数名 + 戻り値" な文字列 )で指定された引数の型と数、
そして実際に指定された引数の型と数を比較します。

  • 引数の型が違う
  • 引数の数が多すぎる
  • 戻り値の型が違う

といったケースにおいて警告の表示を改善しました。

// lib.js
var lib;

lib || (function(global) {

lib = function() {};
lib.fn1 = lib_fn1;  // lib.fn1()
lib.fn2 = lib_fn2;  // lib.fn2(a:Integer/String, b:Boolean = false):Object
lib.fn3 = lib_fn3;  // lib.fn3(...:Integer):Array

function lib_fn1() {
    return true;
}

function lib_fn2(a,   // @arg Integer/String:
                 b) { // @arg Boolean(= false):
                      // @ret Object:
                      // @help: lib.fn2

    return a ? []        // <- ERROR
             : { a: 1 };
}
function lib_fn3(ooo) { // @var_args Integer:
                        // @ret Array:
    return [1, 2];
}

//{@prof
if (typeof prof !== "undefined") {
    prof.add(
        'lib.fn1():Boolean',
        'lib.fn2(a:Integer/String, b:Boolean = false):Object',
        'lib.fn3(...:Integer):Array'
    );
}
lib.help.add(
    "http://example.com/oreore-library/",
    "lib");

//}@prof

})(this.self || global);
// test
> lib.fn1()



> lib.fn1( 1 )
// 引数過多





> lib.fn2()
// 引数不足





> lib.fn2( 123, { a: 123 } );
// 第二引数の型が不一致





> lib.fn2( 1, true );
// 戻値の型が不一致






> lib.fn2( 1, true, true );
// 引数過多





> lib.fn3( 1.2 );
// 型が不一致





> lib.fn3( 1, 1, 1, true, 1 );
// 型(true)が不一致
// test result
▼lib.fn1
true


TypeError:

>>> lib.fn1():Boolean
            ~
            1


TypeError: http://example.com/oreore-library/lib#lib.fn2

>>> lib.fn2(a:Integer/String, b:Boolean = false):Object
            ~~~~~~~~~~~~~~~~
            undefined


TypeError: http://example.com/oreore-library/lib#lib.fn2

>>> lib.fn2(a:Integer/String, b:Boolean = false):Object
                              ~~~~~~~~~
                              {"a":"123"}


▼lib.fn2
TypeError: http://example.com/oreore-library/lib#lib.fn2

>>> lib.fn2(a:Integer/String, b:Boolean = false):Object
                                                 ~~~~~~
                                                 [] 


TypeError: http://example.com/oreore-library/lib#lib.fn2

>>> lib.fn2(a:Integer/String, b:Boolean = false):Object
                                               ~
                                               true 


TypeError: 

>>> lib.fn3(...:Integer):Array
            ~~~~~~~~~~~
            1.2 


TypeError: 

>>> lib.fn3(...:Integer):Array
            ~~~~~~~~~~~
            true 

ご利用はこちらから、 http://code.google.com/p/mofmof-js/downloads/list