在C++中嵌入V8

来源:互联网 发布:蚁群算法详解 编辑:程序博客网 时间:2024/06/05 19:22

Handle和垃圾收集

handle在v8中,用于记录js对象在堆中的位置。v8的垃圾收集器在收集那些不可到达的内存时,会对堆进行整理。它会移动对象的位置,以达到优化内存的目的。

当v8的垃圾收集器移动js对象的位置时,它会同时更新handle的值,让handle能够指向对象新的位置。


v 8中有两种handle

本地handle (LocalHandle) 。 这种handle存在于堆栈之上,并且在析构函数被调用时释放js对象。它的生命周期取决于handle scope, handle scope通常在函数调用的开始位置声明。当handle scope 被删除,垃圾收集器将回收被这些handle指向的对象。

Local Handle通过类 Handle<SomeType>来管理。


注意:handle stack不同于c++的call stack。但是handle scope可以嵌入到c++的stack中。Handle scope只能是作为栈变量,不同通过new方法分配。


持久Handle (Persistent Handle)。持久Handle不再栈上分配,而且需要手动删除。同本地handle一样,持久handle也引用一个堆上分配的对象。当您希望在多个函数中调用一个对象,或者handle的生命周期超出c++的作用域的时候,就需要用持久handle了。

比如,google chrome就会使用持久handle保持一个DOM节点。创建一个支持hnandle,需要使用Persisten类的构造,删除则通过Persistent::Dispose。

通过Persistent::MakeWeak,持久handle可以被标记为 weak。

持久Handle使用 Persistent<SomeType>。


当你需要创建很多handle的时候,就需要用到handle scope。handle scope是一个handle容器。当handle scope被删除时,handle scope管理的所有handle都会被删除。


请看例子 使用C++调用V8 。下图介绍了例子中代码生成的handle对象。注意 Context::New()返回的是一个本地handle,我们为这个本地handle创建了一个持久handle来演示持久handle的用法。

当HandleScope::~HandleScope调用时,如果没有其他的引用,handle scope内的handle将在下次垃圾收集时被移除。

对象source_obj和script_obj将在他们不再被其他任何handle引用,也不会被javascript中的代码引用时被垃圾收集器回收。

但是context是一个持久handle,它将不会被回收,除非主动调用它的Dispose函数。


这里需要注意一个陷阱:你不能从一个声明了handle scope的函数中直接返回一个本地handle。这种情况下,在你返回local handle之前,handle scope就会将该handle删除。


正确的方式,是使用HandleScope::Close方法来返回。如下例:


/ /This function returns a new array with three elements, x, y, and z.  Handle<Array> NewPointArray(int x, int y, int z) {    v8::Isolate* isolate = v8::Isolate::GetCurrent();      // We will be creating temporary handles so we use a handle scope.    HandleScope handle_scope(isolate); //声明了HandleScope了      // Create a new empty array.    Handle<Array> array = Array::New(3);      // Return an empty result if there was an error creating the array.    if (array.IsEmpty())      return Handle<Array>();      // Fill out the values    array->Set(0, Integer::New(x));    array->Set(1, Integer::New(y));    array->Set(2, Integer::New(z));      // Return the value through Close.    return handle_scope.Close(array); //这样才能正确返回一个本地handle  }  



Contexts

在v8中, context是一个分离的、独立的javascript执行环境。当你运行一段js代码时,你必须明确给出一个context。


为什么这是必须的?因为javascript提供了一组内建的函数和对象,这些都可以被javascript代码改变。如果两个完全不相关的javascript代码更改了全局的对象,就会造成不可预知的结果。


在CPU时间和内存角度看,创建一个执行环节似乎是一件很耗费时间和资源的事情,但是,v8通过广泛的缓存策略,仅让你第一次创建context时花销会比较大,后面再创建任意多的context的花销就很小了。


当第一次创建时,v8必须创建内建对象,并解析内建js代码。而后续context只需要创建他们自己的内建对象。


如果v8设置上了snapshot特性(build选项 snapshot=yes,这是默认值),第一次所花费的开销将大幅被优化。


当你创建了一个context后,你可以进入退出它任意多次。当你在context A的时候,你可以进入一个不同的context,B。这时,context B将取代A成为你的当前Context。 当你退出context B的时候,A就自动成为你的当前context:


注意:不同context内的内建函数和对象是相互分离的。


templates

一个template是javascript函数的蓝图。你可以使用一个template来将c++函数和结构体包装到javascript对象中,让javascirpt脚本来使用它。例如,google chrome使用template来包装c++ DOM节点为js对象,并提供全局访问函数。

你也可以创建一组template让所有的context来共享使用。你可以创建任意多的template。但是每个template在context中只有一个实例。


在javascript中,function和object有很大的重叠。在java和c++中,我们通过定义一个类来创建一个新的类型,而在javascirpt中,我们通过创建一个新的function,然后用这个function作为构造函数来创建一个实例。javascript对象的内存组织方式和功能都非常接近于创建它的函数。这影响到了v8 template的工作方法。

有两种template:

function template。这是创建一个独立函数的模板。创建一个javascript的template实例,需要通过template的GetFunction方法。你也可以给这个function template关联一个回调函数。当javascript函数实例调用时,这个回调会被访问。


object template。每个function template都会有一个关联的object template。这是为关联创建该对象的函数。你可以为object template关联两个类型的回调:

  • accessor callbacks : 当脚本访问特定对象的属性时调用;
  • interceptor callbacks 当脚本访问任意对象属性时调用;

下面的代码战士了如何使用template为全局对象增加内建函数的方法

// Create a template for the global object and set the  // built-in global functions.  Handle<ObjectTemplate> global = ObjectTemplate::New();  global->Set(String::New("log"), FunctionTemplate::New(LogCallback));    // Each processor gets its own context so different processors  // do not affect each other.  Persistent<Context> context = Context::New(NULL, global); 

Accessors

一个accessor是一个c++的回调函数。当javascript脚本方位一个对象属性时,它被调用并返回一个值。

accessor通过object template来设置。使用SetAccessor方法。该方法需要提供一个属性名和关联的两个回调(读写)作为参数。


访问全局静态变量

假设有两个C++整型变量,x和y,被javascript当做全局变量来使用。要实现它,javascript需要调用C++的accessor函数。这些accessor函数通过Integer::New将C++整型转换为javascript整数;通过Int32Value将javascript整数转为c++整数。例子如下:

Handle<Value> XGetter(Local<String> property,                         const AccessorInfo& info) {    return Integer::New(x);  }      void XSetter(Local<String> property, Local<Value> value,               const AccessorInfo& info) {    x = value->Int32Value();  }         // YGetter/YSetter are so similar they are omitted for brevity    Handle<ObjectTemplate> global_templ = ObjectTemplate::New();  global_templ->SetAccessor(String::New("x"), XGetter, XSetter);  global_templ->SetAccessor(String::New("y"), YGetter, YSetter);  Persistent<Context> context = Context::New(NULL, global_templ);  
 plaincopy


注意:object template和context是同时创建的。object template可以被一次创建并被多个context使用。


访问动态变量

假设x和y是C++类Point的成员变量:

class Point {     public:      Point(int x, int y) : x_(x), y_(y) { }      int x_, y_;    }  

如果让C++的point实例可以被javascript使用,我们需要为每个C++ point创建一个javascript对象,并且在javascript对象和c++对象之间建立关联。


第一步,创建针对Point对象的object template

Handle<ObjectTemplate> point_templ = ObjectTemplate::New();  

javascript的point对象通过内部field来引用一个c++对象。 这个内部对象只能通过C++代码访问,不能通过javascript访问。 一个对象可以拥有任意多的内部field,它的数量通过如下函数设置:

point_templ->SetInternalFieldCount(1);  

这里,我们设置了一个长度为1的内部field,它的索引从0开始。


然后我们可以添加x和y的访问子:

point_templ.SetAccessor(String::New("x"), GetPointX, SetPointX);  point_templ.SetAccessor(String::New("y"), GetPointY, SetPointY);  

下面,要将一个C++的point实例和javascript的代码关联。我们通过External对象来作为中介。因为javascript不知道如何处理一个c++对象。

Point* p = ...;  Local<Object> obj = point_templ->NewInstance();  obj->SetInternalField(0, External::New(p));  

External实际上是对一个void* 只针的封装。


下面,我们就可以实现对x和y的get和set访问了:

Handle<Value> GetPointX(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<Point*>(ptr)->x_;    return Integer::New(value);  }    void SetPointX(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<Point*>(ptr)->x_ = value->Int32Value();  }  


Interceptors

有两种Interceptor,

  • 命名属性interceptor - 通过属性的字符串名来访问,如: document.theFormName.elementName
  • 索引属性interceptor - 通过索引访问的属性,如 document.forms.elements[0];


V8源代码中的示例process.cc展示了一个使用interceptors的例子。下面是SetNamedPropertyHandler中关于MapGet和MapSet的interceptor部分代码


Handle<ObjectTemplate> result = ObjectTemplate::New();  result->SetNamedPropertyHandler(MapGet, MapSet);  

MapGet的interceptor如下

Handle<Value> JsHttpRequestProcessor::MapGet(Local<String> name,                                               const AccessorInfo &info) {    // Fetch the map wrapped by this object.    map<string, string> *obj = UnwrapMap(info.Holder());      // Convert the JavaScript string to a std::string.    string key = ObjectToString(name);      // Look up the value if it exists using the standard STL idiom.    map<string, string>::iterator iter = obj->find(key);      // If the key is not present return an empty handle as signal.    if (iter == obj->end()) return Handle<Value>();      // Otherwise fetch the value and wrap it in a JavaScript string.    const string &value = (*iter).second;    return String::New(value.c_str(), value.length());  }  




安全模型

”同源“策略阻止不同源内的数据相互访问。


v8通过一个context定义一个”源“。默认情况下,跨context访问是不允许的。为了访问其他context,你需要一个安全token或者安全回调。

安全token可以是任意值,但是通常会使一个符号,一个全局唯一的字符串。你也可以通过SetSecurityToken来得到一个特殊的安全token。如果你不指定的话,v8会在你创建context时自动生成一个。


当一个对全局变量访问产生时,v8的安全系统首先检测全局对象的安全token和试图访问它的代码的安全token是否一致,如果token一致,那么访问将被授权。

如果token不一致,那么v8将通过一个回调来判断是否允许访问。

你可以通过SetAccessCheckCallbacks方法来设置这样一个回调。



异常

当有错误发生时,v8将抛出异常。如,当脚本或者函数试图访问一个不存在的属性时,或者对一个非函数进行调用时。


当操作不成功时,v8会返回一个空的handle。你的代码需要检查v8的返回值是否为空。这一点很重要。 通过函数IsEmpty()

你可以通过TryCatch来捕捉异常,如

TryCatch trycatch;  Handle<Value> v = script->Run();  if (v.IsEmpty()) {      Handle<Value> exception = trycatch.Exception();    String::AsciiValue exception_str(exception);    printf("Exception: %s\n", *exception_str);    // ...  }  

 view plaincop

如果value返回为空,而且你没有一个TryCatch对象,那么你的代码会跳出。如果有TryCatch,异常就会被捕捉,你的代码会继续进行。


继承

javascript是一种无类别的面向对象语言,它使用原型继承来代替类继承。这让习惯传统面向对象语言的程序员困惑。


基于类的面对对象语言,如java, C++,是建筑在对类和对象严格区分的基础上的。javascript是一种基于原型的语言,因而没有这么严格的区分,它只有简单的对象。

javascript不能直接支持类继承,但是javacript的原型机制可以很简单的的实现自定义属性和方法的添加。在javascript中,你可以自定义对象的属性,如


// Create an object "bicycle"   function bicycle(){   }   // Create an instance of bicycle called roadbike  var roadbike = new bicycle()  // Define a custom property, wheels, on roadbike   roadbike.wheels = 2  

这个方法添加到了已经存在的对象实例上。

加入我创建另外一个bycycle的实例,如mountainbike,那么mountainbike.wheels将返回undefined,除非你用同样的方法添加它。


有时,很需要这种需求。希望为每个bycycle对象的实例头添加相同的书写。这是javascript原型对象有用的地方。使用原型对象,通过关键字prototype


// First, create the "bicycle" object  function bicycle(){   }  // Assign the wheels property to the object's prototype  bicycle.prototype.wheels = 2  


这样所有的bicycle()实例都有了wheels属性。

同样的场景,在V8中通过template实现。每个FunctionTemplate都有一个PrototypeTemplate方法,可以设置function的原型。你可以设置属性、绑定c++函数等,这些都通过PrototypeTemplate作用到所有的实例上。如

Handle<FunctionTemplate> biketemplate = FunctionTemplate::New();  biketemplate->PrototypeTemplate().Set(      String::New("wheels"),      FunctionTemplate::New(MyWheelsMethodCallback)->GetFunction();  )  


这将使所有的biketemplate 实例都有wheels方法。当实例的wheels被调用时,C++函数MyWheelsMethodCallback就会被调用。


V8的FunctionTemplate类还提供了一个公共方法Inhert(),你可以通过它来继承任意的FunctionTemplate,如

void Inherit(Handle<FunctionTemplate> parent);  


原帖地址: http://blog.csdn.net/doon/article/details/12971579
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 在外地上学学籍怎么办 偷渡后国内资产怎么办 手机系统太低怎么办 拍古装是短发怎么办 吞下一个李子核怎么办? 宝宝吞了荔枝核怎么办 美吉姆培训不过怎么办 1岁宝宝挑食怎么办 宝宝不愿意开口说话怎么办 自闭症孩子不爱学习怎么办 宝宝不独立走路怎么办 六个月宝宝不认人怎么办 小孩隔奶奶涨怎么办 小孩段奶奶涨怎么办 1岁半还不会说话怎么办 孩子嗓子哑了怎么办 小朋友嗓子哑了怎么办 4周岁宝宝拉肚子怎么办 小孩不肯拉小便怎么办 做销售不爱说话怎么办 我伤害了朋友怎么办 三岁发音不准怎么办 心里憋不住话怎么办 自己不长记性怎么办 孩子不愿意开口说话怎么办 孩子不爱开口说话怎么办 宝宝犟脾气不好怎么办 小孩说话不算话怎么办 孩子说话不算话怎么办 孩子故意不好好说话怎么办 小孩说话吐字不清楚怎么办 腿老是抽筋是怎么办 半夜睡觉脚抽筋怎么办 我不爱说话内向怎么办 小孩子吐字不清怎么办 宝宝前边头发少怎么办 宝宝咬嘴唇龅牙怎么办 小孩老是咬下唇怎么办 五月小孩掉下床怎么办 小孩说话夹舌头怎么办 小孩自闭不说话怎么办