Firefox OS Advent Calendar 2013
12月11日 SpiderMonkey で syntax check してみる
Firefox OSと直接の関係性が薄い記事ですが、開発環境(IDE)まわりを物色中の旨、御了承下さい。
SpiderMonkeyは、MojillaさんのC言語によるJavaScriptエンジンで FirefoxやSeaMonkeyなどで利用されています。
SpiderMonkeyには、JavaScript shell もあって Ver1.8.5 の実行もできるそうです。
さらにYuta.Kikuchiの日記:SpiderMonkeyでのコマンドラインJavascriptを参照すると、Vim では、JSLintとSpiderMonkeyを利用した syntax check ができるそうです。
これを使えば、「JavaScriptの作法チェックやアプリ開発時のちょっとした挙動確認に便利!」
…と思いましたが Vim 使いではない私(泣)は、勉強のためコマンドラインからSpiderMonkeyでJSLintを利用できるようにしてみました。
【お詫び】
私のPC環境は、Ubuntu 12.04 LTS 64bit ですので、サンプル内容も Linux 用になっています。
他の環境の方、ごめんなさい。
SpiderMonkey JavaScript シェルのダウンロード
Mozilla Developer Network:Introduction to the JavaScript shellを読むと…
To get the SpiderMonkey JavaScript shell, see the SpiderMonkey Build Documentation or download a compiled binary for your platform from the Nightly Builds…とありますように、JavaScript shell を利用するだけでしたら、ソースコードを取得してビルドしなくても Nightly Build のバイナリが取得できるようです。
私は、利用中の Firefox のバージョンに合わせるため、Nightly Build ディレクトリから上に上がって 25.0.1-candidates/build1/jsshell-linux-x86_64.zip (64bit Linux版 47MB)をダウンロードしました。
SpiderMonkey JavaScript シェルのインストール
ダウンロードした jsshell-linux-x86_64.zip ファイルを[SpiderMonkey shell展開先]ディレクトりで展開してください。
展開先では jsshell-linux-x86_64 フォルダの下に、以下の4ファイルが配置されています。
- js
- libnspr4.so
- libplc4.so
- libplds4.so
1 2
$ export SPIDER_MONKEY_SHELL=[SpiderMonkey shell展開先]/jsshell-linux-x86_64 $ PATH=$PATH:${SPIDER_MONKEY_SHELL}
SpiderMonkey JavaScript シェルの使い方
前述した方法でターミナル上で js [ENTER] とするだけで、SpiderMonkey JavaScript shell が利用できるようになっています。
help() [ENTER] でヘルプ一覧が表示され、シェルを抜けるには、quit() [ENTER] を実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
$ JS js> help() JavaScript-C25.0.1 version([number]) Get or force a script compilation version number. options([option ...]) Get or toggle JavaScript options. load(['foo.js' ...]) Load files named by string arguments. Filename is relative to the current working directory. loadRelativeToScript(['foo.js' ...]) Load files named by string arguments. Filename is relative to the calling script. evaluate(code[, options]) Evaluate code as though it were the contents of a file. options is an optional object that may have these properties: compileAndGo: use the compile-and-go compiler option (default: true) noScriptRval: use the no-script-rval compiler option (default: false) fileName: filename for error messages and debug info lineNumber: starting line number for error messages and debug info global: global in which to execute the code newContext: if true, create and use a new cx (default: false) saveFrameChain: if true, save the frame chain before evaluating code and restore it afterwards catchTermination: if true, catch termination (failure without an exception value, as for slow scripts or out-of-memory) and return 'terminated' run('foo.js') Run the file named by the first argument, returning the number of of milliseconds spent compiling and executing it. readline() Read a single line from stdin. print([exp ...]) Evaluate and print expressions to stdout. printErr([exp ...]) Evaluate and print expressions to stderr. ~ 長いので省略 ~ system(command) Execute command on the current host, returning result code. trap([fun, [pc,]] exp) Trap bytecode execution. untrap(fun[, pc]) Remove a trap. js> js> quit() $
JSLint について
JavaScript: The Good Parts
-「良いパーツ」によるベストプラクティス
(著者)Douglas Crockford (翻訳)水野 貴明
JSLintは、私たちのJavaScriptコードの検証を行い、推奨されない書き方(アンチパターン)や構文エラー、使用されていない変数、未定義の変数の使用といった潜在的な問題が見つかった際に警告してくれます。…だそうです。
JavaScript: The Good Partsの著者でもある Douglas Crockford さんのサイト JSLint,The JavaScript Code Quality Toolでは、JavaScriptコードを張り付けるだけでオンライン上でチェックを行ってくれます。
特徴は、JavaScript: The Good Parts の著者であるため、Bad Parts(JavaScriptの悪い部分)についてのチェックがとても厳しいことだそうです。
ですが、JavaScriptの Bad Parts に注意してコーディングしていないと、一見、何の問題もないように見えて、思いがけない挙動をとるコードを書いてしまうそうですので…
くろまほうさいきょうでんせつ:JSLintとJSHintとhoisting深刻なバグに見舞われてしまわないよう「転ばぬ先の杖」を実践してくれているのでしょう。
hoistingってなに?var foo = 'bar'; (function(){ if(false){ var foo = 'BAZ'; } alert(foo); // bar でも BAZ でもなく... undefined ! })();↑のコードを実行すると"undefined"とアラートが出る。
このため普段何気なく使っているようなJavaScriptコードであっても、大量の警告が出されるので、使いこなすには、オプションのチェック項目とその内容についてをよく理解してから、チェックを外す項目を選択する必要があるそうです。
使い方やオプションについては、JSLint:Read the instructionsに記載されています。 邦訳については、前述のANALOGIG:JSLintを使おう!などのサイトを御参照ください。
コマンドラインから SpiderMonkey で JSLint を使う(基礎編)
JSLintをコマンドラインから使う(外部コマンドとして使えるようにする)ため、参考となる資料を探してみました。
SpiderMonkeyとJSLintを組み合わせた直接的な資料ではありませんが、
本項では、以下を参考にしています。
- SWEng Tips:JavaScript > JSLintをRhinoで実行する方法
- MACH3.laBlog:JSLintでストリクトなJavaScriptを書く
- hata'daizu:JavaScript文法チェック ソリューション(vim, コマンドライン)
解説すると長くなるので
参考資料とJSLintソースを独自解析したポイントは、以下の通りです。
- JSLint のソースは、GitHub:douglascrockford / JSLintから取得
- JSLint の本体は、jslint.js (旧名:fulljslint.js)という JavaScript
- jslint.js の実装説明は、jslint.js内にコメントで記述されている
- JSLINT(source, option)関数が、jslint.js の本体
- JSLINT関数の第一引数は、チェックするJavaScriptのコード行(string)のArrayをとる
- JSLINT関数の第二引数は、チェック・オプションのObjectをとる
- JSLINT関数でチェックした結果は、JSLINT.errorsプロパティにObjectの配列で格納される
- SpiderMonkey:read ビルトイン関数で、外部JSファイルを読み取ることができる
- (補足)SpiderMonkey:read ビルトイン関数で読み込むテキストは、BOMを取り除く必要がある
JSLint,The JavaScript Code Quality ToolのImplementationより
It is written in JavaScript. The full source code is available:https://github.com/douglascrockford/JSLint
JSLintのソース一覧
- README
- init_ui.js
- intercept.js
- jslint.html
- jslint.js
- lint.html
- title.png
コメント内での記述
1 2 3
// JSLINT is a global function. It takes two parameters. // var myResult = JSLINT(source, option);
コメント内での記述
1 2 3 4
// The first parameter is either a string or an array of strings. If it is a // string, it will be split on '\n' or '\r'. If it is an array of strings, it // is assumed that each string represents one line. The source can be a // JavaScript text or a JSON text.
コメント内での記述
1 2 3 4 5 6
// The second parameter is an optional object of options that control the // operation of JSLINT. Most of the options are booleans: They are all // optional and have a default value of false. One of the options, predef, // can be an array of names, which will be used to declare global variables, // or an object whose keys are used as global names, with a boolean value // that determines if they are assignable.
出力内容が/*jslint browser: true, devel: true */でしたら、 {browser:true, devel:true}を指定することになります。
コメント内での記述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// If it checks out, JSLINT returns true. Otherwise, it returns false. // If false, you can inspect JSLINT.errors to find out the problems. // JSLINT.errors is an array of objects containing these properties: // { // line : The line (relative to 0) at which the lint was found // character : The character (relative to 0) at which the lint was found // reason : The problem // evidence : The text line in which the problem occurred // raw : The raw message before the details were inserted // a : The first detail // b : The second detail // c : The third detail // d : The fourth detail // } // If a stopping error was found, a null will be the last element of the // JSLINT.errors array. A stopping error means that JSLint was not confident // enough to continue. It does not necessarily mean that the error was // especially heinous.
【注意】
問題が多いためにチェックが打ち切られた場合(Lint stop になった場合)は、配列の最終要素に null オブジェクトが返されています。
MOZILLA DEVELOPER NETWORK:Shell global objectsを見ると、 JavaScript シェル中から外部ファイルの内容を読み込むビルトイン関数として、read(filename)が見つかりますので、これを利用します。
実装としては、チェックするJavaScriptファイルを読み込んで、JSLINT関数の第一引数(JavaScriptコード行の配列)を作成することになります。
read 関数を利用して、日本語(UTF-8)を含む JavaScript ファイルを読み込むテスト中に、BOMが読み込まれて文字化けしたJavaScriptコードとして扱われることがありました。
このため、(読込文字).charCodeAt(0) === 65279 のようにしてBOMをチェックし、除外する処理を追加しています。
コマンドラインから SpiderMonkey で JSLint を使う(実装編)
前項のポイント
- JSLint のソースは、GitHub:douglascrockford / JSLintから取得
- JSLint の本体は、jslint.js (旧名:fulljslint.js)という JavaScript
- jslint.js の実装説明は、jslint.js内にコメントで記述されている
- JSLINT(source, option)関数が、jslint.js の本体
- JSLINT関数の第一引数は、チェックするJavaScriptのコード行(string)のArrayをとる
- JSLINT関数の第二引数は、チェック・オプションのObjectをとる
- JSLINT関数でチェックした結果は、JSLINT.errorsプロパティにObjectの配列で格納される
- SpiderMonkey:read ビルトイン関数で、外部JSファイルを読み取ることができる
- (補足)SpiderMonkey:read ビルトイン関数で読み込むテキストは、BOMを取り除く必要がある
コマンドラインから SpiderMonkey で JSLint を実行するためには、 外部から JSLint(JavaScript)に、チェックするJSファイルの内容(チェックするJSファイル名から取得されるもの)を渡さなくてはなりません。
コマンドラインで実行するシェルスクリプトには、任意のJSファイル名を引数として渡せますが、SpiderMonkey に渡せるファイル名は、実行対象のJSファイル名です。
SpiderMonkey に JSLint を実行させるスクリプトを書いたJSファイルを渡せても、そのスクリプトでチェックするJSファイル名を渡す良案が思いつきません。
そこで今回は、コマンドラインで受け取ったチェックするJSファイル名の内容を temp.js にコピーして、 JSLintを実行させるスクリプトは、毎回 temp.js ファイルの内容を読み込むようにします。
以上から、
- SpiderMonkey shell のディレクトリを[SPIDER_MONKEY_SHELL]とする
- JSLint 本体のjslint.jsを[SPIDER_MONKEY_SHELL]/scriptsに配置する
- JSLint を起動させるシェルスクリプト名をjs_syntax_checkとして
[SPIDER_MONKEY_SHELL]/scriptsに配置する - JSLint を実行させるJavaScriptファイル名をmy_jslint.jsとして
[SPIDER_MONKEY_SHELL]/scriptsに配置する - 作業用の temp.js ファイルは、[SPIDER_MONKEY_SHELL]/scriptsにコピーする
各スクリプトを作成しました。
スクリプト等のファイル配置は、以下のようになります。
[SPIDER_MONKEY_SHELL]/-+- js ←* | | SpiderMonkeyの +- libnspr4.so ←| JavaScript シェルファイル | | +- libplc4.so ←| | | +- libplds4.so ←* | +- scripts/-+- jslint.js ←JSLint 本体のJS | +- js_syntax_check ←syntax checkの | シェル・スクリプト +- my_jslint.js ←syntax checkのJS | +- temp.js ←作業用のJSコピー
続いては、各スクリプトの実装内容です。
js_syntax_check シェル・スクリプトの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#!/bin/sh if [ $# = 1 ] ; then if [ -e $1 ] ; then echo JSLint start. else echo argument file is not exist. echo if you run with no arguments, help is displayed. echo exit fi else echo spider_monkey_syntax_check echo Description; echo a syntax checker of JS file echo by using the JSLint and SpiderMonkey. echo echo Usage: echo js_syntax_check JS_file_path echo exit fi export SPIDER_MONKEY_SHELL=[SpiderMonkey shellのディレクトリ] #SyntaxチェックするJSを作業用JSにコピー cp $1 ${SPIDER_MONKEY_SHELL}/scripts/temp.js #Syntaxチェック実行 ${SPIDER_MONKEY_SHELL}/js -f ${SPIDER_MONKEY_SHELL}/scripts/my_jslint.sh echo JSLint end.
my_jslint.sh JavaScriptの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
function checkSyntax () { var SHELL_HOME = "[SpiderMonkey shellのディレクトリ]"; //Syntax チェックするJSの読み込み var data = read(SHELL_HOME + "/scripts/temp.js"); var srcLines = []; var line = ""; for(var i = 0; i < data.length; i++){ var ch = data[i]; //BOM と CR は、無視します。 if(ch.charCodeAt(0) === 65279 || ch === '\r') continue; if(ch === "\n" || i === (data.length-1)){ srcLines.push(line); line = ""; }else{ line += ch; } } //1024文字分の空白を確保する。 (注)JS圧縮は、考慮していません! var space = " "; //32文字分のSpace space += space; space += space; space += space; space += space; space += space; //JSLint オプション設定 (詳細は、http://www.jslint.com/ のオプションを参照) var options = {browser: true, devel: true, sloppy: true}; //JSLint 読み込み load(SHELL_HOME + "/scripts/jslint.js"); //JSLint で、Syntax チェックを行う var result = JSLINT(srcLines, options); if(result === false){ JSLINT.errors.forEach( function(e) { if(e !== null){ if(e.evidence !== undefined){ print("line:"+e.line + ", column:" + e.character + ", " + e.reason); //インデントを直す print("->" + e.evidence); print(space.substr(0, e.character-1) + " ^"); }else{ //Lint stop時の対処 print("line:"+e.line + ", column:" + e.character + ", " + e.reason); //インデントを直す } } } ); } }; checkSyntax();
コマンドラインから syntax check する
作成したスクリプトで、syntax check してみます。
1 2
$ export SPIDER_MONKEY_SHELL=[SpiderMonkey shellのディレクトリ] $ PATH=$PATH:${SPIDER_MONKEY_SHELL}/scripts/js_syntax_check
js_syntax_check CHECK_JS_PATH #CHECK_JS_PATH:チェックするJSファイルへのパス
(例)exam.js は、"for(var i = 0; i < 5; i++);"のみ定義
1 2 3 4 5 6 7 8 9 10
$ js_syntax_check exam.js JSLint start. line:1, column:4, Missing space between 'for' and '('. ->for(var i = 0; i < 5; i++); ^ line:1, column:5, Move 'var' declarations to the top of the function. ->for(var i = 0; i < 5; i++); ^ line:1, column:5, Stopping. (100% scanned). JSLint end.
引数を指定しなかった場合は、ヘルプが表示されます。
1 2 3 4 5 6 7 8
$ js_syntax_check js_syntax_check Description a syntax checker of JS file by using the JSLint and SpiderMonkey. Usage: spider_monkey_syntax_check JS_file_path
【番外編】実際に使ってみる
コンソールの使えるエディタ(jEdit)で、JavaScriptファイルを開きながら、コンソールで syntax check させてみました。
jEditを起動するスクリプトには、あらかじめ SpiderMonkery shell や js_syntax_check へのパスを通すようにする必要がありますが、 コンソールに移動して、編集中のJSファイルのチェックも当然できます。
jEditは、BeanSellというJavaオブジェクトを利用できるスクリプト機能もありますし、編集中の画面とBeanSellとの連携もできます。
BeanSellとシェルスクリプトに、SpiderMonkey(+JSLint)を組み合わせれば、簡単な自動テストが作れるかもしれません。
今後、ちょっとした実験や自動処理に活用できたら良いなぁと思いました。
【追伸】jEdit は、バージョン5.0から標準で日本語対応されたそうです。
SourceForge.JP:jEdit プロジェクト日本語トップページ
オチ...
昨日、関東Firefox OS 勉強会 5th の資料を見て気づいたのですが、
dynamisさんの関東 Firefox OS 勉強会 5th で使用したスライド
Firefox OS Updates 201311の5ページ目より
JavaScript 構文チェックFirefox OS では、JSLint でなく gslint が使われているそうです... orz
JSlint の話がありました。
Firefox OS (Gaia)では Closure Tools の gslint が使われています
https://developers.google.com/closure/utilities/docs/linter_howto?hl=ja&csw=1
コーディングルールを守ろう