JavaScriptがもっと好きになる。JavaScriptを知るために
JSがもっと好きになる。JSを知るために
「JavaScriptとは何か」
みたいな話はしません
「JavaScriptの歴史」
みたいな話でもないです
「JavaScriptを知るために」
知るためには?
「作ってみる」
のは大変なので
「解析してみる」
というわけで
「静的解析しましょうか」
「静的解析しましょうか」
(オチがわかっても何も言わないのが良い観衆)
「静的解析」とは
コードを文字列として分割し、どういった内容が書かれているか解釈すること
コードを文字列として分割し、どういった内容が書かれているか解釈すること
/*
block comment
*/
(function () {
//line comment
console.log(/regexp/);
return 'string';
})()
ただ、ちゃんとした解析は大変なので今回はコードとそれ以外のものに分割してみます
ここからはコードの話になります
その1
「エスケープのエンコード」
まず解析を簡単にするために\エスケープを%エンコード(%nn)します
ただ、単純に変換すると後で戻せないのでまずは%を%%へ変換します
code = code.replace(/%/g, '%%');
次に\エスケープを%エンコードします
(encodeAllは全文字列を%エンコードするfunctionと思ってください)
code = code.eplace(/\\./g, encodeAll);
これで\エスケープされた文字列がなくなったので"等の対応がとれた状態になりました
(function () {
var a = 1 % 1;
return "\"" + a + "\n";
})()
これで\エスケープされた文字列がなくなったので"等の対応がとれた状態になりました
(function () {
var a = 1 %% 1;
return "%22" + a + "%6e";
})()
その2
「リテラルの切り出し」
ここまでで"の対応はつくようになっていますが、更にコメントや正規表現内の記号もすべて変換することでコードと分離しやすくします
正規表現で一気に
code.replace(/(['"])([\s\S]*?)\1|\/\*([\s\S]*)\*\/|\/\/(.*?)$|\/(.+?)\//g, encodeAll);
正規表現で一気に
code.replace(/(['"])([\s\S]*?)\1|\/\*([\s\S]*)\*\/|\/\/(.*?)$|\/(.+?)\//g, encodeAll);
/*
function () {}
*/
(function () {
// return /hoge/
console.log(/[regexp]]/);
return ';';
})()
正規表現で一気に
code.replace(/(['"])([\s\S]*?)\1|\/\*([\s\S]*)\*\/|\/\/(.*?)$|\/(.+?)\//g, encodeAll);
/*
%20%66%75%6e%63%74%69%6f%6e%20%28%29%20%7b%7d
*/
(function () {
//%20%72%65%74%75%72%6e%20%2f%68%6f%67%65%2f
console.log(/%5b%72%65%67%65%78%70%5d/);
return ';';
})()
正規表現解説
(['"])[\s\S]*?\1 // string
|
\/\*[\s\S]*\*\/ // block comment
|
\/\/.*?$ // line comment
|
\/.+?\/ // regexp
入れ子判定が難しくなるので、解析は一回で行います
(「/* "" */」や「"/*" + "*/"」も解析できるように)
これで文字列、コメント、正規表現内の記号が%エスケープできました
/*
%20%66%75%6e%63%74%69%6f%6e%20%28%29%20%7b%7d
*/
(function () {
//%20%72%65%74%75%72%6e%20%2f%68%6f%67%65%2f
console.log(/%5b%72%65%67%65%78%70%5d/);
return ';';
})()
文字列、コメント、正規表現内の記号が%エスケープできればもう記号はコード内にしか無いので解析は容易になります
(functionを取得したい場合、'function'を全体から探せばいい)
コードの話はここまで
「なんに使うのか」
使い道
- 簡易コンパイラ(条件付きコンパイルとか)
- 簡易コード解析(console.log消すとか)
- 言語仕様の理解(実行エンジン作ってる人の気持ちがちょっとわかる)
- 文字列処理のノウハウ(正規表現でどう切り出すかとか)
「ちなみに」
。。。名前?
注意
ここで紹介したコードは以下の様なコードの解釈に失敗します
- /*\*/
- var hoge = 5 / 1; /* " 1; "が正規表現リテラルと解釈される */
- /[/]/
オチ
Esprimaはこのへんの問題なく解析してくれるので、まともに静的解析をしたい場合はEsprimaを使用しましょう
ご清聴ありがとうございました