v8SetNamedPropertyHandler

v8学习笔记

标签: javascriptJavaScriptJavascriptjavaScriptJAVASCRIPTv8V8
  789人阅读  评论(0)  收藏  举报
  分类:

目录(?)[+]

参考V8的英文文档,前面基本上是翻译, 后面是C++和Javascript的调用方式示例

1.     Handle

l  handle可以理解为指针,所有V8的对象都必须通过Handle来访问,因为V8垃圾收集机制决定了;

l  handle都是被v8的垃圾收集器来管理的;

l  handle在传递时都采用传值模式

 

Handle有两种类型:

1.        Local Handle继承至Handle:代表局部变量, 它的生命周期有HandleScope来决定,通常都是在调用的函数栈上, HandleScope只能在c++的堆栈上分配,不能用new来分配,HandleScope析构时Local对象同时被释放

2.        Persistent Handle继承至Handle:它的生命周期是全局的,释放的时候需要手工调用。

    Persistent::New 用于分配对象

    Persistent::Dispose 用于释放对象

V8会定期收集垃圾(无效的Handle),垃圾收集的机制是V8性能的关键(分代模式)

 

2.     Scope

Scope可以理解为Handle的容器,可以包含很多很多的Handle。

Scope可以分:HandleScope、Context::Scope

1.        栈分配器,管理所有的Local Handle对象

2.        HandleScope是构建在c++栈上面的

3.        它对localhandle的管理范围是在它的生存周期内,直到又创建了一个新的HandleScope

4.        当新分配一个HandleScope之后,新的Local Handle将在新的HandleScope内分配,直到这个HandleScope析构

5.        HandleScope析构以后, 它所管理的Local Handle都将被释放,对于js而言,这个时候访问的就是undefined

6.        Context::Scope仅仅管理Context,在构造中Context::Enter, 在析构中Context::Leave

3.     Isolate

isolate代表了一个独立的v8引擎,简单说它是完全独立的,一个isolate中的对象不能被另外一个isolate使用。可以让每个线程拥有一个不同的isolate,来并行执行不同的JavaScript。对于isolate来说,任何时刻最多只能有一个现在对它访问,因此多线程并在执行同一个isolate的时候,需要有锁机制。

4.     Context

Context表示执行环境,JavaScript代码可以运行在不同的执行环境里面,而且必须显示的指定运行在哪个执行环境。

a)        之所有Context,是因为JavaScript有内建的对象,需要隔离不同Context的内建对象

b)        第一次构建Context对象消耗比较大,后面就比较快了(snapshot)

c)        可以从一个Context快速切换到另外一个Context

d)        在C++代码中可以通过Context::GetCurrent()获取当前的Context

5.     Template

Template代表了C++中JavaScript中的函数和对象,可以有很多的templates类型,在一个Context中只能有一个具体的Template的实例,有两种类型的template:

1.        Function template  

2.        Object template

ObjectTemplate用于创建javascript中的object,添加到ObjectTemplate中的属性会添加到用ObjectTemplate创建出来的所有Javascript的object中。

1.        每个Function template关联着一个Object template

        Handle<ObjectTemplate> global = ObjectTemplate::New();
                    global->Set(String::New("log"), FunctionTemplate::New(LogCallback));

2.        可以将自己定义的Object作为内嵌类型,以供其他js使用

Persistent<Context> context = Context::New(NULL, global);

6.     Accessors

当Object的对象被js访问时被调用,会触发C++的回调函数,有两种类型的Accessor

1.        Static Global变量

所谓静态全局变量指的是JavaScript中全局的变量。

  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);

 

2.        动态变量

表示类中的成员变量,比如:

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

需要建立一个JavaScript Object和C++对象关联起来(比如很多point对象,如何在JavaScript中管理?)。每个JavaScript对象都需要保存一个索引(成员变量)指向C++对象,但是这个变量在JavaScript中是访问不到,只能在C++中可以访问,可以理解为内部字段。

  Handle<ObjectTemplate> point_templ = ObjectTemplate::New();
  point_templ->SetInternalFieldCount(1);

这里内部字段的个数设置为1,表示当前对象有一个内部字段(索引为0),指向了C++对象。一个对象可以有多个内部字段。

增加x&y的Accessors:

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

包装一个C++ 对象,并和JavaScript 的ObjectTemplate关联:

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

通过NewInstance构建一个实例,然后通过SetInternalField把C++对象和JavaScript对象关联起来。

External 对象只能用于从内部字段中恢复索引,相当于JavaScript对象到C++对象的一个桥接,上面例子中:Local<External> wrap就代表了JavaScript对象。

7.     Intercetors

能够指定JavaScript访问对象属性时的一个callback。为了效率的原因,有两种不同的interceptor:

命名属性interceptor:通过属性的名称来访问

索引属性interceptor:通过属性的索引来访问

Handle<ObjectTemplate> result = ObjectTemplate::New();
result->SetNamedPropertyHandler(MapGet, MapSet);
 
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());
}
 

3.        accessor和interceptor的区别是, 前者处理某个特性的属性,后者处理所有的属性

Security Model:V8有一套安全检测机制,不从你创建的context环境去访问这个context,缺省是不允许的,如果希望能够访问,就需要使用Security token或者Security callback机制。Security token可以是唯一的字符串。当构建context的时候,可以通过SetSecurityToken 来设定token,如果你没有设定,V8会自己生成一个。或者通过回调SetAccessCheckCallbacks 来询问另外一个context是否允许访问。

8.     Exceptions

JavaScript会抛出异常,在C++中也捕捉到。

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);
    // ...
  }

9.     Inheritance

JavaScript采用原型(prototype)来实现继承方式的。在V8中每个FunctionTemplate 有一个PrototypeTemplate方法,这个方法给template设定了一个function'sprototype,PrototypeTemplate 能够设置相应的函数模板:

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

V8的FunctionTemplate提供了继承函数(Inherit),你能够从其他的functiontemplate里面继承过来。

10.             v8的编译(vs编译)

1.        下载cygwin (必须用chromium上面的版本

svn co ttp://src.chromium.org/svn/trunk/deps/third_party/cygwin@66844 third_party/cygwin

2.        下载Python

svn co ttp://src.chromium.org/svn/trunk/deps/third_party/cygwin@66844 third_party/cygwin

3.        注意:cygwin&python都必须放在v8的源码根目录下third_party下面

4.        在cygwin环境下(自己建立Cygwin.bat,修改里面的脚本路径),进入v8的源码目录:

/cygdrive/d/data/v8/third_party/python_26/python.exe /cygdrive/d/data/v8/build/gyp_v8 -Dtarget_arch=x64

5.        在v8源码目录下, build目录会生成all.sln

6.        用vs打开并编译

11.             C++和JavaScript相互调用

10.1.     Global的使用方式

可以创建Global对象模版,设置到Context中:

     HandleScope handle_scope;

     //注册全局的函数

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

     global_temp->Set(String::New("print"), FunctionTemplate::New(v8helper::Print));

     Persistent<Context>context = Context::New(NULLglobal_temp);

     Context::Scope context_scope(context);

 

从Context获取Global对象,再设置属性:

     HandleScope handle_scope;

     Persistent<Context>context = Context::New();

     Context::Scope context_scope(context);

     Local<Object>global_temp = Context::GetCurrent()->Global(); global_temp->Set(String::New("print"),

         FunctionTemplate::New(v8helper::Print)->GetFunction());

 

10.2.     JavaScript的全局变量和C++的变量关联(普通类型)

在JavaScript中有一个全局变量,它的相关修改都和c++中某个全局变量保持一致,这里变量是简单类型。

//定义全局C++变量

int32_t x = 0;

//定义该变量的访问器

Handle<ValueXGetter(Local<String>propertyconstAccessorInfoinfo)

{    return Integer::New(x);

}

void XSetter(Local<Stringproperty,Local<Value>valueconstAccessorInfoinfo)

{

     x = value->Int32Value();

}

 

//生成对象模版

Handle<ObjectTemplateglobal_templObjectTemplate::New();

//设置对象的访问器

global_templ->SetAccessor(String::New("x"),XGetterXSetter);

 

//TODO: 将global_templ设置到context里面就可以在JavaScript中用x来访问了, 在JavaScript中对变量x的访问, 响应的C++函数XGetter/XSetter会被调用

 

10.3.     从JavaScript中获取C++对象

template<class T>

     static TUnwrap(Handle<Objectobj,int index=0){

         Handle<External>field = Handle<External>::Cast(obj->GetInternalField(index));

         voidptr = field->Value();

         return static_cast<T*>(ptr);

     }

内部对象的含义和使用后面会介绍。

10.4.     JavaScript的全局变量和C++的变量关联(对象)

JavaScript中有一个全局对象,它的修改和C++中的全局对象对应,包括可以操控C++对象中的成员函数,属性等

 

class Obj

{

public:

     Obj(int x) : x_(x) { }

 

public:

 

     static v8::Handle<v8::ValuesetX(const v8::Arguments & args)

     {

         printf("Obj::setX\n");

         Obj *o = v8helper::Unwrap<Obj>(args.Holder());

 

         if(args.Length() > 0)

         {

              o->x_ = args[0]->Int32Value();

         }

        

         return Integer::New(o->x_);

     }

 

     static v8::Handle<v8::ValuegetX(const v8::Arguments & args)

     {

         printf("Obj::getX\n");

         Obj *o = v8helper::Unwrap<Obj>(args.Holder());

 

         return Integer::New(o->x_);

     }

 

     static Handle<ValuegetPropertyX(Local<String>propertyconstAccessorInfoinfo)

     {

         printf("Obj::getPropertyX\n");

         return Integer::New(v8helper::Unwrap<Obj>(info.Holder())->x_);

     }

 

     static void setPropertyX(Local<Stringproperty,Local<Value>valueconstAccessorInfoinfo)

     {

         printf("Obj::setPropertyX\n");

         v8helper::Unwrap<Obj>(info.Holder())->x_value->Int32Value();

     }

 

public:

     int x_;

};

 

Obj*g_o = new Obj(10);

 

void test_object(int argccharargv[])

{

     HandleScope handle_scope;

     //注册全局的函数

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

     global_temp->Set(String::New("print"), FunctionTemplate::New(v8helper::Print));

 

     Persistent<Context>context = Context::New(NULLglobal_temp);

    

     Context::Scope context_scope(context);

 

     //准备JavaScript中的对象

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

     object_temp->SetInternalFieldCount(1);

    

     //创建JavaScript对象

     Local<Object>obj = object_temp->NewInstance();

     //关联到c++对象

     obj->SetInternalField(0,External::New(g_o));

     //关联到C++对象的成员函数

     obj->Set(String::New("setX"), FunctionTemplate::New(Obj::setX)->GetFunction());

     obj->Set(String::New("getX"), FunctionTemplate::New(Obj::getX)->GetFunction());

     //关联到C++对象的属性

     obj->SetAccessor(String::New("x"), Obj::getPropertyXObj::setPropertyX);

 

     //将JavaScript对象放入到执行环境中去

     context->Global()->Set(String::New("obj"),obj, (PropertyAttribute)(v8::ReadOnly));

 

     //在JavaScript中运行

     Handle<Script>script =Script::Compile(String::New("obj.setX(15);print(obj.getX());obj.x=10;print(obj.getX())"));

 

     Handle<Value>result = script->Run();

 

     context.Dispose();

}

 

注意:这种方式下C++暴露给JavaScript的对象是全局的,在JavaScript中可以直接使用,不需要先new一个出来(用new出来的方式下面再介绍),其实也建议不要用new这种方式容易导致对象被提前释放了。

 

10.5.     C++中调用JavaScript中的函数,并传入参数

 

{

         HandleScope handle_scope;

 

         //在JavaScript中运行

         Handle<Script>script = Script::Compile(String::New("functionprocess(request){print(obj.x)}"));

 

         Handle<Value>result = script->Run();

 

         //获取JavaScript中的函数

         Handle<String>process_name = String::New("process");

         Handle<Value>process_val = context->Global()->Get(process_name);

 

         //如果不是函数直接返回

         if (!process_val->IsFunction()) return;

 

         //转换成函数

         Handle<Function>process_fun = Handle<Function>::Cast(process_val);

 

         //如果process要调用多次,可以用Persistent类型, 自己维护它的范围

         Persistent<Function>process = Persistent<Function>::New(process_fun);

         {

              TryCatch try_catch;

 

              //调用函数,并传入参数

              const int argc = 1;

              Handle<Value>argv[argc]= { obj };

              Handle<Value>result = process->Call(context->Global(), argc,argv);

              if (result.IsEmpty()) {

                   String::Utf8Valueerror(try_catch.Exception());

                   //todo

              }

              else

              {

                   //todo

              }

         }

         process.Dispose();

}

 

10.6.     关于ObjectTemplate的一个问题

 

     //准备JavaScript中的对象

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

     object_temp->SetInternalFieldCount(2);

     /*

     //关联到C++对象的成员函数

     object_temp->Set(String::New("setX"),FunctionTemplate::New(Obj::setX)->GetFunction());

     object_temp->Set(String::New("getX"),FunctionTemplate::New(Obj::getX)->GetFunction());

     //关联到C++对象的属性

     object_temp->SetAccessor(String::New("x"),Obj::getPropertyX, Obj::setPropertyX);

*/

     //创建JavaScript对象

     Local<Object>obj = object_temp->NewInstance();

     //关联到c++对象

     obj->SetInternalField(0,External::New(g_o));

     //关联到C++对象的成员函数

     obj->Set(String::New("setX"),FunctionTemplate::New(Obj::setX)->GetFunction());

     obj->Set(String::New("getX"),FunctionTemplate::New(Obj::getX)->GetFunction());

     //关联到C++对象的属性

     obj->SetAccessor(String::New("x"), Obj::getPropertyX,Obj::setPropertyX);

 

     //创建JavaScript对象

     Local<Object>obj1 = object_temp->NewInstance();

     //关联到c++对象

     obj1->SetInternalField(1,External::New(g_o));

 

 

     //将JavaScript对象放入到执行环境中去

     context->Global()->Set(String::New("obj"),obj, (PropertyAttribute)(v8::ReadOnly));

context->Global()->Set(String::New("obj1"), obj1,(PropertyAttribute)(v8::ReadOnly));

 

看上面的例子中,蓝色和红色部分,再ObjectTemplate或者在Object添加属性都可以,在前者添加NewInstance后,所有Object都生效,在后者添加只在当前这个Object生效。

 

10.7.     通过interceptor访问对象属性

可以通过interceptor来访问属性,它的优势是可以不需要些很多访问器,只需要写一个就可以了:

 

static Handle<Value>MapGet(Local<Stringname,const AccessorInfo&info)

     {

         string key = v8helper::ObjectToString(name);

        

         printf("MapGet:%s\n",key.c_str());

 

         if(key != "x"returnHandle<Value>();

 

         return Integer::New(v8helper::Unwrap<Obj>(info.Holder())->x_);

     }

 

 

     static Handle<ValueMapSet(Local<String>nameLocal<Valuevalue_obj,const AccessorInfo&info)

     {

         string key = v8helper::ObjectToString(name);

 

         printf("MapSet:%s\n",key.c_str());

 

         if(key != "x"returnHandle<Value>();

 

         int value = value_obj->Int32Value();

 

         v8helper::Unwrap<Obj>(info.Holder())->x_value_obj->Int32Value();

 

         return Integer::New(v8helper::Unwrap<Obj>(info.Holder())->x_);

              }

 

     HandleScope handle_scope;

     Persistent<Context>context = Context::New();

     Context::Scope context_scope(context);

 

     //注册全局的函数

     Local<Object>global_temp = Context::GetCurrent()->Global();

     global_temp->Set(String::New("print"), FunctionTemplate::New(v8helper::Print)->GetFunction());

 

     //准备JavaScript中的对象

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

     object_temp->SetInternalFieldCount(1);

     object_temp->SetNamedPropertyHandler(Obj::MapGetObj::MapSet);

     //关联到C++对象的属性

//   object_temp->SetAccessor(String::New("x"), Obj::getPropertyX,Obj::setPropertyX);

 

     //创建JavaScript对象

     Local<Object>obj = object_temp->NewInstance();

     //关联到c++对象

     obj->SetInternalField(0,External::New(g_o));

 

     //将JavaScript对象放入到执行环境中去

     context->Global()->Set(String::New("obj"),obj, (PropertyAttribute)(v8::ReadOnly));

     {

         HandleScope handle_scope;

 

         //在JavaScript中运行

         Handle<Script>script = Script::Compile(String::New("print(obj.x);obj.x=15;print(obj.x);"));

 

         Handle<Value>result = script->Run();

     }

context.Dispose();

 

注意:例子中是通过名字字段做的索引,也可以通过数字索引来做(SetIndexedPropertyHandler),原理是一样的。

 

10.8.     ObjectTemplate内部字段的用途(SetInternalField)

这个字段保存ObjectTemplate生成的JavaScript对象和C++对象的关联关系,通常都设置为1(即保存一个JavaScript对象和C++对象的对应)前面的例子都是这样的用。当然也可以为>1的数,如下:

 

class Obj1

{

public:

     Obj1(int x) : x_(x) { }

 

public:

     static Handle<ValuegetPropertyX(Local<String>propertyconstAccessorInfoinfo)

     {

         //       printf("Obj::getPropertyX\n");

         return Integer::New(v8helper::Unwrap<Obj>(info.Holder(),0)->x_);

     }

 

     static void setPropertyX(Local<Stringproperty,Local<Value>valueconstAccessorInfoinfo)

     {

         //       printf("Obj::setPropertyX\n");

         v8helper::Unwrap<Obj>(info.Holder(), 0)->x_value->Int32Value();

     }

 

public:

     int x_;

};

 

class Obj2

{

public:

     Obj2(int x) : x_(x) { }

 

public:

     static Handle<ValuegetPropertyX(Local<String>propertyconstAccessorInfoinfo)

     {

         //       printf("Obj::getPropertyX\n");

         return Integer::New(v8helper::Unwrap<Obj>(info.Holder(),1)->x_);

     }

 

     static void setPropertyX(Local<Stringproperty,Local<Value>valueconstAccessorInfoinfo)

     {

         //       printf("Obj::setPropertyX\n");

         v8helper::Unwrap<Obj>(info.Holder(), 1)->x_value->Int32Value();

     }

 

public:

     int x_;

};

 

Obj1*g_o1 = new Obj1(13);

Obj2*g_o2 = new Obj2(17);

 

void test_object_internal_field(int argccharargv[])

{

     HandleScope handle_scope;

     Persistent<Context>context = Context::New();

     Context::Scope context_scope(context);

 

     //注册全局的函数

     Local<Object>global_temp = Context::GetCurrent()->Global();

     Local<FunctionTemplate>ft = FunctionTemplate::New(v8helper::Print);

     global_temp->Set(String::New("print"), ft->GetFunction());

 

     //准备JavaScript中的对象

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

     object_temp->SetInternalFieldCount(2);

 

     //创建JavaScript对象

     Local<Object>obj1 = object_temp->NewInstance();

     obj1->SetAccessor(String::New("x"), Obj1::getPropertyXObj1::setPropertyX);

     //关联到c++对象

     obj1->SetInternalField(0,External::New(g_o1));

     //将JavaScript对象放入到执行环境中去

     context->Global()->Set(String::New("obj1"),obj1, (PropertyAttribute)(v8::ReadOnly));

 

    

     //创建JavaScript对象

     Local<Object>obj2 = object_temp->NewInstance();

     //关联到c++对象

     obj2->SetInternalField(1,External::New(g_o2));

     obj2->SetAccessor(String::New("x"), Obj2::getPropertyXObj2::setPropertyX);

     context->Global()->Set(String::New("obj2"),obj2, (PropertyAttribute)(v8::ReadOnly));

    

     {

         HandleScope handle_scope;

 

         //;print(obj.x);obj.x=15;print(obj.x);

         //在JavaScript中运行

     //   Handle<Script>script = Script::Compile(String::New("print(obj1.x);"));

         Handle<Script>script = Script::Compile(String::New("print(obj1.x+ ':' + obj2.x);"));

 

         Handle<Value>result = script->Run();

     }

 

     context.Dispose();

}

 

以上例子中可以在ObjectTemplate上面绑定多个JavaScript对象和C++对象的映射,但是不知道这种方式和启用多个ObjectTemplate,每个ObjectTemplate new一个Object的方式有什么差别?难道内存消耗会少一些?

 

10.9.     创建FunctionTemplate和C++对象关联

以上例子中都是创建的JavaScript对象和C++对象关联,简单的说就是在JavaScript直接实用全局的JavaScript对象,这时C++的对象都是创建好的,在JavaScript直接使用。

如何在JavaScript调用FunctionTemplate呢?或者说如何在JavaScript中new一个C++的对象呢?

 

创建一个js框架,底层采用自己的C网络层,上层使用js来实现服务层,看起来困难不大。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值