V8 JavaScriptエンジンの組み込み
概要
V8 JavaScriptエンジンの組み込みと関数等の追加を試した。
V8の組み込み
まずは標準入力からJavaScriptソースを読み込んで実行するケース。
https://developers.google.com/v8/get_startedに載っているものとほぼ同じで、ソースを標準入力から取得するように修正しただけ。このソースをベースに試していく。
#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"; } } 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; // Create a new context. Persistent<Context> context = Context::New(); // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // 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; }
Build手順は省略。以下のtest.jsを読み込ませて実行してみると。
test.js
(function() { return "test test"; })();
実行結果
# ./a.out < test.js test test
関数のリターン値"test test"が表示される。これで、いろいろ試す準備が整った。
関数の追加
まず最初に関数を追加してみる。追加する関数はfunction log(str)で、引数に指定した文字列を標準出力に出力するログ出力関数。
関数を新たに作るにはテンプレート(C++のテンプレートとは別のもの)を使う。テンプレートには関数テンプレートとオブジェクトテンプレートがあるが、関数を作る場合は、関数テンプレートを使って関数実行時のコールバックルーチンを登録し、さらにオブジェクトテンプレートで関数名と関数テンプレートを関連付ける。
#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(); } 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(NULL, global); // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // 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; }
上記ソースではオブジェクトテンプレートに"log"という名前でlog_callback()関数を登録しているので、JavaScriptからlog()を実行するとlog_callback()が実行されることになる。log_callback()では、引数に渡された内容を標準出力に出力する。
以下のtest2.jsを読み込ませて実行してみると。
test2.js
log("test test");
実行結果
# ./a.out < test2.js log: test test <-- log()の実行結果 undefined <-- これはlog()がundefinedを返しているため。
log()関数の実行が確認できた。
次は、グローバル空間への変数の追加を試す。
つづく。