V8 JavaScriptエンジン - オブジェクトの追加
Rev.2を表示中。最新版はこちら。
V8 JavaScriptエンジンの組み込み - オブジェクトの追加
V8 JavaScriptエンジン - 変数の追加の続き。今度は、グローバル領域へオブジェクト({}リテラルで定義するもの)を追加してみる。追加するオブジェクトは以下のとおり。
オブジェクト名:LunchTime
内容:時、分の二つの値を格納するオブジェクト。
プロパティ:
- hour
- min
オブジェクトを追加する場合は、専用のオブジェクトテンプレートを一つ作成(下記ソースではtime_templ)し、そこにプロパティ名とアクセサを登録する。そして、作成したオブジェクトテンプレートをglobal領域用のオブジェクトテンプレートにオブジェクト名(下記ソースではLunchTime)と関連づけて登録する。
各プロパティのアクセサ(set/get_hour(),set/get_min())では、GetInternalField(0)(*1)でオブジェクトに対応するTimeクラスのインスタンスを取り出し、そこからメンバ変数にアクセスする。
(*1)GetInternalField(0)は0番目の内部フィールドの値を取得するという意味。Index0の内部フィールドにはSetInternalField(0, External::New(p))でTimeクラスのインスタンスが事前登録されているため、GetInternalField(0)でインスタンスを取り出すことができる。
#include <v8.h>
#include <iostream>
#include <string>
using namespace v8;
static void read_file(std::istream& ifs, std::string &content)
{
content = "";
std::string t;
while (!ifs.eof()) {
getline(std::cin, t);
content += t + "\n";
}
}
// log()が呼ばれた時のコールバックルーチン
static Handle<Value> log_callback(const Arguments& args) {
if (args.Length() < 1)
return v8::Undefined();
HandleScope scope;
Handle<Value> arg = args[0];
String::Utf8Value value(arg);
std::cout << "log: " << *value << std::endl;
return v8::Undefined();
}
// 追加するオブジェクトに対応するclass
class Time {
public:
Time() : hour(0), min(0) {}
int hour;
int min;
};
// オブジェクトのプロパティのアクセサ
Handle<Value> get_hour(Local<String> property,
const AccessorInfo &info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<Time*>(ptr)->hour;
return Integer::New(value);
}
void set_hour(Local<String> property, Local<Value> value,
const AccessorInfo& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<Time*>(ptr)->hour = value->Int32Value();
}
Handle<Value> get_min(Local<String> property,
const AccessorInfo &info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<Time*>(ptr)->min;
return Integer::New(value);
}
void set_min(Local<String> property, Local<Value> value,
const AccessorInfo& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<Time*>(ptr)->min = value->Int32Value();
}
int main(int argc, char* argv[])
{
std::string js_str;
read_file(std::cin, js_str);
// Create a stack-allocated handle scope.
HandleScope handle_scope;
Handle<ObjectTemplate> global = ObjectTemplate::New();
// log()関数のコールバックを指定
global->Set(String::New("log"), FunctionTemplate::New(log_callback));
// Create a new context.
//Persistent<Context> context = Context::New();
Persistent<Context> context = Context::New(NULL, global);
// Enter the created context for compiling and
// running the hello world script.
Context::Scope context_scope(context);
// オブジェクトの定義
Handle<ObjectTemplate> time_templ = ObjectTemplate::New();
time_templ->SetInternalFieldCount(1);
time_templ->SetAccessor(String::New("hour"), get_hour, set_hour); // プロパティとアクセサ指定
time_templ->SetAccessor(String::New("min"), get_min, set_min);
// オブジェクトに対応するC++のインスタンスを作成して登録
Time* p = new Time;
p->hour = 12; p->min = 0; // 初期値
Local<Object> obj = time_templ->NewInstance();
obj->SetInternalField(0, External::New(p));
context->Global()->Set(String::New("LunchTime"), obj); // globalにLunchTimeオブジェクト登録
// Create a string containing the JavaScript source code.
Handle<String> source = String::New(js_str.c_str());
// Compile the source code.
Handle<Script> script = Script::Compile(source);
// Run the script to get the result.
Handle<Value> result = script->Run();
// Dispose the persistent context.
context.Dispose();
// Convert the result to an ASCII string and print it.
String::AsciiValue ascii(result);
printf("%s\n", *ascii);
return 0;
}
以下のtest4.jsを読み込んで実行してみる。
test4.js
function dump_obj(obj)
{
for (var field in obj) {
log(field + ":" + obj[field]);
}
}
log(typeof LunchTime);
dump_obj(LunchTime); // プロパティを列挙
LunchTime.hour = 11; // writeのテスト
LunchTime.min = 10;
dump_obj(LunchTime);
実行結果
# ./a.out < test4.js log: object <-- 型を確認 log: min:0 <-- プロパティが列挙できることを確認 log: hour:12 log: min:10 <-- プロパティの書き換えを確認 log: hour:11 undefined
for inでLunchTimeオブジェクトのプロパティが列挙できている。またプロパティの更新もできている。
