Merge pull request #14213 from pandamicro/v3

Improved JSCallbackWrapper with generational GC and fix the issue
This commit is contained in:
pandamicro 2015-10-22 18:24:42 +08:00
commit 2d75269a98
6 changed files with 149 additions and 306 deletions

View File

@ -878,49 +878,48 @@ jsval anonEvaluate(JSContext *cx, JS::HandleObject thisObj, const char* string)
} }
JSCallbackWrapper::JSCallbackWrapper() JSCallbackWrapper::JSCallbackWrapper()
: _jsCallback(JSVAL_VOID), _jsThisObj(JSVAL_VOID), _extraData(JSVAL_VOID)
{ {
JSContext* cx = ScriptingCore::getInstance()->getGlobalContext();
_jsCallback.construct(cx, JS::NullHandleValue);
_jsThisObj.construct(cx, JS::NullHandleValue);
_extraData.construct(cx, JS::NullHandleValue);
} }
JSCallbackWrapper::~JSCallbackWrapper() JSCallbackWrapper::~JSCallbackWrapper()
{ {
JSContext* cx = ScriptingCore::getInstance()->getGlobalContext(); _jsCallback.destroyIfConstructed();
JS::RemoveValueRoot(cx, &_jsCallback); _jsThisObj.destroyIfConstructed();
JS::RemoveValueRoot(cx, &_jsThisObj); _extraData.destroyIfConstructed();
} }
void JSCallbackWrapper::setJSCallbackFunc(jsval func) { void JSCallbackWrapper::setJSCallbackFunc(JS::HandleValue func) {
_jsCallback = func; if (!func.isNullOrUndefined())
JSContext* cx = ScriptingCore::getInstance()->getGlobalContext(); _jsCallback.ref() = func;
// Root the callback function.
JS::AddNamedValueRoot(cx, &_jsCallback, "JSCallbackWrapper_callback_func");
} }
void JSCallbackWrapper::setJSCallbackThis(jsval thisObj) { void JSCallbackWrapper::setJSCallbackThis(JS::HandleValue thisObj) {
_jsThisObj = thisObj; if (!thisObj.isNullOrUndefined())
JSContext* cx = ScriptingCore::getInstance()->getGlobalContext(); _jsThisObj.ref() = thisObj;
// Root the this object.
JS::AddNamedValueRoot(cx, &_jsThisObj, "JSCallbackWrapper_callback_this");
} }
void JSCallbackWrapper::setJSExtraData(jsval data) { void JSCallbackWrapper::setJSExtraData(JS::HandleValue data) {
_extraData = data; if (!data.isNullOrUndefined())
_extraData.ref() = data;
} }
const jsval& JSCallbackWrapper::getJSCallbackFunc() const const jsval JSCallbackWrapper::getJSCallbackFunc() const
{ {
return _jsCallback.get(); return _jsCallback.ref().get();
} }
const jsval& JSCallbackWrapper::getJSCallbackThis() const const jsval JSCallbackWrapper::getJSCallbackThis() const
{ {
return _jsThisObj.get(); return _jsThisObj.ref().get();
} }
const jsval& JSCallbackWrapper::getJSExtraData() const const jsval JSCallbackWrapper::getJSExtraData() const
{ {
return _extraData.get(); return _extraData.ref().get();
} }
// cc.CallFunc.create( func, this, [data]) // cc.CallFunc.create( func, this, [data])
@ -932,82 +931,52 @@ static bool js_callFunc(JSContext *cx, uint32_t argc, jsval *vp)
std::shared_ptr<JSCallbackWrapper> tmpCobj(new JSCallbackWrapper()); std::shared_ptr<JSCallbackWrapper> tmpCobj(new JSCallbackWrapper());
tmpCobj->setJSCallbackFunc(args.get(0)); JS::RootedValue callback(cx, args.get(0));
tmpCobj->setJSCallbackFunc(callback);
if(argc >= 2) { if(argc >= 2) {
tmpCobj->setJSCallbackThis(args.get(1)); JS::RootedValue thisObj(cx, args.get(1));
} if(argc == 3) { tmpCobj->setJSCallbackThis(thisObj);
tmpCobj->setJSExtraData(args.get(2)); }
if(argc >= 3) {
JS::RootedValue data(cx, args.get(2));
tmpCobj->setJSExtraData(data);
} }
CallFuncN *ret = CallFuncN::create([=](Node* sender){ CallFuncN *ret = CallFuncN::create([=](Node* sender){
// const jsval& jsvalThis = tmpCobj->getJSCallbackThis(); JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
// const jsval& jsvalCallback = tmpCobj->getJSCallbackFunc();
// const jsval& jsvalExtraData = tmpCobj->getJSExtraData();
JS::RootedValue jsvalThis(cx, tmpCobj->getJSCallbackThis()); JS::RootedValue jsvalThis(cx, tmpCobj->getJSCallbackThis());
JS::RootedObject thisObj(cx, jsvalThis.toObjectOrNull());
JS::RootedValue jsvalCallback(cx, tmpCobj->getJSCallbackFunc()); JS::RootedValue jsvalCallback(cx, tmpCobj->getJSCallbackFunc());
JS::RootedValue jsvalExtraData(cx, tmpCobj->getJSExtraData()); JS::RootedValue jsvalExtraData(cx, tmpCobj->getJSExtraData());
bool hasExtraData = !jsvalExtraData.isUndefined(); JS::RootedValue senderVal(cx);
JS::RootedObject thisObj(cx, jsvalThis.toObjectOrNull()); if (sender)
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
if(sender)
{ {
js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, sender); js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, sender);
senderVal.set(OBJECT_TO_JSVAL(proxy->obj));
JS::RootedValue retval(cx);
if(jsvalCallback != JSVAL_VOID)
{
if (hasExtraData)
{
jsval valArr[2];
valArr[0] = OBJECT_TO_JSVAL(proxy->obj);
valArr[1] = jsvalExtraData;
//TODO: really need root?
// JS_AddValueRoot(cx, valArr);
JS::HandleValueArray callArgs = JS::HandleValueArray::fromMarkedLocation(2, valArr);
JS_CallFunctionValue(cx, thisObj, jsvalCallback, callArgs, &retval);
// JS_RemoveValueRoot(cx, valArr);
}
else
{
jsval senderVal = OBJECT_TO_JSVAL(proxy->obj);
JS::HandleValueArray callArgs = JS::HandleValueArray::fromMarkedLocation(1, &senderVal);
// JS_AddValueRoot(cx, &senderVal);
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS_CallFunctionValue(cx, thisObj, jsvalCallback, callArgs, &retval);
// JS_RemoveValueRoot(cx, &senderVal);
}
}
} }
else else
{ {
JS::RootedValue callRet(cx); senderVal.set(JS::NullValue());
JS_CallFunctionValue(cx, thisObj, jsvalCallback, JS::HandleValueArray::empty(), &callRet);
} }
// I think the JSCallFuncWrapper isn't needed. if (!jsvalCallback.isNullOrUndefined())
// Since an action will be run by a cc.Node, it will be released at the Node::cleanup. {
// By James Chen JS::RootedValue retval(cx);
// JSCallFuncWrapper::setTargetForNativeNode(node, (JSCallFuncWrapper *)this);
jsval valArr[2];
valArr[0] = senderVal;
valArr[1] = jsvalExtraData;
JS::HandleValueArray callArgs = JS::HandleValueArray::fromMarkedLocation(2, valArr);
JS_CallFunctionValue(cx, thisObj, jsvalCallback, callArgs, &retval);
}
}); });
js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::CallFunc>(cx, ret); js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::CallFunc>(cx, ret);
args.rval().set(OBJECT_TO_JSVAL(proxy->obj)); args.rval().set(OBJECT_TO_JSVAL(proxy->obj));
JS_SetReservedSlot(proxy->obj, 0, args.get(0));
if(argc > 1) {
JS_SetReservedSlot(proxy->obj, 1, args.get(1));
}
// if(argc == 3) {
// JS_SetReservedSlot(proxy->obj, 2, args.get(2));
// }
// test->execute();
return true; return true;
} }
JS_ReportError(cx, "Invalid number of arguments"); JS_ReportError(cx, "Invalid number of arguments");
@ -1025,90 +994,69 @@ bool js_cocos2dx_CallFunc_initWithFunction(JSContext *cx, uint32_t argc, jsval *
JSB_PRECONDITION2(action, cx, false, "Invalid Native Object"); JSB_PRECONDITION2(action, cx, false, "Invalid Native Object");
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
std::shared_ptr<JSCallbackWrapper> tmpCobj(new JSCallbackWrapper()); std::shared_ptr<JSCallbackWrapper> tmpCobj(new JSCallbackWrapper());
tmpCobj->setJSCallbackFunc(args.get(0)); JS::RootedValue callback(cx, args.get(0));
tmpCobj->setJSCallbackFunc(callback);
if(argc >= 2) { if(argc >= 2) {
tmpCobj->setJSCallbackThis(args.get(1)); JS::RootedValue thisObj(cx, args.get(1));
} if(argc == 3) { tmpCobj->setJSCallbackThis(thisObj);
tmpCobj->setJSExtraData(args.get(2)); }
if(argc >= 3) {
JS::RootedValue data(cx, args.get(2));
tmpCobj->setJSExtraData(data);
} }
action->initWithFunction([=](Node* sender){ action->initWithFunction([=](Node* sender){
// const jsval& jsvalThis = tmpCobj->getJSCallbackThis(); JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
// const jsval& jsvalCallback = tmpCobj->getJSCallbackFunc();
// const jsval& jsvalExtraData = tmpCobj->getJSExtraData();
JS::RootedValue jsvalThis(cx, tmpCobj->getJSCallbackThis()); JS::RootedValue jsvalThis(cx, tmpCobj->getJSCallbackThis());
JS::RootedObject thisObj(cx, jsvalThis.toObjectOrNull());
JS::RootedValue jsvalCallback(cx, tmpCobj->getJSCallbackFunc()); JS::RootedValue jsvalCallback(cx, tmpCobj->getJSCallbackFunc());
JS::RootedValue jsvalExtraData(cx, tmpCobj->getJSExtraData()); JS::RootedValue jsvalExtraData(cx, tmpCobj->getJSExtraData());
bool hasExtraData = !jsvalExtraData.isUndefined(); JS::RootedValue senderVal(cx);
JS::RootedObject thisObj(cx, jsvalThis.toObjectOrNull()); if (sender)
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
if(sender)
{ {
js_proxy_t *senderProxy = js_get_or_create_proxy<cocos2d::Node>(cx, sender); js_proxy_t *senderProxy = js_get_or_create_proxy<cocos2d::Node>(cx, sender);
senderVal.set(OBJECT_TO_JSVAL(senderProxy->obj));
JS::RootedValue retval(cx);
if(jsvalCallback != JSVAL_VOID)
{
if (hasExtraData)
{
jsval valArr[2];
valArr[0] = OBJECT_TO_JSVAL(senderProxy->obj);
valArr[1] = jsvalExtraData;
// JS_AddValueRoot(cx, valArr);
// JS_CallFunctionValue(cx, thisObj, jsvalCallback, 2, valArr, &retval);
// JS_RemoveValueRoot(cx, valArr);
JS::HandleValueArray callArgs = JS::HandleValueArray::fromMarkedLocation(2, valArr);
JS_CallFunctionValue(cx, thisObj, jsvalCallback, callArgs, &retval);
}
else
{
jsval senderVal = OBJECT_TO_JSVAL(senderProxy->obj);
// JS_AddValueRoot(cx, &senderVal);
// JS_CallFunctionValue(cx, thisObj, jsvalCallback, 1, &senderVal, &retval);
// JS_RemoveValueRoot(cx, &senderVal);
JS::HandleValueArray callArgs = JS::HandleValueArray::fromMarkedLocation(1, &senderVal);
JS_CallFunctionValue(cx, thisObj, jsvalCallback, callArgs, &retval);
}
}
} }
else else
{ {
JS::RootedValue ret(cx); senderVal.set(JS::NullValue());
JS_CallFunctionValue(cx, thisObj, jsvalCallback, JS::HandleValueArray::empty(), &ret);
} }
if (!jsvalCallback.isNullOrUndefined())
// I think the JSCallFuncWrapper isn't needed. {
// Since an action will be run by a cc.Node, it will be released at the Node::cleanup. JS::RootedValue retval(cx);
// By James Chen
// JSCallFuncWrapper::setTargetForNativeNode(node, (JSCallFuncWrapper *)this); jsval valArr[2];
valArr[0] = senderVal;
valArr[1] = jsvalExtraData;
JS::HandleValueArray callArgs = JS::HandleValueArray::fromMarkedLocation(2, valArr);
JS_CallFunctionValue(cx, thisObj, jsvalCallback, callArgs, &retval);
}
}); });
JS_SetReservedSlot(proxy->obj, 0, args.get(0));
if(argc > 1) {
JS_SetReservedSlot(proxy->obj, 1, args.get(1));
}
return true; return true;
} }
JS_ReportError(cx, "Invalid number of arguments"); JS_ReportError(cx, "Invalid number of arguments");
return false; return false;
} }
JSScheduleWrapper::JSScheduleWrapper()
: _pTarget(NULL)
, _priority(0)
, _isUpdateSchedule(false)
{
JSContext* cx = ScriptingCore::getInstance()->getGlobalContext();
_pPureJSTarget.construct(cx);
}
JSScheduleWrapper::~JSScheduleWrapper() JSScheduleWrapper::~JSScheduleWrapper()
{ {
if (_pPureJSTarget.get()) { _pPureJSTarget.destroyIfConstructed();
JSContext* cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RemoveObjectRoot(cx, &_pPureJSTarget);
}
} }
void JSScheduleWrapper::setTargetForSchedule(JS::HandleValue sched, JSScheduleWrapper *target) { void JSScheduleWrapper::setTargetForSchedule(JS::HandleValue sched, JSScheduleWrapper *target) {
@ -1398,44 +1346,26 @@ void JSScheduleWrapper::scheduleFunc(float dt)
jsval data = DOUBLE_TO_JSVAL(dt); jsval data = DOUBLE_TO_JSVAL(dt);
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
//XXX: really need root?
// bool ok = JS_AddValueRoot(cx, &data);
// if (!ok) {
// CCLOG("scheduleFunc: Root value fails.");
// return;
// }
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
if(!_jsCallback.isNullOrUndefined()) { JS::RootedValue callback(cx, getJSCallbackFunc());
if(!callback.isNullOrUndefined()) {
JS::HandleValueArray args = JS::HandleValueArray::fromMarkedLocation(1, &data); JS::HandleValueArray args = JS::HandleValueArray::fromMarkedLocation(1, &data);
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
JS_CallFunctionValue(cx, JS::RootedObject(cx, _jsThisObj.toObjectOrNull()), JS::RootedValue(cx, _jsCallback.get()), args, &retval); JS::RootedObject callbackTarget(cx, getJSCallbackThis().toObjectOrNull());
JS_CallFunctionValue(cx, callbackTarget, callback, args, &retval);
} }
// JS_RemoveValueRoot(cx, &data);
} }
void JSScheduleWrapper::update(float dt) void JSScheduleWrapper::update(float dt)
{ {
jsval data = DOUBLE_TO_JSVAL(dt); jsval data = DOUBLE_TO_JSVAL(dt);
//XXX: really need root? ScriptingCore::getInstance()->executeFunctionWithOwner(getJSCallbackThis(), "update", 1, &data);
// JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
// bool ok = JS_AddValueRoot(cx, &data);
// if (!ok) {
// CCLOG("scheduleFunc: Root value fails.");
// return;
// }
ScriptingCore::getInstance()->executeFunctionWithOwner(_jsThisObj, "update", 1, &data);
// JS_RemoveValueRoot(cx, &data);
} }
Ref* JSScheduleWrapper::getTarget() Ref* JSScheduleWrapper::getTarget()
{ {
return _pTarget; return _pTarget;
} }
@ -1447,15 +1377,12 @@ void JSScheduleWrapper::setTarget(Ref* pTarget)
void JSScheduleWrapper::setPureJSTarget(JS::HandleObject pPureJSTarget) void JSScheduleWrapper::setPureJSTarget(JS::HandleObject pPureJSTarget)
{ {
CCASSERT(_pPureJSTarget == NULL, "The pure js target has been set"); _pPureJSTarget.ref() = pPureJSTarget;
JSContext* cx = ScriptingCore::getInstance()->getGlobalContext();
_pPureJSTarget = pPureJSTarget;
JS::AddNamedObjectRoot(cx, &_pPureJSTarget, "Pure JS target");
} }
JSObject* JSScheduleWrapper::getPureJSTarget() JSObject* JSScheduleWrapper::getPureJSTarget()
{ {
return _pPureJSTarget.get(); return _pPureJSTarget.ref().get();
} }
void JSScheduleWrapper::setPriority(int priority) void JSScheduleWrapper::setPriority(int priority)
@ -1550,8 +1477,8 @@ bool js_CCNode_scheduleOnce(JSContext *cx, uint32_t argc, jsval *vp)
bool ok = true; bool ok = true;
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
// JSObject *obj = JS_THIS_OBJECT(cx, vp); JS::RootedValue thisValue(cx, args.thisv());
JS::RootedObject obj(cx, args.thisv().toObjectOrNull()); JS::RootedObject obj(cx, thisValue.toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj); js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Node *node = (cocos2d::Node *)(proxy ? proxy->ptr : NULL); cocos2d::Node *node = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
@ -1586,7 +1513,7 @@ bool js_CCNode_scheduleOnce(JSContext *cx, uint32_t argc, jsval *vp)
{ {
tmpCobj = new JSScheduleWrapper(); tmpCobj = new JSScheduleWrapper();
tmpCobj->autorelease(); tmpCobj->autorelease();
tmpCobj->setJSCallbackThis(OBJECT_TO_JSVAL(obj)); tmpCobj->setJSCallbackThis(thisValue);
tmpCobj->setJSCallbackFunc(args.get(0)); tmpCobj->setJSCallbackFunc(args.get(0));
tmpCobj->setTarget(node); tmpCobj->setTarget(node);
@ -1630,8 +1557,8 @@ bool js_CCNode_schedule(JSContext *cx, uint32_t argc, jsval *vp)
bool ok = true; bool ok = true;
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
// JSObject *obj = JS_THIS_OBJECT(cx, vp); JS::RootedValue thisValue(cx, args.thisv());
JS::RootedObject obj(cx, args.thisv().toObjectOrNull()); JS::RootedObject obj(cx, thisValue.toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj); js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Node *node = (cocos2d::Node *)(proxy ? proxy->ptr : NULL); cocos2d::Node *node = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
Scheduler *sched = node->getScheduler(); Scheduler *sched = node->getScheduler();
@ -1679,7 +1606,7 @@ bool js_CCNode_schedule(JSContext *cx, uint32_t argc, jsval *vp)
{ {
tmpCobj = new JSScheduleWrapper(); tmpCobj = new JSScheduleWrapper();
tmpCobj->autorelease(); tmpCobj->autorelease();
tmpCobj->setJSCallbackThis(OBJECT_TO_JSVAL(obj)); tmpCobj->setJSCallbackThis(thisValue);
tmpCobj->setJSCallbackFunc(args.get(0)); tmpCobj->setJSCallbackFunc(args.get(0));
tmpCobj->setTarget(node); tmpCobj->setTarget(node);
JSScheduleWrapper::setTargetForSchedule(args.get(0), tmpCobj); JSScheduleWrapper::setTargetForSchedule(args.get(0), tmpCobj);
@ -1710,8 +1637,8 @@ bool js_cocos2dx_CCNode_scheduleUpdateWithPriority(JSContext *cx, uint32_t argc,
{ {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
bool ok = true; bool ok = true;
// JSObject *obj = JS_THIS_OBJECT(cx, vp); JS::RootedValue thisValue(cx, args.thisv());
JS::RootedObject obj(cx, args.thisv().toObjectOrNull()); JS::RootedObject obj(cx, thisValue.toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj); js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL); cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object"); JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
@ -1753,7 +1680,7 @@ bool js_cocos2dx_CCNode_scheduleUpdateWithPriority(JSContext *cx, uint32_t argc,
{ {
tmpCobj = new JSScheduleWrapper(); tmpCobj = new JSScheduleWrapper();
tmpCobj->autorelease(); tmpCobj->autorelease();
tmpCobj->setJSCallbackThis(OBJECT_TO_JSVAL(obj)); tmpCobj->setJSCallbackThis(thisValue);
tmpCobj->setJSCallbackFunc(jsUpdateFunc); tmpCobj->setJSCallbackFunc(jsUpdateFunc);
tmpCobj->setTarget(cobj); tmpCobj->setTarget(cobj);
tmpCobj->setUpdateSchedule(true); tmpCobj->setUpdateSchedule(true);
@ -1775,7 +1702,6 @@ bool js_cocos2dx_CCNode_scheduleUpdateWithPriority(JSContext *cx, uint32_t argc,
bool js_cocos2dx_CCNode_unscheduleUpdate(JSContext *cx, uint32_t argc, jsval *vp) bool js_cocos2dx_CCNode_unscheduleUpdate(JSContext *cx, uint32_t argc, jsval *vp)
{ {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
// JSObject *obj = JS_THIS_OBJECT(cx, vp);
JS::RootedObject obj(cx, args.thisv().toObjectOrNull()); JS::RootedObject obj(cx, args.thisv().toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj); js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL); cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
@ -1785,8 +1711,6 @@ bool js_cocos2dx_CCNode_unscheduleUpdate(JSContext *cx, uint32_t argc, jsval *vp
{ {
cobj->unscheduleUpdate(); cobj->unscheduleUpdate();
do { do {
// JSObject *tmpObj = obj;
__Array *arr = JSScheduleWrapper::getTargetForJSObject(obj); __Array *arr = JSScheduleWrapper::getTargetForJSObject(obj);
// If there aren't any targets, just return true. // If there aren't any targets, just return true.
// Otherwise, the for loop will break immediately. // Otherwise, the for loop will break immediately.
@ -1817,8 +1741,8 @@ bool js_cocos2dx_CCNode_scheduleUpdate(JSContext *cx, uint32_t argc, jsval *vp)
{ {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
bool ok = true; bool ok = true;
// JSObject *obj = JS_THIS_OBJECT(cx, vp); JS::RootedValue thisValue(cx, args.thisv());
JS::RootedObject obj(cx, args.thisv().toObjectOrNull()); JS::RootedObject obj(cx, thisValue.toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj); js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL); cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object"); JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object");
@ -1857,7 +1781,7 @@ bool js_cocos2dx_CCNode_scheduleUpdate(JSContext *cx, uint32_t argc, jsval *vp)
{ {
tmpCobj = new JSScheduleWrapper(); tmpCobj = new JSScheduleWrapper();
tmpCobj->autorelease(); tmpCobj->autorelease();
tmpCobj->setJSCallbackThis(OBJECT_TO_JSVAL(obj)); tmpCobj->setJSCallbackThis(thisValue);
tmpCobj->setJSCallbackFunc(jsUpdateFunc); tmpCobj->setJSCallbackFunc(jsUpdateFunc);
tmpCobj->setTarget(cobj); tmpCobj->setTarget(cobj);
tmpCobj->setUpdateSchedule(true); tmpCobj->setUpdateSchedule(true);

View File

@ -124,24 +124,24 @@ class JSCallbackWrapper: public cocos2d::Ref {
public: public:
JSCallbackWrapper(); JSCallbackWrapper();
virtual ~JSCallbackWrapper(); virtual ~JSCallbackWrapper();
void setJSCallbackFunc(jsval obj); void setJSCallbackFunc(JS::HandleValue callback);
void setJSCallbackThis(jsval thisObj); void setJSCallbackThis(JS::HandleValue thisObj);
void setJSExtraData(jsval data); void setJSExtraData(JS::HandleValue data);
const jsval& getJSCallbackFunc() const; const jsval getJSCallbackFunc() const;
const jsval& getJSCallbackThis() const; const jsval getJSCallbackThis() const;
const jsval& getJSExtraData() const; const jsval getJSExtraData() const;
protected: protected:
JS::Heap<JS::Value> _jsCallback; mozilla::Maybe<JS::PersistentRootedValue> _jsCallback;
JS::Heap<JS::Value> _jsThisObj; mozilla::Maybe<JS::PersistentRootedValue> _jsThisObj;
JS::Heap<JS::Value> _extraData; mozilla::Maybe<JS::PersistentRootedValue> _extraData;
}; };
class JSScheduleWrapper: public JSCallbackWrapper { class JSScheduleWrapper: public JSCallbackWrapper {
public: public:
JSScheduleWrapper() : _pTarget(NULL), _pPureJSTarget(NULL), _priority(0), _isUpdateSchedule(false) {} JSScheduleWrapper();
virtual ~JSScheduleWrapper(); virtual ~JSScheduleWrapper();
static void setTargetForSchedule(JS::HandleValue sched, JSScheduleWrapper *target); static void setTargetForSchedule(JS::HandleValue sched, JSScheduleWrapper *target);
@ -178,7 +178,7 @@ public:
protected: protected:
Ref* _pTarget; Ref* _pTarget;
JS::Heap<JSObject*> _pPureJSTarget; mozilla::Maybe<JS::PersistentRootedObject> _pPureJSTarget;
int _priority; int _priority;
bool _isUpdateSchedule; bool _isUpdateSchedule;
}; };

View File

@ -32,18 +32,19 @@ public:
JSCCBAnimationWrapper() {} JSCCBAnimationWrapper() {}
virtual ~JSCCBAnimationWrapper() {} virtual ~JSCCBAnimationWrapper() {}
void animationCompleteCallback() void animationCompleteCallback()
{ {
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
if(!_jsCallback.isNullOrUndefined() && !_jsThisObj.isNullOrUndefined()) JS::RootedValue callback(cx, getJSCallbackFunc());
JS::RootedValue thisObj(cx, getJSCallbackThis());
if(!callback.isNullOrUndefined() && !thisObj.isNullOrUndefined())
{ {
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS_CallFunctionValue(cx, JS::RootedObject(cx, _jsThisObj.toObjectOrNull()), JS::RootedValue(cx, _jsCallback), JS::HandleValueArray::empty(), &retval); JS_CallFunctionValue(cx, JS::RootedObject(cx, thisObj.toObjectOrNull()), JS::RootedValue(cx, callback), JS::HandleValueArray::empty(), &retval);
} }
} }

View File

@ -28,55 +28,19 @@
class JSArmatureWrapper: public JSCallbackWrapper { class JSArmatureWrapper: public JSCallbackWrapper {
public: public:
JSArmatureWrapper();
virtual ~JSArmatureWrapper();
virtual void setJSCallbackThis(jsval thisObj);
void movementCallbackFunc(cocostudio::Armature *armature, cocostudio::MovementEventType movementType, const std::string& movementID); void movementCallbackFunc(cocostudio::Armature *armature, cocostudio::MovementEventType movementType, const std::string& movementID);
void frameCallbackFunc(cocostudio::Bone *bone, const std::string& evt, int originFrameIndex, int currentFrameIndex); void frameCallbackFunc(cocostudio::Bone *bone, const std::string& evt, int originFrameIndex, int currentFrameIndex);
void addArmatureFileInfoAsyncCallbackFunc(float percent); void addArmatureFileInfoAsyncCallbackFunc(float percent);
private:
bool m_bNeedUnroot;
}; };
JSArmatureWrapper::JSArmatureWrapper()
: m_bNeedUnroot(false)
{
}
JSArmatureWrapper::~JSArmatureWrapper()
{
if (m_bNeedUnroot)
{
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RemoveValueRoot(cx, &_jsThisObj);
}
}
void JSArmatureWrapper::setJSCallbackThis(jsval obj)
{
JSCallbackWrapper::setJSCallbackThis(obj);
JSObject *thisObj = obj.toObjectOrNull();
js_proxy *p = jsb_get_js_proxy(thisObj);
if (!p)
{
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
m_bNeedUnroot = true;
m_bNeedUnroot &= JS::AddValueRoot(cx, &_jsThisObj);
}
}
void JSArmatureWrapper::movementCallbackFunc(cocostudio::Armature *armature, cocostudio::MovementEventType movementType, const std::string& movementID) void JSArmatureWrapper::movementCallbackFunc(cocostudio::Armature *armature, cocostudio::MovementEventType movementType, const std::string& movementID)
{ {
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RootedObject thisObj(cx, _jsThisObj.toObjectOrNull()); JS::RootedObject thisObj(cx, getJSCallbackThis().toObjectOrNull());
js_proxy_t *proxy = js_get_or_create_proxy(cx, armature); js_proxy_t *proxy = js_get_or_create_proxy(cx, armature);
JS::RootedValue callback(cx, getJSCallbackFunc());
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
if (_jsCallback != JSVAL_VOID) if (!callback.isNullOrUndefined())
{ {
int movementEventType = (int)movementType; int movementEventType = (int)movementType;
jsval movementVal = INT_TO_JSVAL(movementEventType); jsval movementVal = INT_TO_JSVAL(movementEventType);
@ -87,31 +51,26 @@ void JSArmatureWrapper::movementCallbackFunc(cocostudio::Armature *armature, coc
valArr[0] = OBJECT_TO_JSVAL(proxy->obj); valArr[0] = OBJECT_TO_JSVAL(proxy->obj);
valArr[1] = movementVal; valArr[1] = movementVal;
valArr[2] = idVal; valArr[2] = idVal;
//JS_AddValueRoot(cx, valArr);
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS_CallFunctionValue(cx, thisObj, JS::RootedValue(cx, _jsCallback), JS::HandleValueArray::fromMarkedLocation(3, valArr), &retval); JS_CallFunctionValue(cx, thisObj, callback, JS::HandleValueArray::fromMarkedLocation(3, valArr), &retval);
//JS_RemoveValueRoot(cx, valArr);
} }
} }
void JSArmatureWrapper::addArmatureFileInfoAsyncCallbackFunc(float percent) void JSArmatureWrapper::addArmatureFileInfoAsyncCallbackFunc(float percent)
{ {
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RootedObject thisObj(cx, _jsThisObj.toObjectOrNull()); JS::RootedObject thisObj(cx, getJSCallbackThis().toObjectOrNull());
JS::RootedValue callback(cx, getJSCallbackFunc());
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
if (_jsCallback != JSVAL_VOID) if (!callback.isNullOrUndefined())
{ {
jsval percentVal = DOUBLE_TO_JSVAL(percent); jsval percentVal = DOUBLE_TO_JSVAL(percent);
//JS_AddValueRoot(cx, &percentVal);
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS_CallFunctionValue(cx, thisObj, JS::RootedValue(cx, _jsCallback), JS::HandleValueArray::fromMarkedLocation(1, &percentVal), &retval); JS_CallFunctionValue(cx, thisObj, callback, JS::HandleValueArray::fromMarkedLocation(1, &percentVal), &retval);
//JS_RemoveValueRoot(cx, &percentVal);
} }
} }
@ -121,10 +80,11 @@ void JSArmatureWrapper::frameCallbackFunc(cocostudio::Bone *bone, const std::str
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RootedObject thisObj(cx, _jsThisObj.toObjectOrNull()); JS::RootedObject thisObj(cx, getJSCallbackThis().toObjectOrNull());
JS::RootedValue callback(cx, getJSCallbackFunc());
js_proxy_t *proxy = js_get_or_create_proxy(cx, bone); js_proxy_t *proxy = js_get_or_create_proxy(cx, bone);
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
if (_jsCallback != JSVAL_VOID) if (!callback.isNullOrUndefined())
{ {
jsval nameVal = std_string_to_jsval(cx, evt); jsval nameVal = std_string_to_jsval(cx, evt);
jsval originIndexVal = INT_TO_JSVAL(originFrameIndex); jsval originIndexVal = INT_TO_JSVAL(originFrameIndex);
@ -135,11 +95,8 @@ void JSArmatureWrapper::frameCallbackFunc(cocostudio::Bone *bone, const std::str
valArr[1] = nameVal; valArr[1] = nameVal;
valArr[2] = originIndexVal; valArr[2] = originIndexVal;
valArr[3] = currentIndexVal; valArr[3] = currentIndexVal;
//JS_AddValueRoot(cx, valArr);
JS_CallFunctionValue(cx, thisObj, JS::RootedValue(cx, _jsCallback), JS::HandleValueArray::fromMarkedLocation(4, valArr), &retval); JS_CallFunctionValue(cx, thisObj, callback, JS::HandleValueArray::fromMarkedLocation(4, valArr), &retval);
//JS_RemoveValueRoot(cx, valArr);
} }
} }
@ -173,7 +130,8 @@ static bool js_cocos2dx_ArmatureAnimation_setMovementEventCallFunc(JSContext *cx
tmpObj->setJSCallbackFunc(args.get(0)); tmpObj->setJSCallbackFunc(args.get(0));
if (argc == 1) if (argc == 1)
{ {
tmpObj->setJSCallbackThis(JSVAL_NULL); JS::RootedValue nullVal(cx, JS::NullValue());
tmpObj->setJSCallbackThis(nullVal);
} }
else else
{ {
@ -219,7 +177,8 @@ static bool js_cocos2dx_ArmatureAnimation_setFrameEventCallFunc(JSContext *cx, u
tmpObj->setJSCallbackFunc(args.get(0)); tmpObj->setJSCallbackFunc(args.get(0));
if (argc == 1) if (argc == 1)
{ {
tmpObj->setJSCallbackThis(JSVAL_NULL); JS::RootedValue nullVal(cx, JS::NullValue());
tmpObj->setJSCallbackThis(nullVal);
} }
else else
{ {
@ -417,7 +376,7 @@ static bool js_cocos2dx_studio_Frame_getEasingParams(JSContext *cx, uint32_t arg
bool ok = true; bool ok = true;
for(size_t i = 0; i < ret.size(); ++i) for(size_t i = 0; i < ret.size(); ++i)
{ {
ok &= JS_SetElement(cx, jsobj, i, ret[i]); ok &= JS_SetElement(cx, jsobj, (int)i, ret[i]);
} }
JSB_PRECONDITION2(ok, cx, false, "js_cocos2dx_studio_Frame_getEasingParams : Error processing arguments"); JSB_PRECONDITION2(ok, cx, false, "js_cocos2dx_studio_Frame_getEasingParams : Error processing arguments");

View File

@ -564,10 +564,11 @@ public:
void animationCallbackFunc(spine::SkeletonAnimation* node, int trackIndex, spEventType type, spEvent* event, int loopCount) const { void animationCallbackFunc(spine::SkeletonAnimation* node, int trackIndex, spEventType type, spEvent* event, int loopCount) const {
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RootedObject thisObj(cx, _jsThisObj.toObjectOrNull()); JS::RootedObject thisObj(cx, getJSCallbackThis().toObjectOrNull());
JS::RootedValue callback(cx, getJSCallbackFunc());
js_proxy_t *proxy = js_get_or_create_proxy(cx, node); js_proxy_t *proxy = js_get_or_create_proxy(cx, node);
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
if (_jsCallback != JSVAL_VOID) if (!callback.isNullOrUndefined())
{ {
jsval nodeVal = OBJECT_TO_JSVAL(proxy->obj); jsval nodeVal = OBJECT_TO_JSVAL(proxy->obj);
jsval trackIndexVal = INT_TO_JSVAL(trackIndex); jsval trackIndexVal = INT_TO_JSVAL(trackIndex);
@ -585,10 +586,8 @@ public:
valArr[3] = eventVal; valArr[3] = eventVal;
valArr[4] = loopCountVal; valArr[4] = loopCountVal;
//JS_AddValueRoot(cx, valArr);
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS_CallFunctionValue(cx, thisObj, JS::RootedValue(cx, _jsCallback), JS::HandleValueArray::fromMarkedLocation(5, valArr), &retval); JS_CallFunctionValue(cx, thisObj, callback, JS::HandleValueArray::fromMarkedLocation(5, valArr), &retval);
//JS_RemoveValueRoot(cx, valArr);
} }
} }
}; };

View File

@ -31,53 +31,17 @@ using namespace cocos2d::ui;
class JSStudioEventListenerWrapper: public JSCallbackWrapper { class JSStudioEventListenerWrapper: public JSCallbackWrapper {
public: public:
JSStudioEventListenerWrapper();
virtual ~JSStudioEventListenerWrapper();
virtual void setJSCallbackThis(jsval thisObj);
virtual void eventCallbackFunc(Ref*,int); virtual void eventCallbackFunc(Ref*,int);
private:
bool m_bNeedUnroot;
}; };
JSStudioEventListenerWrapper::JSStudioEventListenerWrapper()
: m_bNeedUnroot(false)
{
}
JSStudioEventListenerWrapper::~JSStudioEventListenerWrapper()
{
if (m_bNeedUnroot)
{
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RemoveValueRoot(cx, &_jsThisObj);
}
}
void JSStudioEventListenerWrapper::setJSCallbackThis(jsval jsThisObj)
{
JSCallbackWrapper::setJSCallbackThis(jsThisObj);
JSObject *thisObj = jsThisObj.toObjectOrNull();
js_proxy *p = jsb_get_js_proxy(thisObj);
if (!p)
{
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
m_bNeedUnroot = true;
m_bNeedUnroot &= JS::AddValueRoot(cx, &_jsThisObj);
}
}
void JSStudioEventListenerWrapper::eventCallbackFunc(Ref* sender,int eventType) void JSStudioEventListenerWrapper::eventCallbackFunc(Ref* sender,int eventType)
{ {
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RootedObject thisObj(cx, _jsThisObj.isNullOrUndefined() ? NULL : _jsThisObj.toObjectOrNull()); JS::RootedObject thisObj(cx, getJSCallbackThis().toObjectOrNull());
JS::RootedValue callback(cx, getJSCallbackFunc());
js_proxy_t *proxy = js_get_or_create_proxy(cx, sender); js_proxy_t *proxy = js_get_or_create_proxy(cx, sender);
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
if (!_jsCallback.isNullOrUndefined()) if (!callback.isNullOrUndefined())
{ {
jsval touchVal = INT_TO_JSVAL(eventType); jsval touchVal = INT_TO_JSVAL(eventType);
@ -85,12 +49,8 @@ void JSStudioEventListenerWrapper::eventCallbackFunc(Ref* sender,int eventType)
valArr[0] = OBJECT_TO_JSVAL(proxy->obj); valArr[0] = OBJECT_TO_JSVAL(proxy->obj);
valArr[1] = touchVal; valArr[1] = touchVal;
//JS::AddValueRoot(cx, valArr);
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS_CallFunctionValue(cx, thisObj, callback, JS::HandleValueArray::fromMarkedLocation(2, valArr), &retval);
JS_CallFunctionValue(cx, thisObj, JS::RootedValue(cx, _jsCallback), JS::HandleValueArray::fromMarkedLocation(2, valArr), &retval);
//JS::RemoveValueRoot(cx, valArr);
} }
} }