Google Chrome に搭載されたオープンソースの JavaScript エンジン "v8" を試す
その発表から世間を騒がせた、Google の Web ブラウザ Chrome が遂に公開された。
Mac ユーザなうえに、日々の Web ブラウジングは Safari と iPhone で満足していることもあり、Chrome 自体への興味は薄い。しかし、オープンソースの JavaScript エンジンも独自開発している、となると話は別だ。しかも、Strongtalk や HotSpot Java VM の開発者が関わっている、というのだから俄然興味が湧いてくる(参考記事)。
Google が開発、公開している JavaScript 処理系は、その名も v8。
v8 の特徴
では、v8 には既存の処理系に比べて、どのような利点があるのだろうか。Google がわざわざ独自に開発し、Chrome という重要なプロダクトに搭載して公開するくらいだから、そこには他の処理系では達成できないアドバンテージがあるはずである。
Google Code で公開されている文書 Design Elements から概観をつかんでみよう。
オブジェクトのプロパティへのアクセスが高速 JavaScript ではオブジェクトのプロパティを自由に追加・削除できる。
これを実現するためには、ハッシュ表や二分木といった辞書による実装が一般的だが、そうするとプロパティにアクセスするたびに辞書を検索しなくてはいけない。
v8 では hidden class という実装テクニックを用いることにより、オブジェクトのプロパティへのアクセス方法が、オブジェクトの格納されているメモリのオフセットへのアクセスとなる。そのため、無駄な検索を省くことができ、高速なアクセスを実現している。
動的なマシンコード生成 JavaScript のコードは最初に実行されるときにマシンコードに変換される。バイトコードやインタプリタはない。また、プロパティへのアクセスは特別扱いされ、インライン・キャッシュという Smalltalk 実装系由来の技法が使われているらしい(参考:Efficient Implementation of the Smalltalk-80 System)
効率的なガベージコレクション v8 のガベージコレクションの特徴は以下のとおりである:
- stop-the-world ガベージコレクション中はプログラムが停止する
- 世代別ガベージコレクション 一度のガベージコレクションではオブジェクトの一部のみを検査することにより、プログラム停止時間を短縮
- “保守的"ではない このため、未使用のオブジェクトをポインタと判定してしまい、メモリリークすることがない
- メモリ・コンパクションができる メモリのフラグメンテーションを防ぐ
総じて、バランスの良い実装を選びながら、特にメモリ効率に気をつかっている印象を受ける。
試す
どうだろう? v8 に興味が湧いてきたんじゃないだろうか。
では、能書きはこれくらいにして、実際に v8 を使ってみよう。なお、環境は Mac OS X Leopard、Xcode はインストール済みで GCC, Python, Subversion などがインストールされているものとする。
まずは適当なディレクトリに、ソースコードをチェックアウトする。
% cd Developer/Workspace/JavaScript/
% svn checkout http://v8.googlecode.com/svn/trunk/ v8
% cd v8/
ビルドには SCons というビルドツールが必要だ。MacPorts でインストールしておこう。
% sudo port install scons
ビルドオプションは、scons --help
で確認できる。
% scons --help
scons: Reading SConscript files ...
scons: done reading SConscript files.
mode: compilation mode (debug, release)
default: release
actual: release
sample: build sample (shell, process)
...
とりあえず試したいだけなので、デフォルトの設定でビルドしてみようか。
% scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
...
同じディレクトリに libv8.a
というライブラリができたはずだ。
簡単な C++ プログラムで使ってみよう
他の C++ プログラムに組み込んで使うことを念頭に開発されているのも、v8 の特徴だ。プロジェクト・サイトにある Hello World プログラムよりは実用的なプログラムで感触をつかんでみよう。
以下は、標準入力から受け取ったプログラムを構文解析して実行、結果を標準出力に書き出すプログラムだ(Gist にもアップしておいた。gist:8559)。
#include <iostream>
#include <sstream>
#include <v8.h>
using namespace v8;
int main(int argc, const char **argv) {
// Create a scope and environment
HandleScope scope;
Handle<Context> context = Context::New();
Context::Scope context_scope(context);
// Reading a source code
std::stringbuf buffer;
std::cin.get(buffer, EOF);
Handle<String> source = String::New(buffer.str().c_str());
// Register try/cache handler for error reporting
TryCatch try_catch;
// Compile the source code.
Handle<Script> script = Script::Compile(source);
if (script.IsEmpty()) {
// Print errors that happened during compilation.
String::AsciiValue error(try_catch.Exception());
std::cout << *error << std::endl;
return -1;
}
// Running script
Handle<Value> result = script->Run();
if (result.IsEmpty()) {
// Print errors that happened during execution.
String::AsciiValue error(try_catch.Exception());
std::cout << *error << std::endl;
return -1;
}
// Convert the result to an ASCII string and print it.
String::AsciiValue ascii(result);
std::cout << *ascii << std::endl;
return 0;
}
このプログラムは gcc で次のようにコンパイルする。V8_HOME
環境変数には v8 をチェックアウトしたディレクトリを設定してほしい。
% g++ -I${V8_HOME}/include -L${V8_HOME} -lv8 -o simple_v8_shell simple_v8_shell.cc
実行例:
% ./simple_v8_shell
'Hello, ' + 'World!'
Hello, World!
% ./simple_v8_shell
1 + 2 * 3
7
% ./simple_v8_shell
var fib = function(n) {
return (n < 2) ? n : fib(n - 2) + fib(n - 1)
}
fib(10)
55
最後に
現時点では、v8 が実際のところどれだけのものなのか、確証をもって言えることは多くない。しかし、v8 の登場で強く感じたことがふたつある。
まず、JavaScript エンジンの実装は Tamarin (Adobe/Mozilla)、SquirrelFish (Apple)、そして、今回の v8 (Google) … 強豪たちとホットな技術がひしめく戦場になりつつある。手の出尽くした感があるブラウザ競争とは異なり、この戦場はまさに「未開のフロンティア」だろう。
また、ここ数年で多くのプログラマに浸透した動的言語だが、これから数年のうちに、今度は動的言語の効率的な実装もプログラマ一般に浸透していくのではないか、という思いを強くした。