您的位置:首页 > 编程语言 > C语言/C++

javasript与c&c++的交互

2011-08-14 10:16 405 查看
相信初学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调用的次数必需相等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: