使用v8与js做交互,理解node基本原理
April 28, 2015
前阶段刚说到全栈的问题,到底要多深,多后期才能算是全栈每个人心中都有自己的看法,我自己感觉多学一些总不是坏事。
最近用 node 遇到了一些障碍,在看 node 的源码了解一下,然后想到网上现在还没有相关的分析,大多数都是开发 addons,而且中途 v8 进行了一次大升级,很多能查到的资料都是错得,所以就在这里简单地写一些自己的经验吧。
众所周知,node 是基于 v8 做的,v8 是什么?简单来说就是一个 javascript 的解析器,他会读取 js 程序,使用 jit 技术实时编译我们写的程序,最后运行的是机器码,所以 v8 运行的 js 特别快。
安装依赖工具
首先我们需要进行环境的配置,首先去 v8 主页https://code.google.com/p/v8/,在wiki中找到BuildingWithGYP这一项,然后发现被移动到另一个页面,我们点击过去
我们发现这个页面提到了一个新的工具 gclient,v8 项目就是用这个管理的,所以我们需要下载它,现在去他的主页https://code.google.com/p/gclient/,我们发现他已经成为 depot_tools 的一个子项目[如下图],然后我们点击链接跳转过去
由于我是用的 mac,所以直接使用 mac 的安装方式
Installing on Linux and Mac
- Confirm
git
is installed. git 2.2.1+ recommended. - Fetch depot_tools:
$ git clone
chromium/tools/depot_tools.git - Git at Google - Add
depot_tools
to your PATH:$ export PATH=`pwd`/depot_tools:"$PATH"
- Yes, you want to put depot_tools ahead of everything else, otherwise gcl will refer to the GNU Common Lisp compiler.
- You may want to add this to your
.bashrc
file or your shell’s equivalent so that you don’t need to reset your $PATH manually each time you open a new shell.
这里建议提前安装 xcode,这样的话相关的工具都会安装好,不需要自己在配置。
安装 v8
安装使用两个命令
gclient config https://chromium.googlesource.com/v8/v8.git
gclient sync
剩下 gclient 就会全自动的帮我们完成之后的操作,看网速,我大概等了将近 2 个小时
生成 xcode 工程
进入到我们刚才下号的 v8 目录,输入下面的命令
build/gyp_v8 -Dtarget_arch=x64
然后就可以使用 xcode 打开/build 目录下面的 all.xcodeproj
制作简单地 log 函数
我在这里就举一个简单地例子,让大家熟悉 node 的原理,在 xcode 中找到 sample 项目,右键 process 复制一个 target,我们重命名这个复制好的 target 叫 helloV8,然后再 source 这个 group 里面创建一个 cpp 文件,随意起名字,我这里叫做 helloV8.cpp
将下面的代码复制一份到新创建好的 helloV8.cpp 中
#include "include/v8.h"
#include "include/libplatform/libplatform.h"
#include <iostream>
#include <fstream>
#include <sstream>
using namespace v8;
void printjs(const FunctionCallbackInfo<Value>& args) {
v8::String::Utf8Value v8Str(args[0]);
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
std::cout << *v8Str << std::endl;
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "from yeanzhi"));
}
void strLength(const FunctionCallbackInfo<Value>& args) {
v8::String::Utf8Value v8Str(args[0]);
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
int length = strlen(*v8Str);
args.GetReturnValue().Set(Integer::New(isolate, length));
}
void loadjs(const FunctionCallbackInfo<Value>& args) {
v8::String::Utf8Value v8Str(args[0]);
std::ifstream f(*v8Str);
std::stringbuf buf;
f >> buf;
Local<Value> result = Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(), buf.str().c_str()))->Run();
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("\n%s\n", *utf8);
}
int main(int argc, char* argv[]) {
// Initialize V8.
V8::InitializeICU();
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
// Create a new Isolate and make it the current one.
Isolate* isolate = Isolate::New();
{
Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);
auto global = v8::ObjectTemplate::New(isolate);
global->Set(v8::String::NewFromUtf8(isolate, "printjs"), FunctionTemplate::New(isolate, &printjs));
global->Set(v8::String::NewFromUtf8(isolate, "loadjs"), FunctionTemplate::New(isolate, &loadjs));
global->Set(v8::String::NewFromUtf8(isolate, "strLength"), FunctionTemplate::New(isolate, &strLength));
// Create a new context.
Local<Context> context = Context::New(isolate, NULL, global);
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Local<String> source = String::NewFromUtf8(isolate, "loadjs('app.js')");
// Compile the source code.
Local<Script> script = Script::Compile(source);
// Run the script to get the result.
Local<Value> result = script->Run();
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("\n%s\n", *utf8);
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
return 0;
}
这端代码最核心的功能就是下面这段
auto global = v8::ObjectTemplate::New(isolate);
global->Set(v8::String::NewFromUtf8(isolate, "printjs"), v8::FunctionTemplate::New(isolate, &printjs));
global->Set(v8::String::NewFromUtf8(isolate, "loadjs"), v8::FunctionTemplate::New(isolate, &loadjs));
global->Set(v8::String::NewFromUtf8(isolate, "strLength"), v8::FunctionTemplate::New(isolate, &strLength));
// Create a new context.
Local<Context> context = Context::New(isolate, nullptr, global);
众所周知,js 有一个全局空间的概念,这里我们创建了三个函数 printjs,loadjs,strLength,并将他们绑定到全局空间中,这样我在创建好的 app.js 中就可以直接使用这三个函数
然后看这段代码
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Local<String> source = String::NewFromUtf8(isolate, "loadjs('app.js')");
// Compile the source code.
Local<Script> script = Script::Compile(source);
// Run the script to get the result.
Local<Value> result = script->Run();
这里我们做了两件事,一个是通过 loadjs 加载了 app.js 这个文件,然后编译这段代码,去执行他,下面是 app.js 的源码
printjs("hello world")
function hello() {
printjs("from yeanzhi")
}
hello()
var patt1 = new RegExp("e")
var myDate = new Date()
var arr = []
arr.push("2fjdsaf")
var i = 5,
j = 1
var res = i + j
printjs(res)
if (i == 5) {
printjs(true)
} else {
printjs(false)
}
while (i < 10) {
printjs("yeanzhi is supermen")
i++
printjs(i)
}
printjs("==============>>>>>>>>>>>>>")
printjs(strLength("yeanzhi is supermen"))
printjs(arr[0])
printjs(myDate.getTime())
loadJS
loadjs 中做了几件事情,首先拿到需要执行文件的内容,然后编译,执行,输出结果,下面是源码
void loadjs(const FunctionCallbackInfo<Value>&args){
v8::String::Utf8Value v8Str(args[0]);
std::ifstream f(*v8Str);
std::stringbuf buf;
f>>&buf;
Local<Value> result = Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(), buf.str().c_str()))->Run();
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("\n%s\n", *utf8);
}
运行
运行 helloV8 这个 target,会发现在 js 文件已经成功的执行
总结,v8 就是一个解析器,通过加载 js 文件的内容,在 v8 中编译后就可以执行 js 文件。node 就是基于这个做的,我们会在 node 的 Projects 里面发现 v8 的项目,和我们刚才下载的是完全一致的。
v8 在 node 0.10~0.11 这段时间中进行了一次惨无人道的升级,波及相当多的 api,导致网上能搜索到得大部分内容都不再适用了,如果想了解更多 v8 地内容,可以去这里获得https://developers.google.com/v8,如果以后有时间,我会在写一些 nodejs 的源码分析。
Written by xi ming You should follow him on Github