パート2.オブジェクトのテンプレート、破棄時の通知など。
パート1ここ: krovosos.habrahabr.ru/blog/72474
-V8オブジェクトテンプレート
Javascriptオブジェクトをゼロから作成したくない場合があります。 V8オブジェクトにすぐに機能を提供したい場合があります。 たとえば、オブジェクトにメソッドを持たせ、すぐにC ++関数のセットの一部に関連付けたり、C ++クラスを「ラップ」したりする必要があります。
また、このV8オブジェクト(V8の外部)にいくつかのポインターを関連付けることもできます。 たとえば、C ++クラスの関連インスタンスへのポインター。
これを行うには、オブジェクトのテンプレート(クラスObjectTemplate)を使用します。 テンプレートに基づいてV8オブジェクトを作成すると、すぐに特定の機能で開始します。 ランタイム期間の後半に作成するオブジェクトのタイプごとに、テンプレートを作成する(および永続的なV8ハンドルに保存する)必要があります。 後で、オブジェクトを作成するときに、このテンプレートを示します。
次のようにデータベース(SQLITEなど)をラップするC ++クラスがあるとします:
class dblite
{
bool open( const char * name);
void close();
bool execute( const char * sql);
int error_code();
};
* This source code was highlighted with Source Code Highlighter .
dbliteオブジェクトをラップするV8オブジェクトが必要だとします。 V8オブジェクトにopen()、close()、execute()メソッド、およびerrorCodeプロパティを提供するとします。
これを行うには、最初に補助コードを作成する必要があります。
まず、V8オブジェクトからC ++オブジェクトへの関連リンクを取得する必要があります。 テンプレートを作成するとき、保存するリンクの数を正確に指定し(これについては後で説明します)、V8オブジェクトは内部にvoid *要素の通常の配列を作成します。 次に、その要素に戻り、void *を選択して、必要なC ++クラスに変換します。 たとえば、C ++クラスdbliteへのポインターがV8オブジェクトの外部リンクの配列のゼロセルに格納されていることがわかっている場合、次のように取得できます。
dblite* unwrap_dblite(Handle<Object> obj)
{
Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
void * ptr = field->Value();
return static_cast<dblite*>(ptr);
}
* This source code was highlighted with Source Code Highlighter .
次に、C ++コードでコールバック関数を宣言する必要があります。 つまり、V8から呼び出されるC ++関数です。 今のところ、このような呼び出しの2つのタイプを検討してください。
V8からC ++へのコールバックにより、オブジェクトのプロパティの値を取得します。
Handle<Value> SomeProperty(Local< String > name, const AccessorInfo& info)
{
// info.Holder() V8 ( Handle<Object>)
// name ,
...
}
* This source code was highlighted with Source Code Highlighter .
V8オブジェクトのメソッドが呼び出されたときに発生するV8からC ++へのコールバック:
Handle<Value> SomeMethod( const Arguments& args)
{
// args.This() V8 ( Handle<Object>)
// args ( )
...
}
* This source code was highlighted with Source Code Highlighter .
たとえば、errorCodeプロパティの場合、コールバック関数は次のようになります。
Handle<Value> ErrorCode(Local< String > name, const AccessorInfo& info)
{
// Handle<Object>, dblite, error_code(),
// V8 ( ),
return Integer::New(unwrap_dblite(info.Holder())->error_code());
}
* This source code was highlighted with Source Code Highlighter .
また、open()メソッドの場合、コールバック関数は次のようになります。
Handle<Value> Open( const Arguments& args)
{
if (args.Length() < 1) return Undefined(); // ? !
dblite* db = unwrap_dblite(args.This()); // dblite
string sql = to_string(args[0]); // C++, to_string
return Boolean::New(db->open(sql.data())); // open()
}
* This source code was highlighted with Source Code Highlighter .
dblite :: open()およびdblite :: execute()メソッドと通信するために、すでに同様のClose()およびExecute()関数を作成できることを願っていますか?
Handle<Value> Close( const Arguments& args)
{
// !
}
* This source code was highlighted with Source Code Highlighter .
Handle<Value> Execute( const Arguments& args)
{
// !
}
* This source code was highlighted with Source Code Highlighter .
一般に、dbliteをラップするV8オブジェクトのテンプレートを作成する準備ができています。
Handle<ObjectTemplate> CreateDbLiteTemplate()
{
HandleScope handle_scope;
Local<ObjectTemplate> result = ObjectTemplate::New(); //
result->SetInternalFieldCount(1); // ( V8) (void*)
// dblite*
// ( ) - ?
result->SetAccessor( String ::NewSymbol( "errorCode" ), ErrorCode); //
// -
result->Set( String ::NewSymbol( "open" ), FunctionTemplate::New(Open));
result->Set( String ::NewSymbol( "close" ), FunctionTemplate::New(Close));
result->Set( String ::NewSymbol( "execute" ), FunctionTemplate::New(Execute));
// , HandleScope
// "" HandleScope - handle_scope
return handle_scope.Close(result);
}
* This source code was highlighted with Source Code Highlighter .
少し説明。 文字列:: NewSymbol()は名前のハッシュを使用し、既知の名前が使用されている場合、文字列:: New()より高速です。
オブジェクトテンプレート内の関数は、関数テンプレート(FunctionTemplate)を通じて登録されます。
これで、dbliteに基づいてV8オブジェクトを作成する関数を作成する準備ができましたか? そうでもない。 しかし、そのようなオブジェクトの寿命はどうですか? 永続化すると、破壊されることはありません。 一時-dbliteオブジェクトの関連インスタンスは破棄されないため、ガベージコレクターによって破棄され、メモリが「リーク」します。
だから、次の部分にスムーズに進んでください。
-V8オブジェクトを破棄するときに通知を受け取る
前半で述べたように、V8オブジェクトの有効期間は、使用カウンターに基づいてV8自体によって制御されます。
永続ハンドルがV8のオブジェクトを解放することは決してありません。 そして、一時的なものはV8自体によって破壊されますが、それについては何も知りません。
この状況から抜け出すために、V8は「疎接続」リンクの概念を使用します。 V8オブジェクトを「疎結合」としてマークし、コールバックの通知関数を渡すことができます。 ある時点で、ガベージコレクターは「疎結合」オブジェクトのみがV8オブジェクトを参照していることに気付き、渡された関数を呼び出します。
オブジェクトは、「疎結合」MakeWeak関数になります。
void Persistent<T>::MakeWeak( void * parameters, WeakReferenceCallback callback);
* This source code was highlighted with Source Code Highlighter .
このケースの「完全に弱く接続された」オブジェクトに関する通知コールバックの関数は次のようになります。
void DbLiteWeakCallback(Persistent<Value> object , void * parameter)
{
dblite* db = (dblite*) parameter; // parameters MakeWeak, dblite*
delete db; // C++
object .ClearWeak(); // "-",
object .Dispose(); // ,
}
* This source code was highlighted with Source Code Highlighter .
これで、dbliteをラップするV8オブジェクトを作成するコードを作成する準備ができました。
Persistent<ObjectTemplate> DbLite_template; //
Persistent<Object> CreateDbLite( const char * db_name)
{
Persistent<Object> obj;
// -
if (DbLite_template.IsEmpty()) DbLite_template = Persistent<ObjectTemplate>::New(CreateDbLiteTemplate());
// c++
dblite* db = new dblite; if (db_name) db->open(db_name);
// js
obj = Persistent<Object>::New(DbLite_template->NewInstance());
// "-", parameter dblite*
obj.MakeWeak(db, DbLiteWeakCallback);
// js c++
obj->SetInternalField(0, External::New(db));
return obj;
}
* This source code was highlighted with Source Code Highlighter .
すべて順調ですが、javascriptコードからCreateDblite関数にアクセスする方法は? コンテキストを作成する場合、適切なメソッドをそのテンプレートに追加する必要があります(はい、コンテキストにもテンプレートを設定できます)。