在V8源码中的核心概念中,总结了一下shell.cc这个demo的知识,源码中还有另外一个例子,有shell.cc中没有涉及到的知识,这边文章就来总结一下。
process.cc
This sample provides the code necessary to extend a hypothetical HTTP request processing application - which could be part of a web server, for example - so that it is scriptable. It takes a JavaScript script as an argument, which must provide a function called Process. The JavaScript Process function can be used to, for example, collect information such as how many hits each page served by the fictional web server gets.
process.cc 扩展了一个假象的http处理程序(这里处理程序可能是web server的一部分)。它使用JavaScript作为参数,这段JavaScript必须提供一个叫Process的函数。这个Process函数可以用来收集每个页面被请求的次数。
跑demo
我们看看这个demo如何跑起来。
-
首先设置一下启动参数。
- 运行
可以看到打印的结果。
由于代码比较长,下面分析一下主要代码。
分析代码
这里省略初始化v8的代码,因为上一篇文章已经过了一遍。
Handle<String> source = ReadFile(isolate, file);
if (source.IsEmpty()) {
fprintf(stderr, "Error reading '%s'.\n", file.c_str());
return 1;
}
JsHttpRequestProcessor processor(isolate, source);
map<string, string> output;
if (!processor.Initialize(&options, &output)) {
fprintf(stderr, "Error initializing processor.\n");
return 1;
}
if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))
return 1;
PrintMap(&output);
可以流程是:
- 读文件
- 新建JsHttpRequestProcessor 对象 processor
- 初始化processor=>processor.Initialize
- 处理程序ProcessEntries
- 打印PrintMap(&output);
C++调用JavaScript函数
上一篇文章我们过了如果用JavaScript调用C++代码,现在需要反过来,看看C++如何调用JavaScript代码。
// Execute the script and fetch the Process method.
bool JsHttpRequestProcessor::Initialize(map<string, string>* opts,
map<string, string>* output) {
// Create a handle scope to hold the temporary references.
HandleScope handle_scope(GetIsolate());
// Create a template for the global object where we set the
// built-in global functions.
Handle<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());
global->Set(String::NewFromUtf8(GetIsolate(), "log"),
FunctionTemplate::New(GetIsolate(), LogCallback));
// Each processor gets its own context so different processors don't
// affect each other. Context::New returns a persistent handle which
// is what we need for the reference to remain after we return from
// this method. That persistent handle has to be disposed in the
// destructor.
v8::Handle<v8::Context> context = Context::New(GetIsolate(), NULL, global);
context_.Reset(GetIsolate(), context);
// Enter the new context so all the following operations take place
// within it.
Context::Scope context_scope(context);
// Make the options mapping available within the context
if (!InstallMaps(opts, output))
return false;
// Compile and run the script
if (!ExecuteScript(script_))
return false;
// The script compiled and ran correctly. Now we fetch out the
// Process function from the global object.
Handle<String> process_name = String::NewFromUtf8(GetIsolate(), "Process");
Handle<Value> process_val = context->Global()->Get(process_name);
// If there is no Process function, or if it is not a function,
// bail out
if (!process_val->IsFunction()) return false;
// It is a function; cast it to a Function
Handle<Function> process_fun = Handle<Function>::Cast(process_val);
// Store the function in a Persistent handle, since we also want
// that to remain after this call returns
process_.Reset(GetIsolate(), process_fun);
// All done; all went well
return true;
}
- 获取JavaScript函数
Handle<Value> process_val = context->Global()->Get(process_name);
- 存贮函数handle:
process_.Reset(GetIsolate(), process_fun);
下面再看看如何调用上面保存的函数。
- 调用的入口在这里:
ProcessEntries(&processor, kSampleSize, kSampleRequests);
- 后两个参数是一个数组,就是模拟一些http请求。
const int kSampleSize = 6;
StringHttpRequest kSampleRequests[kSampleSize] = {
StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),
StringHttpRequest("/", "localhost", "google.net", "firefox"),
StringHttpRequest("/", "localhost", "google.org", "safari"),
StringHttpRequest("/", "localhost", "yahoo.com", "ie"),
StringHttpRequest("/", "localhost", "yahoo.com", "safari"),
StringHttpRequest("/", "localhost", "yahoo.com", "firefox")
};
- 处理的函数
bool JsHttpRequestProcessor::Process(HttpRequest* request) {
// Create a handle scope to keep the temporary object references.
HandleScope handle_scope(GetIsolate());
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(GetIsolate(), context_);
// Enter this processor's context so all the remaining operations
// take place there
Context::Scope context_scope(context);
// Wrap the C++ request object in a JavaScript wrapper
Handle<Object> request_obj = WrapRequest(request);
// Set up an exception handler before calling the Process function
TryCatch try_catch;
// Invoke the process function, giving the global object as 'this'
// and one argument, the request.
const int argc = 1;
Handle<Value> argv[argc] = { request_obj };
v8::Local<v8::Function> process =
v8::Local<v8::Function>::New(GetIsolate(), process_);
Handle<Value> result = process->Call(context->Global(), argc, argv);
if (result.IsEmpty()) {
String::Utf8Value error(try_catch.Exception());
Log(*error);
return false;
} else {
return true;
}
}
3.1 我们可以看到这段关键的调用代码
// Invoke the process function, giving the global object as 'this'
// and one argument, the request.
const int argc = 1;
Handle<Value> argv[argc] = { request_obj };
v8::Local<v8::Function> process =
v8::Local<v8::Function>::New(GetIsolate(), process_);
Handle<Value> result = process->Call(context->Global(), argc, argv);
- JavaScript代码的Process函数
function Initialize() { }
function Process(request) {
if (options.verbose) {
log("Processing " + request.host + request.path +" from " + request.referrer + "@" + request.userAgent);
if (!output[request.host]) {
output[request.host] = 1;
} else {
output[request.host]++
}
}
Initialize();
可以看到JavaScript中可以使用C++传过来的变量,也可以调用C++函数。
通过阅读这两个例子,和阅读官方文档,对何如嵌入v8引擎有了初步的了解,我们可以去阅读node的源码了,看看node是如何使用v8的。