43574


他人の書いたソースコードからこの２つを見分ける方法はありますか？  
そういうツールなどがあれば教えていただきたいです。  
できればC、PHPまたはjavascriptでお願いします。

慣れれば瞬時にわかることなんでしょうけど、初心者なので簡単に整理する方法があればな、と思い質問させていただきました。


CC BY-SA 3.0
2018-04-26T07:46:16.610
43567
2018-06-08T07:52:46.700
28320
question
javascript php c
組み込み(ビルトイン)関数とユーザー定義関数について教えてください


jsタグがあるのでjsに限っての内容ですが投稿してみます。

## 定義

この回答では「組み込み関数」を次のいずれかに該当するものとして定義します。

  * [言語仕様](https://tc39.github.io/ecma262/)に定義されていて、処理系（ブラウザなど）が実装しており、処理系の実装のまま「変更」されていないもの
  * 処理系が独自の拡張で実装し、処理系の実装のまま「変更」されていないもの e.g. [nodeの組み込みモジュール実装](https://nodejs.org/dist/latest-v10.x/docs/api/)

これら以外の関数はすべてユーザー関数とします。

## 通常の場合

基本的には、言語仕様および処理系の仕様書・参考資料で組み込み関数であるとされているかどうかを調べれば十分でしょう*1。たとえば、Firefoxであれば[MDNにメソッド一覧があります](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Methods_Index)。ただし、言語仕様にあるからといって実装されているとは限りませんし（特に最近追加されたような新しい機能など）、また処理系についても文書がどのバージョンの処理系に対応しているか、などを気にする必要があります。

## 組み込み関数の「変更」

JavaScriptにおいて、ユーザーは組み込み関数を容易に置き換えることができます。これはprototypeのプロパティを変更する方法、オブジェクトのプロパティで直接実装する方法、などがあります。これらを総称して単に「変更」と呼ぶことにします。たとえば、次のコードは`Array.prototype.slice`関数をユーザー関数`f`で置き換え「変更」します。

[code]

    // 本来の実装
    console.assert([].slice === Array.prototype.slice);
    
    const f = () => { /* dummy */ };
    Array.prototype.slice = f;
    
    // 置き換えられている！
    console.assert([].slice === f);
    
[/code]

ソースコードを「読んで」見分けるとするならば、組み込み関数であるかを一般にチェックするためには、ライブラリ等を含めたコードすべてを確認して、これらが前述の例のように組み込み関数を置き換えていないかをチェックする必要があります。言い換えれば、全体のコードがなければ、またその実行順が明らかでなければ、実行時に組み込み関数がある呼び出しの実体として使われるかどうかは断定できません。

## 自動で検証可能か

原理的には前項で述べたようなことをコードの静的解析で調べればいいので、不可能ではありませんが、そうした処理を直接（つまり、ソースのm行n列めにある関数呼び出しの関数が組み込みであるか否かを出力/表示するような）実装しているような解析ツールの存在は、私はいまのところ聞いたことがありません。  
他方、部分的な解法としては、js向けlintツールとして有名な[ESlint](https://eslint.org/)の[no-extend-
native](https://eslint.org/docs/rules/no-extend-
native)なるルールを用いてprotypeの変更を特定するという方法が使えるかもしれません*2。

## リフレクション的な検証

どうやったら実行時に判断できるか、という方法の一つを紹介します。関数[Function.prototype.toString](https://tc39.github.io/ecma262/#sec-
function.prototype.tostring)は、関数オブジェクトの文字列表現を与える関数です。この「表現」というのは言語仕様では処理系依存とされているため注意が必要です。ただし、最近のメジャーなブラウザではある程度共通していて、次のようになっています。

  1. 関数がユーザー関数である場合  
ユーザーがjsコード中で定義した関数に関してはそのソースコードを返します。

[code]     const f = () => { return [] };

    console.log(f.toString()); // "() => { return [] }"
    
[/code]

  2. 関数が組み込み関数である場合  
組み込み関数オブジェクトに対して当該関数を呼び出す場合、返り値はソースコードではなく、組み込み関数であることを示す文字列になります。

[code]     const g = Array.prototype.slice;

    console.log(g.toString()); // "function slice() { [native code] }"
    
[/code]

処理系がこれらの出力に準ずること、そして`Function.prototype.toString`が「変更」されていないことを前提として、次のような検証コードを書くことができます。

[code]

    // 関数が組み込みであるかを判定する関数
    const __fpts = Function.prototype.toString;
    const isBuiltInFunction = f => typeof f === 'function' &&
        __fpts.call(f).slice(-15).startsWith('[native code]');
    
    // 例
    console.assert(isBuiltInFunction(__fpts)); // it must be true
    console.assert(isBuiltInFunction(isBuiltInFunction) === false);
    console.assert(isBuiltInFunction(function () {}) === false);
    console.log(isBuiltInFunction([].concat)); // (maybe) true
    
[/code]

ただし、これは与えられた関数が組み込みであるかを確認するだけの処理であって、組み込み関数だけれども本来あるべき関数でないといった状況には対応できません。

[code]

    // 組み込み関数を「変更」
    Array.prototype.slice = Array.prototype.concat;
    
    console.assert(isBuiltInFunction(Array.prototype.slice));
    // Array.prototype.concatは組み込み関数である
    
[/code]

また、すでに述べた通り、この処理は極めて処理系依存な振る舞いを用いた処理であり、これをなんらかのアプリケーションで応用することは推薦されません（または、十分な検証が必要です）。

## その他

たぶん質問者さんは、コードを読んでいて見覚えのない関数がでてきたときに、その処理がなんなのかを簡単に調べたいというだけなのかと思います（その意味において、この回答はあまりに冗長で実用性がないですね…）。数が膨大でなく、簡単に試せるならば、実際にコードを実行して、デバッガで止めて、関数やその入出力を調べたりしてしまうのが早いと思います。また、実際のところ、prototypeを上書きで変更あるいは拡張するようなことは（polyfillを除いて）珍しいので、オブジェクトが[組み込み型](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects)であればそのメソッドの呼び出しは組み込み関数であると期待していいようにも思えます。

* * *

*1: JavaScript界隈においては、prototypeを変更するのは _とても_ 推薦されないコーディングです。理由としては、今回の回答で述べているように、外部から検証するのが非常にとってもかなり面倒で、かつ未知の不具合を生じさせうるからです。  
*2: ページにいろいろ注意が書いてあります。また、私はこの機能の実装について詳細を確認していません。


CC BY-SA 3.0
2018-04-26T11:08:52.453
43574
2018-04-26T11:08:52.453
43567
answer


PHP の場合ですが `get_defined_functions()` で調べられます。  
戻り値の配列から `internal` キーで参照できる配列が「組み込み（ビルトイン）関数」で、  
戻り値の配列から `user` キーで参照できる配列が「ユーザー定義関数」となります。

[code]

    $defined_functions = get_defined_functions();
    var_dump($defined_functions['internal']);
    var_dump($defined_functions['user']);
    
[/code]

詳しくは下記よりマニュアルを参照ください。

> この関数は、組込（内部）関数およびユーザー定義関数を共に含む定義済みの全ての関数のリストを有する多次元配列を返します。  
>  内部関数は、 `$arr["internal"]` 、ユーザー定義関数は `$arr["user"]` によりアクセス可能となります。  
>  \-- <http://php.net/manual/ja/function.get-defined-functions.php>


CC BY-SA 4.0
2018-06-08T07:52:46.700
44614
2018-06-08T07:52:46.700
28832
43567
answer