1. 程式人生 > >cocos2dx-js 繫結c++

cocos2dx-js 繫結c++

最近在倒騰c++繫結方法到js,供js呼叫。在此記錄一下,供交流和學習。

coscos2dx有很多版本,例如cocos2dx-js, -lua版本。使用指令碼語言進行協同開發,好處之一就提現在版本升級方面。當修改了邏輯之後,只需要替換本地的響應指令碼程式碼即可。省去了一下稽核流程。

這裡說說c++繫結到js,供js呼叫c++方法。

 主要用到的幾個巨集:

用於宣告的巨集:

<p class="p1"><span class="s1">#define JS_BINDED_CLASS_GLUE(klass) \</span></p><p class="p1"><span class="s1">static JSClass js_class; \</span></p><p class="p1"><span class="s1">static JSObject* js_proto; \</span></p><p class="p1"><span class="s1">static JSObject* js_parent; \</span></p><p class="p1"><span class="s1">static void _js_register(JSContext* cx, JS::HandleObject global);</span></p>
JS_BINDED_CLASS_GLUE,這個巨集主要定義了3個變數和1個方法。

js_class初始化比較固定:

JSClass jsclass = {
        "方法名", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
<span style="white-space:pre">		</span> JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,basic_object_finalize
    };

其中basic_object_finalize,一般為該類的銷燬方法。功能類似解構函式。


js_proto初始化需要呼叫方法:JS_InitClass
JavaScriptObjCBridge::js_proto = JS_InitClass(cx, global, JS::NullPtr(), &JavaScriptObjCBridge::js_class , JavaScriptObjCBridge::_
<span style="white-space:pre">						</span>js_constructor, 0, props, funcs, NULL, NULL);
js_parent初始化直接設定為NULL就行。

方法:void_js_register(JSContext *cx, JS::HandleObject global)

該方法供js引擎呼叫,這裡面主要的工作就初始化函式呼叫列表。

<span style="font-family: Arial, Helvetica, sans-serif;">#define JS_BINDED_CONSTRUCTOR(klass) \</span>
static bool _js_constructor(JSContext *cx, unsigned argc, jsval *vp)

該方法主要用來對需要註冊到js的類進行初始化。初始化的工作內容包括,js_proto,js_parent這個兩個變數的初始化,並把加入js引擎裡面去。供js呼叫。


#define JS_BINDED_FUNC(klass, name) \
bool name(JSContext *cx, unsigned argc, jsval *vp)
該巨集主要定義需要繫結到js的方法名。有一個對應的實現方法:
#define JS_BINDED_FUNC_IMPL(klass, name) \
static bool klass##_func_##name(JSContext *cx, unsigned argc, jsval *vp) { \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
JS::RootedObject thisObj(cx, args.thisv().toObjectOrNull()); \
klass* obj = (klass*)JS_GetPrivate(thisObj); \
if (obj) { \
return obj->name(cx, argc, vp); \
} \
JS_ReportError(cx, "Invalid object call for function %s", #name); \
return false; \
} \
bool klass::name(JSContext *cx, unsigned argc, jsval *vp)
判斷傳過來的引數是否為空,如果不為空,呼叫name這個方法。此處獲取引數的方式與以往版本有所不同。

之前獲取引數的方式:JS_ARGV這個巨集,實際就是把vp這個指標偏移2.

現在獲取引數的方式:通過方法:JS::CallArgsFromvp(argc,vp).

下面以一個簡單的例子,刪除無關程式碼。

.h
class JavaScriptObjCBridge{
public:
    JS_BINDED_CLASS_GLUE(JSObjCBridge);
    JS_BINDED_CONSTRUCTOR(JSObjCBridge);
    JS_BINDED_FUNC(JSObjCBridge, callStaticMethod);
};


.cpp
/**
 *  @brief Initialize Object and needed properties.
 *
 */
JS_BINDED_CLASS_GLUE_IMPL(JavaScriptObjCBridge);

/**
 *  @brief Implementation for the Javascript Constructor
 *
 */
JS_BINDED_CONSTRUCTOR_IMPL(JavaScriptObjCBridge)
{
    JavaScriptObjCBridge* jsj = new (std::nothrow) JavaScriptObjCBridge();
    
    js_proxy_t *p;
    jsval out;
    
    JS::RootedObject proto(cx, JavaScriptObjCBridge::js_proto);
    JS::RootedObject parentProto(cx, JavaScriptObjCBridge::js_parent);
    JS::RootedObject obj(cx, JS_NewObject(cx, &JavaScriptObjCBridge::js_class, proto, parentProto));
    
    if (obj) {
        JS_SetPrivate(obj, jsj);
        out = OBJECT_TO_JSVAL(obj);
    }
    
    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    args.rval().set(out);
    p = jsb_new_proxy(jsj, obj);
    
    JS::AddNamedObjectRoot(cx, &p->obj, "JavaScriptObjCBridge");
    return true;
}

/**
 *  @brief destructor for Javascript
 *
 */
static void basic_object_finalize(JSFreeOp *freeOp, JSObject *obj)
{
    CCLOG("basic_object_finalize %p ...", obj);
    
    js_proxy_t* nproxy;
    js_proxy_t* jsproxy;
    JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
    JS::RootedObject jsobj(cx, obj);
    jsproxy = jsb_get_js_proxy(jsobj);
    if (jsproxy) {
        nproxy = jsb_get_native_proxy(jsproxy->ptr);
        jsb_remove_proxy(nproxy, jsproxy);
    }
}
<p class="p1"><span class="s1">JS_BINDED_FUNC_IMPL</span><span class="s2">(</span><span class="s3">JavaScriptObjCBridge</span><span class="s2">, callStaticMethod){</span></p><p class="p1"><span class="s2">    </span><span class="s3">JS</span><span class="s2">::</span><span class="s3">CallArgs</span><span class="s2"> args = </span><span class="s3">JS</span><span class="s2">::</span><span class="s3">CallArgsFromVp</span><span class="s2">(argc, vp);</span></p><p class="p1"><span class="s2">    </span><span class="s1">if</span><span class="s2"> (argc >= 2) {</span></p><p class="p1"><span class="s2">        </span><span class="s3">JSStringWrapper</span><span class="s2"> arg0(args.</span><span class="s3">get</span><span class="s2">(0));</span></p><p class="p1"><span class="s2">        </span><span class="s3">JSStringWrapper</span><span class="s2"> arg1(args.</span><span class="s3">get</span><span class="s2">(1));</span></p><p class="p1"><span class="s2">        </span><span class="s3">CallInfo</span><span class="s2"> call(arg0.</span><span class="s3">get</span><span class="s2">(),arg1.</span><span class="s3">get</span><span class="s2">());</span></p><p class="p1"><span class="s2">        </span><span class="s1">bool</span><span class="s2"> ok = call.</span><span class="s3">execute</span><span class="s2">(cx,args.</span><span class="s3">array</span><span class="s2">(),argc);</span></p><p class="p1"><span class="s2">        </span><span class="s1">if</span><span class="s2">(!ok){</span></p><p class="p2"><span class="s4">            </span><span class="s3">JS_ReportError</span><span class="s4">(cx, </span><span class="s2">"js_cocos2dx_JSObjCBridge : call result code: %d"</span><span class="s4">, call.</span><span class="s3">getErrorCode</span><span class="s4">());</span></p><p class="p1"><span class="s2">            </span><span class="s1">return</span><span class="s2"> </span><span class="s1">false</span><span class="s2">;</span></p><p class="p1"><span class="s2">        }</span></p><p class="p3"><span class="s4">        args.</span><span class="s2">rval</span><span class="s4">().</span><span class="s2">set</span><span class="s4">(</span><span class="s2">convertReturnValue</span><span class="s4">(cx, call.</span><span class="s2">getReturnValue</span><span class="s4">(), call.</span><span class="s2">getReturnValueType</span><span class="s4">()));</span></p><p class="p1"><span class="s2">        </span><span class="s1">return</span><span class="s2"> ok;</span></p><p class="p1"><span class="s2">    }</span></p><p class="p4"><span class="s4">    </span><span class="s2">return</span><span class="s4"> </span><span class="s2">false</span><span class="s4">;</span></p><p class="p1"><span class="s2">}</span></p>
<p class="p1"><span class="s1">/**</span></p><p class="p1"><span class="s1"> *  @brief register JavascriptJavaBridge to be usable in js</span></p><p class="p1"><span class="s1"> *</span></p><p class="p1"><span class="s1"> */</span></p><p class="p2"><span class="s2">void</span><span class="s3"> </span><span class="s1">JavaScriptObjCBridge</span><span class="s3">::_js_register(</span><span class="s1">JSContext</span><span class="s3"> *cx, </span><span class="s1">JS</span><span class="s3">::</span><span class="s1">HandleObject</span><span class="s3"> global)</span></p><p class="p3"><span class="s1">{</span></p><p class="p3"><span class="s1">    </span><span class="s4">JSClass</span><span class="s1"> jsclass = {</span></p><p class="p2"><span class="s3">        </span><span class="s5">"JavaScriptObjCBridge"</span><span class="s3">, </span><span class="s2">JSCLASS_HAS_PRIVATE</span><span class="s3">, </span><span class="s1">JS_PropertyStub</span><span class="s3">, </span><span class="s1">JS_DeletePropertyStub</span><span class="s3">, </span><span class="s1">JS_PropertyStub</span><span class="s3">, </span><span class="s1">JS_StrictPropertyStub</span><span class="s3">, </span><span class="s1">JS_EnumerateStub</span><span class="s3">, </span><span class="s1">JS_ResolveStub</span><span class="s3">, </span><span class="s1">JS_ConvertStub</span><span class="s3">,</span><span class="s1">basic_object_finalize</span></p><p class="p3"><span class="s1">    };</span></p><p class="p4"><span class="s1">    </span></p><p class="p2"><span class="s3">    </span><span class="s1">JavaScriptObjCBridge</span><span class="s3">::</span><span class="s1">js_class</span><span class="s3"> = jsclass;</span></p><p class="p3"><span class="s1">    </span><span class="s2">static</span><span class="s1"> </span><span class="s4">JSPropertySpec</span><span class="s1"> props[] = {</span></p><p class="p5"><span class="s3">        </span><span class="s1">JS_PSG</span><span class="s3">(</span><span class="s5">"__nativeObj"</span><span class="s3">, </span><span class="s4">js_is_native_obj</span><span class="s3">, </span><span class="s1">JSPROP_PERMANENT</span><span class="s3"> | </span><span class="s1">JSPROP_ENUMERATE</span><span class="s3"> ),</span></p><p class="p5"><span class="s3">        </span><span class="s1">JS_PS_END</span></p><p class="p3"><span class="s1">    };</span></p><p class="p4"><span class="s1">    </span></p><p class="p3"><span class="s1">    </span><span class="s2">static</span><span class="s1"> </span><span class="s4">JSFunctionSpec</span><span class="s1"> funcs[] = {</span></p><p class="p3"><span class="s1">        </span><span class="s2">JS_BINDED_FUNC_FOR_DEF</span><span class="s1">(JavaScriptObjCBridge, callStaticMethod),</span></p><p class="p5"><span class="s3">        </span><span class="s1">JS_FS_END</span></p><p class="p3"><span class="s1">    };</span></p><p class="p4"><span class="s1">    </span></p><p class="p2"><span class="s3">    </span><span class="s1">JavaScriptObjCBridge</span><span class="s3">::</span><span class="s1">js_parent</span><span class="s3"> = </span><span class="s2">NULL</span><span class="s3">;</span></p><p class="p2"><span class="s3">    </span><span class="s1">JavaScriptObjCBridge</span><span class="s3">::</span><span class="s1">js_proto</span><span class="s3"> = </span><span class="s1">JS_InitClass</span><span class="s3">(cx, global, </span><span class="s1">JS</span><span class="s3">::</span><span class="s1">NullPtr</span><span class="s3">(), &</span><span class="s1">JavaScriptObjCBridge</span><span class="s3">::</span><span class="s1">js_class</span><span class="s3"> , </span><span class="s1">JavaScriptObjCBridge</span><span class="s3">::</span><span class="s1">_js_constructor</span><span class="s3">, 0, props, funcs, </span><span class="s2">NULL</span><span class="s3">, </span><span class="s2">NULL</span><span class="s3">);</span></p><p class="p3"><span class="s1">}</span></p><p class="p4"><span class="s1"></span>
</p>