javasript与c&c++的交互

相信初学javascript的工作于嵌入式系统的人,当然包括我自己,都有一个疑问,如果应用是html/css/javascrip写,而中间件是c/c++写,那么javascript与c/c++中间件API是如何相互调用的呢?通过一段时间的学习,在此做个总结

1.JavaScriptCore与头文件

基于webkit的开发需要包含下面两个头文件:
#include <WebKit.h>
#include <JavaScriptCore/JavaScript.h>

JavaScriptCore API: http://developer.apple.com/library/mac/#documentation/Carbon/Reference/WebKit_JavaScriptCore_Ref/JSObjectRef_h/index.html


2.在c++中创建javascript类

如果你熟悉javascript类,那么你会看到在c++中创建javascript类与在javascript里创建类是基本一样的,除了在c++中需要写成员函数,还可以有一个finalize函数,这个函数在javascript中是没有的。

2.1定义一个javascript类

static JSClassDefinition jsBaseTvDefinition =
{
    0,                     //version
    kJSClassAttributeNone, //attributes
    "__BaseTvClass",       //className
    0,                     //parentClass
    0,                     //staticValues
    jsBaseTvFunctions,     //staticFunctions
    0,                     //Initialize
    finalize,                     //Finalize
    0,                     //has Property
    0,                     //get Property
    0,                     //set Property
    0,                     //delete Property
    0,                     //getPropertyNames
    0,                     //callAsFunction
    0,                     //hasInstance
    0,                     //callAsConstructor
    0                      //convertToType
}
这里我定义了一个叫"__BaseTvClass"的类和一个叫finalize的方法(前缀"__"不是必需的,只是为了防止与javascript中定义的类冲突)。在javascript中,它相当于下面这个样子
function __BaseTvClass
{
...
}

2.2定义成员函数

static JSStaticFunction jsBaseTvFunctions[]
{
 {"setSource",JSBaseTv::setSource,kJSPropertyAttributeNone},
 {"getCurrentSource",JSBaseTv::getCurrentSource,kJSPropertyAttributeNone},
 {"startChannelScan",JSBaseTv::startChannelScan,kJSPropertyAttributeNone},
 {"abortChannelScan",JSBaseTv::abortChannelScan,kJSPropertyAttributeNone},
 {0,0,0}
}

这是个函数名的数组和对应的c/c++函数,它在javascript中相当于:

function __BaseTvClass()
{
 this.setSource=function()
 {
 }
 this.getCurrentSource=function()
 {
 }
 this.startChannelScan=function()
 {
 }
 this.abortChannelScan=function()
 {
 }
}

JSStaticFunction的声明是:
typedef struct { 
    const char *const name; 
    JSObjectCallAsFunctionCallback callAsFunction; 
    JSPropertyAttributes attributes; 
} JSStaticFunction

name:一个字符串,表示属性的名字;
callAsFunction:

一个callback函数,即当name作为一个函数调用时,callAsFunction会被调用。可以理解为name与callAsFunction的绑定,name对javascript可见,当javascript中调用name时,即相当于callAsFunction被调用。

attributes:name的一个属性集

下面介绍下函数的实现,在c++中每个函数都有一个的声明,参数都是一致的:

JSValueRef JSBaseTv::startChannelScan(JSContextRef ctx,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,
 const JSValueRef arguments[],JSValueRef *exception)
{
  if(argumentCount < 1)
  	return JSValueMakeNull(ctx);
  JSStringRef jsStr = JSValueToStringCopy(ctx,arguments[0],NULL);
  
  char scanType[32];
  JSStringGetUTF8CString(jsStr,scanType,sizeof(scanType)-1);
  if(!strcmp(scanType,"atv"))
  {
  /*  */
  }
  else if(!strcmp(scanType,"dtv"))
  {
  /*  */
  }
  else
  {
  /*  */
  }
  return JSValueMakeNull(ctx);	
} 
JSValueRefJSBaseTv::abortChannelScan(JSContextRef ctx,JSObjectRef function,JSObjectRefthisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef*exception){/*argumentCount代表了传入参数的个数,arguments[]存储了具体的参数,arguments[0]是第一个参数,arguments[1]是第二个参数,以此类推*/}
你可以看到每个函数参数的样式一样的,都是5个参数。

finalize方法是可选的,你可以通过它在javascript类清除之前做一些资源释放之类的动作
void JSBaseTv::finalize(JSObjectRef thisObject) {
	// delete me;
}


3.创建javascript类的对象实例

通过上面的代码我们定义了一个c++的类和对应的一些函数,下面介绍怎么创建它的实例,从而在javasript中能够调用它。
void JSBaseTv::Setup()
{
 globalContext = JSGlobalContextCreate(0);
 JSObjectRef global = JSContextGetGlobalOjbect(globalContext);
 JSClassRef baseTvClass = JSClassCreate(&jsBaseTvDefinition);
 JSOjbectRef baseTv = JSObjectMake(globalContext,baseTvClass,NULL);
 JSStringRef name = JSStringCreateWithUTF8CString("$BaseTv");
 JSObjectSetProperty(globalContext,global,name,baseTv,kJSPropertyAttributeNone,0);
 JSStringRelease(name);
 JSClassRelease(baseTvClass);
}
这段代码创建了一个jsBaseTvDefinition的类,并且创建了一个全局变量$BaseTv,"$"不是必需的。$BaseTv实际上就是jsBaseTvDefinition的实例化,放在javascript中,它相当的作用:
$BaseTv = new jsBaseTvDefinition();

现在全局变量$BaseTv对HTML里的javascript是可见的,你可以直接以名字$BaseTv或者window[“$BaseTv”]来使用,例如下面这段javascript代码调用函数startChannelScan

startChannelScan:function()
{
 $BaseTv.startChannelScan("atv");
}


再看下面这个函数:
JSValueRef JSBaseTv::getDevices(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
	DLNA* me = (DLNA*) JSObjectGetPrivate(thisObject);

	if (me->devices.size() == 0)
		return JSValueMakeNull(ctx);

	Lock lock(&me->mutex);
	JSObjectRef* js = new JSObjectRef[me->devices.size()];
	size_t i = 0;
	for (map<string, DeviceHandle>::iterator it = me->devices.begin(); it != me->devices.end(); i++, it++) {
		JSObjectRef o = JSObjectMake(ctx, NULL, NULL);
		DeviceInfo info;
		GetDeviceInfo(me->dmp, it->second, &info);
		setProperty(ctx, o, "id", info.udn);
		setProperty(ctx, o, "name", info.friendlyName);
		setProperty(ctx, o, "desc", info.modelDescription);
		js[i] = o;
	}

	JSObjectRef r = JSObjectMakeArray(ctx, i, js, exception);
	delete[] js;
	return r;
}
void JSBaseTv::setProperty(JSContextRef ctx, JSObjectRef o, const char* name, const char* value) {
	JSStringRef n = JSStringCreateWithUTF8CString(name);
	JSStringRef v = JSStringCreateWithUTF8CString(value);
	JSObjectSetProperty(ctx, o, n, JSValueMakeString(ctx, v), kJSPropertyAttributeNone, 0);
	JSStringRelease(n);
	// JSStringRelease(v); // Do not release v as it's still needed by JSValue. It'll be released by GC.
}

void JSBaseTv::setProperty(JSContextRef ctx, JSObjectRef o, const char* name, double value) {
	JSStringRef n = JSStringCreateWithUTF8CString(name);
	JSObjectSetProperty(ctx, o, n, JSValueMakeNumber(ctx, value), kJSPropertyAttributeNone, 0);
	JSStringRelease(n);
}

 然后你可以在javascript中调用 
    
 
    
 
    
 
   
var servers=$BaseTv.GetDevices()

现在servers就是一个对象数组,包含了属性id,name,desc
[{ id: "…", name: "…", desc: "…" }, …];

4.从c/c++调用javascript函数

前面介绍了如何定义一个类,并且从javascript中调用它,但一个完整的应用可能需要从c/c++中调用javascript的函数,比如丢一些事件给javascript。其实比较简单,如果有一个你想调用的全局函数并且你知道它的名字和参数,你可以通过JSObjectGetProperty获取这个函数的句柄,然后通过JSCallAsFunction调用它。
另一种方法是你可以通过javascript注册一个函数,存储它,需要的时候调用它。下面是一个例子:
JSValueRef JSBaseTv::info(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
	if (argumentCount < 1) {
		*exception = JSValueMakeString(ctx, JSStringCreateWithUTF8CString("Info(callback) argument invalid."));
		return JSValueMakeNull(ctx);
	}
	DLNA* me = (DLNA*) JSObjectGetPrivate(thisObject);
	JSObjectRef cb = const_cast<JSObjectRef>(arguments[0]);
	if (!JSObjectIsFunction(ctx, cb)) {
		me->infoCallback = NULL;
		return JSValueMakeNull(ctx);
	}
	me->infoCallback = cb;
	return JSValueMakeNull(ctx);
}
在javascript中,你可以调用
$BaseTv.info(msgHandler.handle);
handle这个函数就注册下去了。
在javascript中相应的定义了:
var msgHandler = function() {
this.handle = function(msg, err) {
…
    };
}

需要的时候,在c/c++中你可以用下面的方法调用msgHanlder.handle
void JSBaseTv::msg(const char* msg, int id) {
	if (!infoCallback) return;
	JSValueRef arguments[2];
	JSStringRef n = JSStringCreateWithUTF8CString(msg);
	arguments[0] = JSValueMakeString(globalContext, n);
	arguments[1] = JSValueMakeNumber(globalContext, id);
	JSObjectCallAsFunction(globalContext, infoCallback, NULL, 2, arguments, NULL);
	JSStringRelease(n);
}

5.javascript对象的生命周期


JavascriptCore的GC(Garbage Collection)会负责回收你创建的对象,GC会监视任何由JSValueMakeNumber,JSValueMakeString,  JSObjectMake返回的结果,如果不用的时候GC会回收它,有几种情况例外:

JSStringCreateWithUTF8CString, JSStringCreateWithCFString需要用JSStringRelease释放;

JSObjectCopyPropertyNames(一个对象属性数组)需要用JSPropertyNameArrayRelease释放;

JSCreateClass需要JSClassRelease释放;

由JSGlobalContextCreate创建的Javascript context本身需要用JSGlobalContextRelease释放.

对于什么时候该主动调用release函数,什么时候GC会自动release,可能会觉得有点混乱。可以简单地这样理解,由javascriptcontext设置或创建的对象,GC会自动回收。例如:

JSStringRef n = JSStringCreateWithUTF8CString(name);
JSValueRef ret =JSObjectGetProperty(ctx, o, n, NULL);
JSStringRelease(n);

n不是由context创建,所以需要主动回收,ret由ctx创建,GC会负责回收,在这里不需要主动调用release函数释放。

如果不想GC回收一个对象,可以使用函数JSValueProtect(JSContextRefctx, JSValueRef value)保护value不被GC回收,函授JSValueUnProtect释放保护,GC就可以回收value了,JSValueProtect与JSValueUnProtect调用的次数必需相等。












猜你喜欢

转载自blog.csdn.net/wowo1109/article/details/6685478
今日推荐