2012年2月29日水曜日

V8 JavaScript Engine libraries.cc

V8と言って思い浮かべるのがエンジンなのか、JavaScriptなのか、と言うと私はJavaScript。と言う訳で今回はV8 JavaScript Engineについて調査。

 V8をビルドするとobj/releaseもしくはobj/debug以下にlibraries.ccexperimental-libraries.ccと言うソースコードとそのビルド結果となるlibraries.oexperimenta-libraries.oいう合計4個のファイルが生成される。これは一体どうやって作られて、どんな目的のモノなのであろうか。

 まずはビルド時のログ出力をlibrariesをキーに検索すると、以下のような出力がある事が分かる(ちなみに、以下の出力はscons d8 mode=debugでビルドした時のもの。他の場合でも同じだとは思う)。
JS2C(["obj/debug/libraries.cc"], ["src/runtime.js", "src/v8natives.js", "src/array.js", "src/string.js", "src/uri.js", "src/math.js", "src/messages.js", "src/apinatives.js", "src/date.js", "src/regexp.js", "src/json.js", "src/liveedit-debugger.js", "src/mirror-debugger.js", "src/debug-debugger.js", "src/macros.py"])
g++ -o obj/debug/libraries.o -c -fno-rtti -fno-exceptions -fvisibility=hidden -Wall -Werror -W -Wno-unused-parameter -Woverloaded-virtual -Wnon-virtual-dtor -pedantic -m32 -g -O0 -ansi -DV8_TARGET_ARCH_IA32 -DENABLE_DISASSEMBLER -DDEBUG -DENABLE_DEBUGGER_SUPPORT -DV8_ENABLE_CHECKS -DOBJECT_PRINT -Iobj/debug -Isrc -Isrc obj/debug/libraries.cc
JS2C(["obj/debug/experimental-libraries.cc"], ["src/proxy.js", "src/collection.js", "src/macros.py"])
g++ -o obj/debug/experimental-libraries.o -c -fno-rtti -fno-exceptions -fvisibility=hidden -Wall -Werror -W -Wno-unused-parameter -Woverloaded-virtual -Wnon-virtual-dtor -pedantic -m32 -g -O0 -ansi -DV8_TARGET_ARCH_IA32 -DENABLE_DISASSEMBLER -DDEBUG -DENABLE_DEBUGGER_SUPPORT -DV8_ENABLE_CHECKS -DOBJECT_PRINT -Iobj/debug -Isrc -Isrc obj/debug/experimental-libraries.cc
 どうもJS2Cでccファイルを生成して、その後にg++でビルドしている様子。JS2Cと言う関数名からするとJavaScriptをCに変換しているのであろう。そのJS2Cはtools/js2c.pyファイルに定義されてて、そのファイルの先頭には以下の様なコメントがある。
# This is a utility for converting JavaScript source code into C-style
# char arrays. It is used for embedded JavaScript code in the V8
# library.
 つまり、JavaSciptコードをC形式の文字列配列に変換する、と言う事でJavaScript関数をCの関数に変換している訳ではないようなので、ちょっとがっかり。

 とりあえずJS2Cの第二引数に与えられているjsもpyもsrc以下と言う事なのでsrcフォルダを見てみると、jsもpyも全てそこに存在している。とりあえずv8natives.jsの中身を見てみると、どうやらJavaScriptコードの様子。。。なのだが微妙に違う記法が混じっていて、関数名の先頭に%記号が付いている。。。。こんなのありだっけか?と思ったが、恐らくpyの方で何かしらの変換をするのかも知れない。

// Helper function used to install functions on objects.
function InstallFunctions(object, attributes, functions) {
  if (functions.length >= 8) {
    %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
  }
  for (var i = 0; i < functions.length; i += 2) {
    var key = functions[i];
    var f = functions[i + 1];
    %FunctionSetName(f, key);
    %FunctionRemovePrototype(f);
    %SetProperty(object, key, f, attributes);
    %SetNativeFlag(f);
  }
  %ToFastProperties(object);
}
とりあえずsrcフォルダ以下でOptimizeObjectForAddingMultiplePropertiesを検索キーとしてgrepをかけてみると以下のファイルが見つかった(subversion関連は除外)。
./src/v8natives.js
./src/runtime.cc
./src/runtime.h
./src/math.js
どうもOptimizeObjectForAddingMultiplePropertiesはruntime.ccで定義されているcの関数である事が分かった。と言う事は%が先頭についた関数はcで定義された関数を直接記述する特別な記法なのかも知れない。

次にlibraries.ccの中を見ると以下の通りで、正確にはsources[]の中身を見る必要があるが、やはりスクリプトをC形式の文字列データに置き換えて、V8からアクセス出来る様にしている様子。
// Copyright 2011 Google Inc. All Rights Reserved. 
// This file was generated from .js source files by SCons.  If you
// want to make changes to this file you should either change the
// javascript source files or the SConstruct script.
#include "v8.h"
#include "natives.h"
#include "utils.h"
namespace v8 {
namespace internal {
static const byte sources[] = { .....(数値の羅列).....};
static const char* raw_sources = reinterpret_cast<const char*>(sources);  // const byteをconst charにキャスト
  template <>
  int NativesCollection<CORE>::GetBuiltinsCount() { // ビルトイン関数の個数
    return 14;
  }
  template <>
  int NativesCollection<CORE>::GetDebuggerCount() {  // デバッガー関数の個数
    return 3;
  }
  template <>
  int NativesCollection<CORE>::GetIndex(const char* name) { // オブジェクト名からインデックスを導出
    if (strcmp(name, "liveedit") == 0) return 0;
 。。。
    if (strcmp(name, "json") == 0) return 13;
    return -1;
  }
  template <>
  int NativesCollection<CORE>::GetRawScriptsSize() { // 生スクリプトサイズ
    return 238136;
  }
  template <>
  Vector<const char> NativesCollection<CORE>::GetRawScriptSource(int index) { // 生スクリプトソースをインデックスをキーに取得
    if (index == 0) return Vector<const char>(raw_sources + 144015, 15179);
 。。。
    if (index == 13) return Vector<const char>(raw_sources + 138135, 5880);
    return Vector<const char>("", 0);
  }
  template <>
  Vector<const char> NativesCollection<CORE>::GetScriptName(int index) { // スクリプト名をインデックスをキーに取得
    if (index == 0) return Vector<const char>("native liveedit.js", 18);
 。。。
    if (index == 13) return Vector<const char>("native json.js", 14);
    return Vector<const char>("", 0);
  }
  template <>
  Vector<const byte> NativesCollection<CORE>::GetScriptsSource() { // スクリプトソースを取得
    return Vector<const byte>(sources, 238136);
  }
  template <>
  void NativesCollection<CORE>::SetRawScriptsSource(Vector<const char> raw_source) { // 生スクリプトの設定
    ASSERT(238136 == raw_source.length());
    raw_sources = raw_source.start();
  }
}  // internal
}  // v8
 しかし、スクリプトコードをV8から見る事がどんな状況で必要なのだろうか?ここのスクリプトコードをJITコンパイルしてからbuilt-inオブジェクトのメソッドは実行されている?少なくともbuilt-in関数のソースコードはJavaScriptでは見える必要はないので、JITが有力である気がする。となると独自のbuilt-in関数を組み込む場合にはjsファイルを作成して、SConsのスクリプトを書き換えれば、追加が出来る様になるとか?

うーん、もう少し調べたいが、遅いので今日はこれまで。

0 件のコメント:

コメントを投稿