V8 の埋め込み入門

このドキュメントでは、V8 の主要な概念をいくつか紹介し、「hello world」の例を使って V8 コードを使い始める方法を説明します。

対象読者 #

このドキュメントは、C++ アプリケーションに V8 JavaScript エンジンを埋め込みたい C++ プログラマーを対象としています。独自のアプリケーションの C++ オブジェクトとメソッドを JavaScript から利用できるようにし、JavaScript のオブジェクトと関数を C++ アプリケーションから利用できるようにする方法を説明します。

Hello world #

JavaScript のステートメントを文字列引数として受け取り、JavaScript コードとして実行し、結果を標準出力に出力する Hello World の例 を見てみましょう。

まず、いくつかの重要な概念

これらの概念については、詳細ガイド で詳しく説明しています。

サンプルを実行する #

以下の手順に従って、サンプルを自分で実行してください。

  1. Git の手順 に従って、V8 のソースコードをダウンロードします。

  2. この hello world サンプルの手順は、V8 v11.9 で最後にテストされています。 このブランチは、git checkout branch-heads/11.9 -b sample -t でチェックアウトできます。

  3. ヘルパースクリプトを使用してビルド構成を作成します

    tools/dev/v8gen.py x64.release.sample

    ビルド構成を確認して手動で編集するには、以下を実行します

    gn args out.gn/x64.release.sample
  4. Linux 64 システムで静的ライブラリをビルドします

    ninja -C out.gn/x64.release.sample v8_monolith
  5. ビルドプロセスで作成された静的ライブラリにリンクして、hello-world.cc をコンパイルします。たとえば、GNU コンパイラを使用する 64 ビット Linux では、次のようになります。

    g++ -I. -Iinclude samples/hello-world.cc -o hello_world -fno-rtti -lv8_monolith -lv8_libbase -lv8_libplatform -ldl -Lout.gn/x64.release.sample/obj/ -pthread -std=c++17 -DV8_COMPRESS_POINTERS -DV8_ENABLE_SANDBOX
  6. より複雑なコードの場合、V8 は ICU データファイルがないと失敗します。このファイルをバイナリが格納されている場所にコピーします

    cp out.gn/x64.release.sample/icudtl.dat .
  7. コマンドラインで hello_world 実行可能ファイルを実行します。たとえば、Linux の V8 ディレクトリで、以下を実行します。

    ./hello_world
  8. Hello, World! と出力されます。やった!

メインブランチと同期しているサンプルを探している場合は、hello-world.cc ファイルを確認してください。これは非常に単純な例であり、文字列としてスクリプトを実行する以上のことをしたいと思うでしょう。 以下の詳細ガイド には、V8 埋め込み者向けの詳しい情報が記載されています。

その他のサンプルコード #

以下のサンプルは、ソースコードのダウンロードの一部として提供されています。

process.cc #

このサンプルは、たとえば Web サーバーの一部である可能性のある、架空の HTTP リクエスト処理アプリケーションをスクリプト化できるように拡張するために必要なコードを提供します。 Process という関数を指定する必要がある JavaScript スクリプトを引数として取ります。 JavaScript の Process 関数は、たとえば、架空の Web サーバーによって提供される各ページのヒット数を収集するなど、情報を収集するために使用できます。

shell.cc #

このサンプルは、ファイル名を引数として受け取り、その内容を読み取って実行します。 JavaScript コードスニペットを入力して実行できるコマンドプロンプトが含まれています。このサンプルでは、オブジェクトテンプレートと関数テンプレートを使用して、print などの追加関数が JavaScript に追加されています。

詳細ガイド #

スタンドアロンの仮想マシンとしての V8 の使用方法と、ハンドル、スコープ、コンテキストなどの V8 の主要な概念について理解したので、これらの概念についてさらに詳しく説明し、V8 を独自の C++ アプリケーションに埋め込むための鍵となる他のいくつかの概念を紹介します。

V8 API は、スクリプトのコンパイルと実行、C++ メソッドとデータ構造へのアクセス、エラー処理、セキュリティチェックの有効化のための関数を提供します。アプリケーションは、他の C++ ライブラリと同様に V8 を使用できます。 C++ コードは、ヘッダー include/v8.h をインクルードすることにより、V8 API を介して V8 にアクセスします。

ハンドルとガベージコレクション #

ハンドルは、ヒープ内の JavaScript オブジェクトの場所への参照を提供します。 V8 ガベージコレクターは、アクセスできなくなったオブジェクトによって使用されているメモリを再利用します。ガベージコレクションプロセス中、ガベージコレクターはしばしばオブジェクトをヒープ内の異なる場所に移動します。ガベージコレクターがオブジェクトを移動すると、ガベージコレクターはオブジェクトを参照するすべてのハンドルをオブジェクトの新しい場所で更新します。

オブジェクトは、JavaScript からアクセスできず、それを参照するハンドルがない場合、ガベージと見なされます。ガベージコレクターは、ガベージと見なされるすべてのオブジェクトを定期的に削除します。 V8 のガベージコレクションメカニズムは、V8 のパフォーマンスの鍵となります。

ハンドルにはいくつかの種類があります

もちろん、オブジェクトを作成するたびにローカルハンドルを作成すると、多くのハンドルが発生する可能性があります。これは、ハンドルスコープが非常に役立つところです。ハンドルスコープは、多くのハンドルを保持するコンテナと考えることができます。ハンドルスコープのデストラクタが呼び出されると、そのスコープ内で作成されたすべてのハンドルがスタックから削除されます。ご想像のとおり、これにより、ハンドルが指すオブジェクトは、ガベージコレクターによってヒープから削除される資格を得ます。

非常に単純な hello world の例 に戻ると、次の図にハンドルスタックとヒープ割り当てオブジェクトを示します。 Context::New()Local ハンドルを返し、Persistent ハンドルの使用方法を示すために、それに基づいて新しい Persistent ハンドルを作成することに注意してください。

デストラクタ HandleScope::~HandleScope が呼び出されると、ハンドルスコープが削除されます。削除されたハンドルスコープ内のハンドルによって参照されるオブジェクトは、それらへの他の参照がない場合、次のガベージコレクションで削除される資格があります。ガベージコレクターは、source_objscript_obj オブジェクトもヒープから削除できます。これは、それらがハンドルによって参照されなくなり、JavaScript から到達できなくなったためです。コンテキストハンドルは永続ハンドルであるため、ハンドルスコープが終了しても削除されません。コンテキストハンドルを削除する唯一の方法は、明示的に Reset を呼び出すことです。

**注意:** このドキュメント全体で、「ハンドル」という用語はローカルハンドルを指します。永続ハンドルの場合は、その用語を完全な形で使用します。

このモデルには、1 つのよくある落とし穴があることに注意することが重要です。*ハンドルスコープを宣言する関数からローカルハンドルを直接返すことはできません*。そうすると、返そうとしているローカルハンドルは、関数が戻る直前にハンドルスコープのデストラクタによって削除されてしまいます。ローカルハンドルを返す正しい方法は、HandleScope の代わりに EscapableHandleScope を構築し、ハンドルスコープの Escape メソッドを呼び出して、返したい値を持つハンドルを渡すことです。実際にどのように動作するかの例を次に示します。

// This function returns a new array with three elements, x, y, and z.
Local<Array> NewPointArray(int x, int y, int z) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();

// We will be creating temporary handles so we use a handle scope.
v8::EscapableHandleScope handle_scope(isolate);

// Create a new empty array.
v8::Local<v8::Array> array = v8::Array::New(isolate, 3);

// Return an empty result if there was an error creating the array.
if (array.IsEmpty())
return v8::Local<v8::Array>();

// Fill out the values
array->Set(0, Integer::New(isolate, x));
array->Set(1, Integer::New(isolate, y));
array->Set(2, Integer::New(isolate, z));

// Return the value through Escape.
return handle_scope.Escape(array);
}

Escape メソッドは、引数の値を囲んでいるスコープにコピーし、すべてのローカルハンドルを削除してから、安全に返すことができる新しいハンドルコピーを返します。

コンテキスト #

V8では、コンテキストとは、それぞれ独立した無関係のJavaScriptアプリケーションをV8の単一インスタンス内で実行できる実行環境です。JavaScriptコードを実行するコンテキストを明示的に指定する必要があります。

なぜこれが必要なのでしょうか?JavaScriptは、JavaScriptコードによって変更可能な組み込みユーティリティ関数とオブジェクトのセットを提供しているためです。たとえば、完全に無関係な2つのJavaScript関数が両方ともグローバルオブジェクトを同じ方法で変更した場合、予期しない結果が発生する可能性が高くなります。

CPU時間とメモリの観点から見ると、構築する必要がある組み込みオブジェクトの数を考えると、新しい実行コンテキストを作成するのはコストのかかる操作のように思えるかもしれません。しかし、V8の広範なキャッシングにより、最初に作成するコンテキストはある程度コストがかかりますが、後続のコンテキストははるかに安価になります。これは、最初のコンテキストは組み込みオブジェクトを作成し、組み込みJavaScriptコードを解析する必要があるのに対し、後続のコンテキストはそれぞれのコンテキストの組み込みオブジェクトを作成するだけで済むためです。V8のスナップショット機能(ビルドオプションsnapshot=yesで有効化され、デフォルトです)を使用すると、スナップショットにはシリアライズされたヒープが含まれており、組み込みJavaScriptコードのコンパイル済みコードが含まれているため、最初のコンテキストの作成に費やされる時間が高度に最適化されます。ガベージコレクションに加えて、V8の広範なキャッシングもV8のパフォーマンスの鍵となります。

コンテキストを作成したら、何度でも出入りできます。コンテキストAにいる間に別のコンテキストBに入ることもできます。これは、現在のコンテキストAをBに置き換えることを意味します。Bを終了すると、Aが現在のコンテキストとして復元されます。これは以下に示されています。

各コンテキストの組み込みユーティリティ関数とオブジェクトは個別に保持されていることに注意してください。コンテキストを作成するときに、オプションでセキュリティトークンを設定できます。詳細については、セキュリティモデルセクションを参照してください。

V8でコンテキストを使用する動機は、ブラウザの各ウィンドウとiframeが独自の新しいJavaScript環境を持つことができるようにするためでした。

テンプレート #

テンプレートは、コンテキスト内のJavaScript関数とオブジェクトの青写真です。テンプレートを使用して、C++関数とデータ構造をJavaScriptオブジェクト内にラップし、JavaScriptスクリプトで操作できるようにすることができます。たとえば、Google Chromeはテンプレートを使用して、C++ DOMノードをJavaScriptオブジェクトとしてラップし、グローバル名前空間に関数をインストールします。テンプレートのセットを作成し、作成するすべての新しいコンテキストで同じテンプレートを使用できます。必要な数のテンプレートを持つことができます。ただし、特定のコンテキストには、テンプレートのインスタンスを1つだけ持つことができます。

JavaScriptでは、関数とオブジェクトの間に強い二重性があります。JavaまたはC++で新しいタイプのオブジェクトを作成するには、通常、新しいクラスを定義します。JavaScriptでは、代わりに新しい関数を作成し、その関数をコンストラクタとして使用してインスタンスを作成します。JavaScriptオブジェクトのレイアウトと機能は、それを構築した関数と密接に関連しています。これは、V8テンプレートの動作方法に反映されています。テンプレートには2つのタイプがあります。

次のコードは、グローバルオブジェクトのテンプレートを作成し、組み込みグローバル関数を設定する例を示しています。

// Create a template for the global object and set the
// built-in global functions.
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
global->Set(v8::String::NewFromUtf8(isolate, "log"),
v8::FunctionTemplate::New(isolate, LogCallback));

// Each processor gets its own context so different processors
// do not affect each other.
v8::Persistent<v8::Context> context =
v8::Context::New(isolate, nullptr, global);

このコード例は、process.ccサンプルのJsHttpProcessor::Initializerから抜粋したものです。

アクセサー #

アクセサーは、JavaScriptスクリプトによってオブジェクトプロパティにアクセスされたときに値を計算して返すC++コールバックです。アクセサーは、SetAccessorメソッドを使用して、オブジェクトテンプレートを介して構成されます。このメソッドは、関連付けられているプロパティの名前と、スクリプトがプロパティの読み取りまたは書き込みを試みたときに実行する2つのコールバックを受け取ります。

アクセサーの複雑さは、操作するデータのタイプによって異なります。

静的グローバル変数へのアクセス #

コンテキスト内でグローバル変数としてJavaScriptで使用できるようにする2つのC++整数変数、xyがあるとします。これを行うには、スクリプトがこれらの変数を読み書きするたびに、C++アクセサー関数を呼び出す必要があります。これらのアクセサー関数は、Integer::Newを使用してC++整数をJavaScript整数に変換し、Int32Valueを使用してJavaScript整数をC++整数に変換します。次に例を示します。

void XGetter(v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<Value>& info) {
info.GetReturnValue().Set(x);
}

void XSetter(v8::Local<v8::String> property, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
x = value->Int32Value();
}

// YGetter/YSetter are so similar they are omitted for brevity

v8::Local<v8::ObjectTemplate> global_templ = v8::ObjectTemplate::New(isolate);
global_templ->SetAccessor(v8::String::NewFromUtf8(isolate, "x"),
XGetter, XSetter);
global_templ->SetAccessor(v8::String::NewFromUtf8(isolate, "y"),
YGetter, YSetter);
v8::Persistent<v8::Context> context =
v8::Context::v8::New(isolate, nullptr, global_templ);

上記のコードのオブジェクトテンプレートは、コンテキストと同時に作成されることに注意してください.テンプレートは事前に作成し、任意の数のコンテキストに使用できます.

動的変数へのアクセス #

前の例では、変数は静的でグローバルでした。ブラウザのDOMツリーのように、操作されているデータが動的である場合はどうでしょうか。xyがC++クラスPointのオブジェクトフィールドであるとします。

class Point {
public:
Point(int x, int y) : x_(x), y_(y) { }
int x_, y_;
}

任意の数のC++ pointインスタンスをJavaScriptで使用できるようにするには、各C++ pointに1つのJavaScriptオブジェクトを作成し、JavaScriptオブジェクトとC++インスタンスの間に接続を作成する必要があります。これは、外部値と内部オブジェクトフィールドを使用して行われます。

最初に、pointラッパーオブジェクトのオブジェクトテンプレートを作成します。

v8::Local<v8::ObjectTemplate> point_templ = v8::ObjectTemplate::New(isolate);

各JavaScript pointオブジェクトは、内部フィールドを使用してラッパーとなるC++オブジェクトへの参照を保持します。これらのフィールドは、JavaScript内からはアクセスできず、C++コードからのみアクセスできるため、このように呼ばれています。オブジェクトは任意の数の内部フィールドを持つことができ、内部フィールドの数はオブジェクトテンプレートで次のように設定されます。

point_templ->SetInternalFieldCount(1);

ここでは、内部フィールドカウントが1に設定されているため、オブジェクトにはインデックスが0の内部フィールドが1つあり、C++オブジェクトを指しています。

テンプレートにxyのアクセサーを追加します。

point_templ->SetAccessor(v8::String::NewFromUtf8(isolate, "x"),
GetPointX, SetPointX);
point_templ->SetAccessor(v8::String::NewFromUtf8(isolate, "y"),
GetPointY, SetPointY);

次に、テンプレートの新しいインスタンスを作成し、内部フィールド0をポイントpの外部ラッパーに設定することにより、C++ポイントをラップします。

Point* p = ...;
v8::Local<v8::Object> obj = point_templ->NewInstance();
obj->SetInternalField(0, v8::External::New(isolate, p));

外部オブジェクトは、単にvoid*のラッパーです。外部オブジェクトは、内部フィールドに参照値を格納するためにのみ使用できます。JavaScriptオブジェクトはC++オブジェクトを直接参照できないため、外部値はJavaScriptからC++に移行するための「ブリッジ」として使用されます。その意味で、外部値は、ハンドルがC++にJavaScriptオブジェクトへの参照を作成させるため、ハンドルの反対です。

xgetおよびsetアクセサーの定義を以下に示します。yアクセサーの定義は、xyに置き換えられる点を除いて同じです。

void GetPointX(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
v8::Local<v8::Object> self = info.Holder();
v8::Local<v8::External> wrap =
v8::Local<v8::External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<Point*>(ptr)->x_;
info.GetReturnValue().Set(value);
}

void SetPointX(v8::Local<v8::String> property, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
v8::Local<v8::Object> self = info.Holder();
v8::Local<v8::External> wrap =
v8::Local<v8::External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<Point*>(ptr)->x_ = value->Int32Value();
}

アクセサーは、JavaScriptオブジェクトによってラップされたpointオブジェクトへの参照を抽出し、関連付けられたフィールドを読み書きします。このようにして、これらの汎用アクセサーは、任意の数のラップされたポイントオブジェクトで使用できます。

インターセプター #

スクリプトがオブジェクトプロパティにアクセスするたびにコールバックを指定することもできます。これらはインターセプターと呼ばれます。効率化のために、インターセプターには2つのタイプがあります。

V8ソースコードに付属のサンプルprocess.ccには、インターセプターの使用例が含まれています。次のコードスニペットでは、SetNamedPropertyHandlerMapGetおよびMapSetインターセプターを指定します。

v8::Local<v8::ObjectTemplate> result = v8::ObjectTemplate::New(isolate);
result->SetNamedPropertyHandler(MapGet, MapSet);

MapGetインターセプターを以下に示します。

void JsHttpRequestProcessor::MapGet(v8::Local<v8::String> name,
const v8::PropertyCallbackInfo<Value>& info) {
// Fetch the map wrapped by this object.
map<string, string> *obj = UnwrapMap(info.Holder());

// Convert the JavaScript string to a std::string.
string key = ObjectToString(name);

// Look up the value if it exists using the standard STL idiom.
map<string, string>::iterator iter = obj->find(key);

// If the key is not present return an empty handle as signal.
if (iter == obj->end()) return;

// Otherwise fetch the value and wrap it in a JavaScript string.
const string &value = (*iter).second;
info.GetReturnValue().Set(v8::String::NewFromUtf8(
value.c_str(), v8::String::kNormalString, value.length()));
}

アクセサーと同様に、指定されたコールバックは、プロパティにアクセスされるたびに呼び出されます。アクセサーとインターセプターの違いは、インターセプターはすべてのプロパティを処理するのに対し、アクセサーは1つの特定のプロパティに関連付けられていることです。

セキュリティモデル #

「同一オリジンポリシー」(Netscape Navigator 2.0で最初に導入された)は、ある「オリジン」からロードされたドキュメントまたはスクリプトが、別の「オリジン」のドキュメントのプロパティを取得または設定することを防ぎます。ここでオリジンという用語は、ドメイン名(例:www.example.com)、プロトコル(例:https)、およびポートの組み合わせとして定義されています。たとえば、www.example.com:81www.example.comと同じオリジンではありません。2つのWebページが同じオリジンを持つと見なされるためには、3つすべてが一致する必要があります。この保護がなければ、悪意のあるWebページが別のWebページの整合性を損なう可能性があります。

V8では、「オリジン」はコンテキストとして定義されています。呼び出し元のコンテキスト以外へのアクセスは、デフォルトでは許可されていません。呼び出し元のコンテキスト以外にアクセスするには、セキュリティトークンまたはセキュリティコールバックを使用する必要があります。セキュリティトークンは任意の値にすることができますが、通常はシンボル、つまり他の場所には存在しない正規の文字列です。コンテキストを設定するときに、SetSecurityTokenを使用してセキュリティトークンをオプションで指定できます。セキュリティトークンを指定しない場合、V8は作成しているコンテキストのトークンを自動的に生成します。

グローバル変数にアクセスしようとすると、V8セキュリティシステムは最初に、アクセスされているグローバルオブジェクトのセキュリティトークンと、グローバルオブジェクトにアクセスしようとしているコードのセキュリティトークンを照合します。トークンが一致すると、アクセスが許可されます。トークンが一致しない場合、V8はコールバックを実行して、アクセスを許可するかどうかを確認します。オブジェクトテンプレートのSetAccessCheckCallbacksメソッドを使用して、オブジェクトのセキュリティコールバックを設定することにより、オブジェクトへのアクセスを許可するかどうかを指定できます。その後、V8セキュリティシステムは、アクセスされているオブジェクトのセキュリティコールバックを取得し、それを呼び出して、別のコンテキストがアクセスを許可されているかどうかを尋ねることができます。このコールバックには、アクセスされているオブジェクト、アクセスされているプロパティの名前、アクセスの種類(たとえば、読み取り、書き込み、または削除)が渡され、アクセスを許可するかどうかが返されます。

このメカニズムはGoogle Chromeに実装されているため、セキュリティトークンが一致しない場合、特別なコールバックを使用して、次のものへのアクセスのみが許可されます。window.focus()window.blur()window.close()window.locationwindow.open()history.forward()history.back()、およびhistory.go()。.

例外 #

V8は、エラーが発生した場合に例外をスローします。たとえば、スクリプトまたは関数が存在しないプロパティを読み取ろうとした場合、または関数ではない関数が呼び出された場合などです。

操作が成功しなかった場合、V8は空のハンドルを返します。したがって、コードが実行を継続する前に、戻り値が空のハンドルではないことを確認することが重要です。Localクラスのパブリックメンバー関数IsEmpty()を使用して、空のハンドルを確認します。

TryCatchを使用して例外をキャッチできます。次に例を示します。

v8::TryCatch trycatch(isolate);
v8::Local<v8::Value> v = script->Run();
if (v.IsEmpty()) {
v8::Local<v8::Value> exception = trycatch.Exception();
v8::String::Utf8Value exception_str(exception);
printf("Exception: %s\n", *exception_str);
// ...
}

返される値が空のハンドルであり、TryCatch を使用していない場合、コードは処理を中断する必要があります。TryCatch を使用している場合は、例外がキャッチされ、コードは処理を続行できます。

継承 #

JavaScript は *クラスのない* オブジェクト指向言語であり、そのため、古典的な継承ではなくプロトタイプ継承を使用します。これは、C++ や Java のような従来のオブジェクト指向言語で訓練されたプログラマーにとっては、戸惑うかもしれません。

Java や C++ のようなクラスベースのオブジェクト指向言語は、クラスとインスタンスという 2 つの異なるエンティティの概念に基づいています。JavaScript はプロトタイプベースの言語であるため、この区別を設けません。単にオブジェクトがあります。JavaScript は、クラス階層の宣言をネイティブにサポートしていません。しかし、JavaScript のプロトタイプメカニズムは、オブジェクトのすべてのインスタンスにカスタムプロパティとメソッドを追加するプロセスを簡素化します。JavaScript では、オブジェクトにカスタムプロパティを追加できます。例えば

// Create an object named `bicycle`.
function bicycle() {}
// Create an instance of `bicycle` called `roadbike`.
var roadbike = new bicycle();
// Define a custom property, `wheels`, on `roadbike`.
roadbike.wheels = 2;

このように追加されたカスタムプロパティは、そのオブジェクトのインスタンスに対してのみ存在します。bicycle() の別のインスタンス、例えば mountainbike を作成した場合、wheels プロパティが明示的に追加されない限り、mountainbike.wheelsundefined を返します。

これがまさに必要な場合もありますが、オブジェクトのすべてのインスタンスにカスタムプロパティを追加すると便利な場合があります。結局のところ、すべての自転車には車輪があります。ここで、JavaScript のプロトタイプオブジェクトが非常に役立ちます。プロトタイプオブジェクトを使用するには、カスタムプロパティを追加する前に、オブジェクトのキーワード prototype を参照します。次のようにします。

// First, create the “bicycle” object
function bicycle() {}
// Assign the wheels property to the object’s prototype
bicycle.prototype.wheels = 2;

これで、bicycle() のすべてのインスタンスに、wheels プロパティが組み込まれるようになりました。

V8 では、テンプレートで同じアプローチが使用されます。各 FunctionTemplate には、関数のプロトタイプのテンプレートを提供する PrototypeTemplate メソッドがあります。PrototypeTemplate にプロパティを設定し、C++ 関数をそれらのプロパティに関連付けることができます。これらは、対応する FunctionTemplate のすべてのインスタンスに存在します。例えば

v8::Local<v8::FunctionTemplate> biketemplate = v8::FunctionTemplate::New(isolate);
biketemplate->PrototypeTemplate().Set(
v8::String::NewFromUtf8(isolate, "wheels"),
v8::FunctionTemplate::New(isolate, MyWheelsMethodCallback)->GetFunction()
);

これにより、biketemplate のすべてのインスタンスのプロトタイプチェーンに wheels メソッドが組み込まれるようになります。このメソッドが呼び出されると、C++ 関数 MyWheelsMethodCallback が呼び出されます。

V8 の FunctionTemplate クラスは、関数テンプレートを別の関数テンプレートから継承させたい場合に呼び出すことができるパブリックメンバー関数 Inherit() を提供します。次のようにします。

void Inherit(v8::Local<v8::FunctionTemplate> parent);