Mingz技术博客

...

导航

Google V8编程详解(五)JS调用C++

http://blog.csdn.net/feiyinzilgd/article/details/8453230

最近由于忙着解决个人单身的问题,时隔这么久才更新第五章。

上一章主要讲了Google V8的Context概念。那么其实Google V8的基本概念还有FunctionTemplate, ObjectTemplate等比较重要的基本概念,这些概念将在后续章节中进行渗透。

本章主要来讲讲如何通过V8来实现JS调用C++。JS调用C++,分为JS调用C++函数(全局),和调用C++类。

JS调用C++函数

JS调用C++函数,就是通过FunctionTemplate和ObjectTemplate进行扩展的。

FunctionTemplate,ObjectTemplate可以理解为JS function和C++ 函数之间的binding。FunctionTemplate实现了JS函数和C++函数的绑定,当然这种绑定是单向的,只能实现JS调用C++的函数。说的更直白一点,FunctionTemplate和ObjectTemplate就相当于JS的function和object。

基本原理就是先将C++ 函数通过FunctionTemplate实现绑定,然后将这个FunctionTemplate注册到JS的global上去,这样,JS就可以调用C++函数了。

代码如下:

上面这段代码实现了在JS调用C++ Yell()函数。

基本步骤分为A, B , C三步:

 

  1. #include "v8.h"  
  2. #include <string.h>  
  3. #include <stdio.h>  
  4.   
  5. using namespace v8;  
  6. using namespace std;  
  7.   
  8.   
  9. Handle<Value> Yell(const Arguments& args) {  
  10.     HandleScope  handle_scope;  
  11.     char buffer[4096];  
  12.       
  13.     memset(buffer, 0, sizeof(buffer));  
  14.     Handle<String> str = args[0]->ToString();  
  15.     str->WriteAscii(buffer);  
  16.     printf("Yell: %s\n", buffer);  
  17.   
  18.     return Undefined();  
  19. }  
  20.   
  21. int main(int argc, char** argv) {  
  22.     HandleScope handle_scope;  
  23.   
  24.     //A  
  25.     Handle<FunctionTemplate> fun = FunctionTemplate::New(Yell);  
  26.   
  27.     //B  
  28.     Handle<ObjectTemplate> global = ObjectTemplate::New();  
  29.     global->Set(String::New("yell"), fun);  
  30.   
  31.     //C  
  32.     Persistent<Context> cxt = Context::New(NULL, global);  
  33.   
  34.     Context::Scope context_scope(cxt);  
  35.     Handle<String> source = String::New("yell('Google V8!')");  
  36.     Handle<Script> script = Script::Compile(source);  
  37.     Handle<Value> result = script->Run();  
  38.   
  39.     cxt.Dispose();  
  40. }  



 

第一步,定义一个FunctionTempte并与C++函数绑定:

 

  1. Handle<FunctionTemplate> fun = FunctionTemplate::New(Yell);  


第二部,定义一个ObectTemplate,并向该对象注册一个FunctionTemplate

 

 

  1. Handle<ObjectTemplate> global = ObjectTemplate::New();  
  2. global->Set(String::New("yell"), fun);  


第三部,将该对象注册到JS的global中去:

 

 

  1. Persistent<Context> cxt = Context::New(NULL, global);  

 

JS调用C++类

JS其实是无法直接使用C++类的,当JS中new一个对象的时候,需要手动将C++产生的对象同JS的对象进行绑定。从而就造成了JS使用C++类的假象:

 

[javascript] view plaincopyprint?
 
  1. var cloudapp = new CloudApp();  
  2. cloudapp.xxInterface();  

这一点V8做的不够强大,而Qt的QML(类JS脚本语言)就能实现自动绑定。

 

InternalField

当JS new一个对象的时候,C++中也会同步的new一个对象并将该指针保存在C++内部,并维护这个指针list,这就是V8 InternalField的作用。所有需要跟JS绑定的C++指针都存在这个InternalField中,其实就是一个list,一个V8 Object可以拥有任意数量的InternalField。如果需要使用保存在InterField中的C++指针,直接Get出来即可:

将C++指针封装到InternalField中:

 

  1. //....  
  2. void* ptr = ...  
  3. object->SetInternalField(0, External::New(ptr));  

上面这段代码将一个C++指针ptr保存在InternalField的index 0处。然后将来的某个时候如果需要获取这个指针,只需使用index 0来获取该指针。

 

将C++指针从InternalField中获取出来:

 

  1. Local<External> wrap = Local<External>::Cast(object->GetInternalField(0));  
  2. void* ptr = wrap->Value();  

object->GetInternalField(0)就是从InternalField取出index=0处的C++指针。

External

 

既然说到C++指针的绑定,就必须说一下V8的External了。V8的External就是专门用来封装(Wrap)和解封(UnWrap)C++指针的。V8的External 实现如下:

 

  1. Local<Value> External::Wrap(void* value) {  
  2.   return External::New(value);  
  3. }  
  4.   
  5.   
  6. void* External::Unwrap(Handle<v8::Value> obj) {  
  7.   return External::Cast(*obj)->Value();  
  8. }  

External其实就是C++指针的载体。这也就解释了前面在InternalField中设置和获取InternalField中的C++指针的时候,使用了External::New和wrap->Value()的原因了。External::Value()返回的就是C++指针。

 

下面开始上代码,看看究竟是如何实现JS调用C++类的:

 

  1. //C++Externtion  
  2. #include "v8.h"  
  3. #include "utils.h"  
  4.   
  5. #include <iostream>  
  6. #include <string>  
  7.   
  8. using namespace std;  
  9.   
  10. using namespace v8;  
  11.   
  12. enum AppState{  
  13.     IDEL = 0,  
  14.     LOADED,  
  15.     STOP  
  16. };  
  17.   
  18. class CloudApp {  
  19. public:  
  20.     CloudApp(int id) {   
  21.         state = IDEL;  
  22.         appId = id;  
  23.     }  
  24.     void start() {  
  25.         cout << "CloudApp been Loaded id = " << appId << endl;  
  26.         state = LOADED;  
  27.     };  
  28.   
  29.     int getState() { return state;}  
  30.     int getAppId() { return appId;}  
  31.       
  32. private:  
  33.     AppState state;  
  34.     int appId;    
  35. };  
  36.   
  37. //向MakeWeak注册的callback.  
  38. void CloudAppWeakReferenceCallback(Persistent<Value> object  
  39.                                                 , void * param) {  
  40.     if (CloudApp* cloudapp = static_cast<CloudApp*>(param)) {  
  41.         delete cloudapp;  
  42.     }  
  43. }  
  44.   
  45. //将C++指针通过External保存为Persistent对象,避免的指针被析构  
  46. Handle<External> MakeWeakCloudApp(void* parameter) {  
  47.     Persistent<External> persistentCloudApp =   
  48.         Persistent<External>::New(External::New(parameter));  
  49.           
  50. //MakeWeak非常重要,当JS世界new一个CloudApp对象之后  
  51. //C++也必须new一个对应的指针。  
  52. //JS对象析构之后必须想办法去析构C++的指针,可以通过MakeWeak来实现,  
  53. //MakeWeak的主要目的是为了检测Persistent Handle除了当前Persistent   
  54. //的唯一引用外,没有其他的引用,就可以析构这个Persistent Handle了,  
  55. //同时调用MakeWeak的callback。这是我们可以再这个callback中delete   
  56. //C++指针  
  57.     persistentCloudApp.MakeWeak(parameter, CloudAppWeakReferenceCallback);  
  58.   
  59.     return persistentCloudApp;  
  60. }  
  61.   
  62. //将JS传进来的参数解析之后,创建C++对象  
  63. CloudApp* NewCloudApp(const Arguments& args) {  
  64.     CloudApp* cloudApp = NULL;  
  65.       
  66.     if (args.Length() == 1) {  
  67.         cloudApp = new CloudApp(args[0]->ToInt32()->Value());   
  68.     } else {  
  69.         v8::ThrowException(String::New("Too many parameters for NewCloudApp"));  
  70.     }  
  71.   
  72.     return cloudApp;  
  73. }  
  74.   
  75. //相当于JS对应的构造函数,当JS中使用new CloudApp的时候,这个callback将自动被调用  
  76. Handle<Value> CloudAppConstructCallback(const Arguments& args) {  
  77.     if (!args.IsConstructCall())  
  78.         return Undefined();  
  79.       
  80.     CloudApp* cloudapp = NewCloudApp(args);  
  81.     Handle<Object> object = args.This();  
  82.   
  83.     object->SetInternalField(0, MakeWeakCloudApp(cloudapp));  
  84.   
  85.     return Undefined();  
  86. }  
  87.   
  88. Handle<Value> GetState(const Arguments& args) {  
  89.     Handle<Object> self = args.Holder();  
  90.   
  91.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
  92.     void* ptr = wrap->Value();  
  93.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
  94.   
  95.     return Integer::New(cloudapp->getState());  
  96. }  
  97.   
  98. Handle<Value> GetAppId(const Arguments& args) {  
  99.     Handle<Object> self = args.Holder();  
  100.   
  101.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
  102.     void* ptr = wrap->Value();  
  103.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
  104.   
  105.     return Integer::New(cloudapp->getAppId());  
  106. }   
  107.   
  108. Handle<Value> Start(const Arguments& args) {  
  109.     Handle<Object> self = args.Holder();  
  110.   
  111.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
  112.     void* ptr = wrap->Value();  
  113.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
  114.   
  115.     cloudapp->start();  
  116.   
  117.     return Undefined();  
  118. }  
  119.   
  120. void SetupCloudAppInterface(Handle<ObjectTemplate> global) {  
  121.     Handle<FunctionTemplate> cloudapp_template =   
  122.         FunctionTemplate::New(CloudAppConstructCallback);  
  123.     cloudapp_template->SetClassName(String::New("CloudApp"));  
  124.   
  125.     Handle<ObjectTemplate> cloudapp_proto = cloudapp_template->PrototypeTemplate();  
  126.     //这一步,完全可以使用cloudapp_inst->Set(....)  
  127.     //使用prototype更符合JS编程  
  128.     cloudapp_proto->Set(String::New("start"), FunctionTemplate::New(Start));  
  129.     cloudapp_proto->Set(String::New("state"), FunctionTemplate::New(GetState));  
  130.     cloudapp_proto->Set(String::New("appid"), FunctionTemplate::New(GetAppId));  
  131.       
  132.     //******很重要!!!  
  133.     Handle<ObjectTemplate> cloudapp_inst = cloudapp_template->InstanceTemplate();  
  134.     cloudapp_inst->SetInternalFieldCount(1);  
  135.       
  136.     //向JS世界注册一个函数,其本质就是向JS世界的global注册一个类。  
  137.     //所以,也是通过向global注入CloudApp类。  
  138.     global->Set(String::New("CloudApp"), cloudapp_template);  
  139. }  
  140.   
  141. void InitialnilizeInterface(Handle<ObjectTemplate> global) {  
  142.     SetupCloudAppInterface(global);  
  143. }  
  144.   
  145. void LoadJsAndRun() {  
  146.     Handle<String> source = ReadJS("script.js");  
  147.     Handle<Script> script = Script::Compile(source);  
  148.     Handle<Value> result = script->Run();  
  149.   
  150.     printValue(result);  
  151. }  
  152.   
  153. void Regist2JsContext(Handle<ObjectTemplate>& object  
  154.                             , Persistent<Context>& context) {  
  155.     context = Context::New(NULL, object);  
  156. }  
  157.   
  158. int main(int argc, char** argv) {  
  159.     HandleScope handle_scope;  
  160.     Handle<ObjectTemplate> global = ObjectTemplate::New();  
  161.     Persistent<Context> context;  
  162.       
  163.     InitialnilizeInterface(global);  
  164.     Regist2JsContext(global, context);  
  165.     Context::Scope context_scope(context);  
  166.     LoadJsAndRun();  
  167.   
  168.     context.Dispose();  
  169.       
  170.     return 0;  
  171. }  


JS代码如下:

 

 

[javascript] view plaincopyprint?
 
  1. //script.js  
  2. var cloudapp = new CloudApp(24);  
  3. cloudapp.start();  
  4. var result;  

上面的代码基本可以从函数名称和注释中明白是什么意思。最后再讲一点SetInternalFieldCount:

 

 

  1. Handle<ObjectTemplate> cloudapp_inst = cloudapp_template->InstanceTemplate();  
  2. cloudapp_inst->SetInternalFieldCount(1);  


在其他的操作都就绪之后还必须SetInsternalFieldCount(),这一点是为了告诉V8,我们有几个InternalField,这里是只有1个。否则,在JS和C++指针交互过程中,V8在查找InternalField的时候会越界的。

 

版权申明:
转载文章请注明原文出处,任何用于商业目的,请联系本人:hyman_tan@126.com

posted on 2013-08-01 20:08  Mingz2013  阅读(2419)  评论(0编辑  收藏  举报