Implement full GC relied memory model

This commit is contained in:
pandamicro 2015-12-17 21:53:40 +08:00
parent 022bcc24f1
commit e1bc6640f4
7 changed files with 604 additions and 532 deletions

View File

@ -92,18 +92,6 @@ void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
++_referenceCount;
#if CC_ENABLE_SCRIPT_BINDING && CC_ENABLE_GC_FOR_NATIVE_OBJECTS
if (_scriptOwned && !_rooted)
{
auto scriptMgr = ScriptEngineManager::getInstance()->getScriptEngine();
if (scriptMgr && scriptMgr->getScriptType() == kScriptTypeJavascript)
{
_referenceCountAtRootTime = _referenceCount-1;
scriptMgr->rootObject(this);
}
}
#endif // CC_ENABLE_SCRIPT_BINDING
}
void Ref::release()
@ -111,17 +99,6 @@ void Ref::release()
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
--_referenceCount;
#if CC_ENABLE_SCRIPT_BINDING && CC_ENABLE_GC_FOR_NATIVE_OBJECTS
if (_scriptOwned && _rooted && _referenceCount==/*_referenceCountAtRootTime*/ 1)
{
auto scriptMgr = ScriptEngineManager::getInstance()->getScriptEngine();
if (scriptMgr && scriptMgr->getScriptType() == kScriptTypeJavascript)
{
scriptMgr->unrootObject(this);
}
}
#endif // CC_ENABLE_SCRIPT_BINDING
if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)

View File

@ -642,6 +642,36 @@ public:
*/
virtual ccScriptType getScriptType() { return kScriptTypeNone; };
/**
* Reflect the retain relationship to script scope
*/
virtual void retainScriptObject(Ref* owner, Ref* target) = 0;
/**
* Add the script object to root object
*/
virtual void rootScriptObject(Ref* target) = 0;
/**
* Reflect the release relationship to script scope
*/
virtual void releaseScriptObject(Ref* owner, Ref* target) = 0;
/**
* Remove the script object from root object
*/
virtual void unrootScriptObject(Ref* target) = 0;
/**
* Release all children native refs for the given node in script scope
*/
virtual void releaseAllChildrenRecursive(Node* node) = 0;
/**
* Release all native refs for the given owner in script scope
*/
virtual void releaseAllNativeRefs(cocos2d::Ref* owner) = 0;
/**
* Remove script object,The specific meaning should refer to the ScriptType.
* For Lua, @see removeScriptObjectByObject of LuaEngine.

View File

@ -72,16 +72,20 @@
#define TRACE_DEBUGGER_SERVER(...) CCLOG(__VA_ARGS__)
#else
#define TRACE_DEBUGGER_SERVER(...)
#endif // #if DEBUG
#endif // #if COCOS2D_DEBUG
#define BYTE_CODE_FILE_EXT ".jsc"
// EXPERIMENTAL: Enable this in order to get rid of retain/release
// when using the Garbage Collector
#define CC_ENABLE_GC_FOR_NATIVE_OBJECTS 0
#define CC_ENABLE_GC_FOR_NATIVE_OBJECTS 1
using namespace cocos2d;
#if COCOS2D_DEBUG
int ScriptingCore::retainCount = 0;
#endif //COCOS2D_DEBUG
static std::string inData;
static std::string outData;
static std::vector<std::string> g_queue;
@ -98,7 +102,7 @@ static void serverEntryPoint(unsigned int port);
std::unordered_map<std::string, js_type_class_t*> _js_global_type_map;
static std::unordered_map<void*, js_proxy_t*> _native_js_global_map;
static std::unordered_map<JSObject*, js_proxy_t*> _js_native_global_map;
std::unordered_map<JSObject*, JSObject*> _js_hook_owner_map;
static char *_js_log_buf = NULL;
@ -215,22 +219,18 @@ static std::string getMouseFuncName(EventMouse::MouseEventType eventType)
return funcName;
}
static void removeJSObject(JSContext* cx, cocos2d::Ref* nativeObj)
void removeJSObject(JSContext* cx, cocos2d::Ref* nativeObj)
{
auto proxy = jsb_get_native_proxy(nativeObj);
if (proxy)
{
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
#if not CC_ENABLE_GC_FOR_NATIVE_OBJECTS
JS::RemoveObjectRoot(cx, &proxy->obj);
#endif
// remove the proxy here, since this was a "stack" object, not heap
// when js_finalize will be called, it will fail, but
// the correct solution is to have a new finalize for event
jsb_remove_proxy(proxy);
#else
// only remove when not using GC,
// otherwise finalize won't be able to find the proxy
JS::RemoveObjectRoot(cx, &proxy->obj);
jsb_remove_proxy(proxy);
#endif
}
else CCLOG("removeJSObject: BUG: cannot find native object = %p", nativeObj);
}
@ -421,8 +421,6 @@ void registerDefaultClasses(JSContext* cx, JS::HandleObject global) {
JS_DefineFunction(cx, jsc, "garbageCollect", ScriptingCore::forceGC, 0, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE );
JS_DefineFunction(cx, jsc, "dumpRoot", ScriptingCore::dumpRoot, 0, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE );
JS_DefineFunction(cx, jsc, "addGCRootObject", ScriptingCore::addRootJS, 1, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE );
JS_DefineFunction(cx, jsc, "removeGCRootObject", ScriptingCore::removeRootJS, 1, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE );
JS_DefineFunction(cx, jsc, "executeScript", ScriptingCore::executeScript, 1, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE );
// register some global functions
@ -439,9 +437,8 @@ void registerDefaultClasses(JSContext* cx, JS::HandleObject global) {
JS_DefineFunction(cx, global, "__isObjectValid", ScriptingCore::isObjectValid, 1, JSPROP_READONLY | JSPROP_PERMANENT);
}
static void sc_finalize(JSFreeOp *freeOp, JSObject *obj)
{
CCLOG("jsbindings: finalizing JS object %p (global class)", obj);
static void sc_finalize(JSFreeOp *freeOp, JSObject *obj) {
CCLOGINFO("jsbindings: finalizing JS object %p (global class)", obj);
}
//static JSClass global_class = {
@ -618,11 +615,11 @@ void ScriptingCore::createGlobalContext() {
_global.construct(_cx);
_global.ref() = NewGlobalObject(_cx);
JSAutoCompartment ac(_cx, _global.ref());
// Removed in Firefox v34
js::SetDefaultObjectForContext(_cx, _global.ref());
JSAutoCompartment ac(_cx, _global.ref());
runScript("script/jsb_prepare.js");
for (std::vector<sc_register_sth>::iterator it = registrationList.begin(); it != registrationList.end(); it++) {
@ -876,9 +873,163 @@ bool ScriptingCore::log(JSContext* cx, uint32_t argc, jsval *vp)
}
void ScriptingCore::removeScriptObjectByObject(cocos2d::Ref* nativeObj)
void ScriptingCore::retainScriptObject(cocos2d::Ref* owner, cocos2d::Ref* target)
{
auto proxy = jsb_get_native_proxy(nativeObj);
JS::RootedObject global(_cx, _global.ref());
JS::RootedObject jsbObj(_cx);
get_or_create_js_obj(_cx, global, "jsb", &jsbObj);
JS::RootedValue jsbVal(_cx, OBJECT_TO_JSVAL(jsbObj));
if (jsbVal.isNullOrUndefined())
{
return;
}
js_proxy_t *pOwner = jsb_get_native_proxy(owner);
js_proxy_t *pTarget = jsb_get_native_proxy(target);
if (!pOwner || !pTarget)
{
return;
}
JS::RootedValue valOwner(_cx, OBJECT_TO_JSVAL(pOwner->obj));
JS::RootedValue valTarget(_cx, OBJECT_TO_JSVAL(pTarget->obj));
JS::RootedValue retval(_cx);
jsval valArr[2];
valArr[0] = valOwner;
valArr[1] = valTarget;
JS::HandleValueArray args = JS::HandleValueArray::fromMarkedLocation(2, valArr);
executeFunctionWithOwner(jsbVal, "registerNativeRef", args, &retval);
}
void ScriptingCore::rootScriptObject(cocos2d::Ref* target)
{
JS::RootedObject global(_cx, _global.ref());
JS::RootedObject jsbObj(_cx);
get_or_create_js_obj(_cx, global, "jsb", &jsbObj);
JS::RootedValue jsbVal(_cx, OBJECT_TO_JSVAL(jsbObj));
if (jsbVal.isNullOrUndefined())
{
return;
}
js_proxy_t *pTarget = jsb_get_native_proxy(target);
if (!pTarget)
{
return;
}
JS::RootedValue valTarget(_cx, OBJECT_TO_JSVAL(pTarget->obj));
JS::RootedObject root(_cx);
get_or_create_js_obj(_cx, jsbObj, "_root", &root);
JS::RootedValue valRoot(_cx, OBJECT_TO_JSVAL(root));
JS::RootedValue retval(_cx);
jsval valArr[2];
valArr[0] = valRoot;
valArr[1] = valTarget;
JS::HandleValueArray args = JS::HandleValueArray::fromMarkedLocation(2, valArr);
executeFunctionWithOwner(jsbVal, "registerNativeRef", args, &retval);
}
void ScriptingCore::releaseScriptObject(cocos2d::Ref* owner, cocos2d::Ref* target)
{
JS::RootedObject global(_cx, _global.ref());
JS::RootedObject jsbObj(_cx);
get_or_create_js_obj(_cx, global, "jsb", &jsbObj);
JS::RootedValue jsbVal(_cx, OBJECT_TO_JSVAL(jsbObj));
if (jsbVal.isNullOrUndefined())
{
return;
}
js_proxy_t *pOwner = jsb_get_native_proxy(owner);
js_proxy_t *pTarget = jsb_get_native_proxy(target);
if (!pOwner || !pTarget)
{
return;
}
JS::RootedValue valOwner(_cx, OBJECT_TO_JSVAL(pOwner->obj));
JS::RootedValue valTarget(_cx, OBJECT_TO_JSVAL(pTarget->obj));
JS::RootedValue retval(_cx);
jsval valArr[2];
valArr[0] = valOwner;
valArr[1] = valTarget;
JS::HandleValueArray args = JS::HandleValueArray::fromMarkedLocation(2, valArr);
executeFunctionWithOwner(jsbVal, "unregisterNativeRef", args, &retval);
}
void ScriptingCore::unrootScriptObject(cocos2d::Ref* target)
{
JS::RootedObject global(_cx, _global.ref());
JS::RootedObject jsbObj(_cx);
get_or_create_js_obj(_cx, global, "jsb", &jsbObj);
JS::RootedValue jsbVal(_cx, OBJECT_TO_JSVAL(jsbObj));
if (jsbVal.isNullOrUndefined())
{
return;
}
js_proxy_t *pTarget = jsb_get_native_proxy(target);
if (!pTarget)
{
return;
}
JS::RootedValue valTarget(_cx, OBJECT_TO_JSVAL(pTarget->obj));
JS::RootedObject root(_cx);
get_or_create_js_obj(_cx, jsbObj, "_root", &root);
JS::RootedValue valRoot(_cx, OBJECT_TO_JSVAL(root));
JS::RootedValue retval(_cx);
jsval valArr[2];
valArr[0] = valRoot;
valArr[1] = valTarget;
JS::HandleValueArray args = JS::HandleValueArray::fromMarkedLocation(2, valArr);
executeFunctionWithOwner(jsbVal, "unregisterNativeRef", args, &retval);
}
void ScriptingCore::releaseAllChildrenRecursive(cocos2d::Node *node)
{
const Vector<Node*>& children = node->getChildren();
for (auto child : children)
{
releaseScriptObject(node, child);
releaseAllChildrenRecursive(child);
}
}
void ScriptingCore::releaseAllNativeRefs(cocos2d::Ref* owner)
{
JS::RootedObject global(_cx, _global.ref());
JS::RootedObject jsbObj(_cx);
get_or_create_js_obj(_cx, global, "jsb", &jsbObj);
JS::RootedValue jsbVal(_cx, OBJECT_TO_JSVAL(jsbObj));
if (jsbVal.isNullOrUndefined())
{
return;
}
js_proxy_t *pOwner = jsb_get_native_proxy(owner);
if (!pOwner)
{
return;
}
JS::RootedValue valOwner(_cx, OBJECT_TO_JSVAL(pOwner->obj));
JS::RootedValue retval(_cx);
jsval valArr[1];
valArr[0] = valOwner;
JS::HandleValueArray args = JS::HandleValueArray::fromMarkedLocation(1, valArr);
executeFunctionWithOwner(jsbVal, "unregisterAllNativeRefs", args, &retval);
}
void ScriptingCore::removeScriptObjectByObject(Ref* pObj)
{
auto proxy = jsb_get_native_proxy(pObj);
if (proxy)
{
JSContext *cx = getGlobalContext();
@ -950,33 +1101,6 @@ bool ScriptingCore::dumpRoot(JSContext *cx, uint32_t argc, jsval *vp)
return true;
}
bool ScriptingCore::addRootJS(JSContext *cx, uint32_t argc, jsval *vp)
{
if (argc == 1) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
JS::Heap<JSObject*> o(args.get(0).toObjectOrNull());
if (AddNamedObjectRoot(cx, &o, "from-js") == false) {
LOGD("something went wrong when setting an object to the root");
}
return true;
}
return false;
}
bool ScriptingCore::removeRootJS(JSContext *cx, uint32_t argc, jsval *vp)
{
if (argc == 1) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
JS::Heap<JSObject*> o(args.get(0).toObjectOrNull());
if (o != nullptr) {
JS::RemoveObjectRoot(cx, &o);
}
return true;
}
return false;
}
void ScriptingCore::pauseSchedulesAndActions(js_proxy_t* p)
{
JS::RootedObject obj(_cx, p->obj.get());
@ -1024,6 +1148,7 @@ void ScriptingCore::cleanupSchedulesAndActions(js_proxy_t* p)
bool ScriptingCore::isFunctionOverridedInJS(JS::HandleObject obj, const std::string& name, JSNative native)
{
JSAutoCompartment ac(_cx, obj);
JS::RootedValue value(_cx);
bool ok = JS_GetProperty(_cx, obj, name.c_str(), &value);
if (ok && !value.isNullOrUndefined() && !JS_IsNativeFunction(value.toObjectOrNull(), native))
@ -1191,11 +1316,8 @@ bool ScriptingCore::handleTouchesEvent(void* nativeObj, cocos2d::EventTouch::Eve
JSAutoCompartment ac(_cx, _global.ref());
bool ret = false;
std::string funcName = getTouchesFuncName(eventCode);
JS::RootedObject jsretArr(_cx, JS_NewArrayObject(this->_cx, 0));
JS::RootedObject jsretArr(_cx, JS_NewArrayObject(_cx, 0));
int count = 0;
js_type_class_t *typeClassEvent = nullptr;
@ -1203,12 +1325,12 @@ bool ScriptingCore::handleTouchesEvent(void* nativeObj, cocos2d::EventTouch::Eve
if (touches.size()>0)
typeClassTouch = js_get_type_from_native<cocos2d::Touch>(touches[0]);
typeClassEvent = js_get_type_from_native<cocos2d::Event>(event);
typeClassEvent = js_get_type_from_native<cocos2d::EventTouch>((cocos2d::EventTouch*)event);
for (const auto& touch : touches)
{
JS::RootedValue jsret(_cx, OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, touch, typeClassTouch, "cocos2d::Touch")));
if (!JS_SetElement(this->_cx, jsretArr, count, jsret))
JS::RootedValue jsret(_cx, OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, touch, typeClassTouch, "cocos2d::Touch")));
if (!JS_SetElement(_cx, jsretArr, count, jsret))
{
break;
}
@ -1220,13 +1342,13 @@ bool ScriptingCore::handleTouchesEvent(void* nativeObj, cocos2d::EventTouch::Eve
{
jsval dataVal[2];
dataVal[0] = OBJECT_TO_JSVAL(jsretArr);
dataVal[1] = OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, event, typeClassEvent, "cocos2d::Event"));
dataVal[1] = OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, event, typeClassEvent, "cocos2d::EventTouch"));
ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), funcName.c_str(), 2, dataVal, jsvalRet);
}
for (auto& touch : touches)
{
removeJSObject(this->_cx, touch);
removeJSObject(_cx, touch);
}
removeJSObject(_cx, event);
@ -1251,11 +1373,11 @@ bool ScriptingCore::handleTouchEvent(void* nativeObj, cocos2d::EventTouch::Event
if (p)
{
js_type_class_t *typeClassTouch = js_get_type_from_native<cocos2d::Touch>(touch);
js_type_class_t *typeClassEvent = js_get_type_from_native<cocos2d::Event>(event);
js_type_class_t *typeClassEvent = js_get_type_from_native<cocos2d::EventTouch>((cocos2d::EventTouch*)event);
jsval dataVal[2];
dataVal[0] = OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, touch, typeClassTouch, "cocos2d::Touch"));
dataVal[1] = OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, event, typeClassEvent, "cocos2d::Event"));
dataVal[0] = OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, touch, typeClassTouch, "cocos2d::Touch"));
dataVal[1] = OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, event, typeClassEvent, "cocos2d::EventTouch"));
ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), funcName.c_str(), 2, dataVal, jsvalRet);
}
@ -1274,7 +1396,7 @@ bool ScriptingCore::handleMouseEvent(void* nativeObj, cocos2d::EventMouse::Mouse
bool ScriptingCore::handleMouseEvent(void* nativeObj, cocos2d::EventMouse::MouseEventType eventType, cocos2d::Event* event, JS::MutableHandleValue jsvalRet)
{
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JSAutoCompartment ac(_cx, _global.ref());
std::string funcName = getMouseFuncName(eventType);
bool ret = false;
@ -1282,8 +1404,8 @@ bool ScriptingCore::handleMouseEvent(void* nativeObj, cocos2d::EventMouse::Mouse
js_proxy_t * p = jsb_get_native_proxy(nativeObj);
if (p)
{
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::Event>(event);
jsval dataVal = OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, event, typeClass, "cocos2d::Event"));
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::EventMouse>((cocos2d::EventMouse*)event);
jsval dataVal = OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, event, typeClass, "cocos2d::EventMouse"));
ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), funcName.c_str(), 1, &dataVal, jsvalRet);
removeJSObject(_cx, event);
@ -1293,8 +1415,8 @@ bool ScriptingCore::handleMouseEvent(void* nativeObj, cocos2d::EventMouse::Mouse
return ret;
}
bool ScriptingCore::executeFunctionWithObjectData(void* nativeObj, const char *name, JSObject *obj) {
bool ScriptingCore::executeFunctionWithObjectData(void* nativeObj, const char *name, JSObject *obj)
{
js_proxy_t * p = jsb_get_native_proxy(nativeObj);
if (!p) return false;
@ -1363,8 +1485,6 @@ bool ScriptingCore::executeFunctionWithOwner(jsval owner, const char *name, cons
bool ScriptingCore::handleKeybardEvent(void* nativeObj, cocos2d::EventKeyboard::KeyCode keyCode, bool isPressed, cocos2d::Event* event)
{
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
js_proxy_t * p = jsb_get_native_proxy(nativeObj);
if (nullptr == p)
@ -1372,10 +1492,10 @@ bool ScriptingCore::handleKeybardEvent(void* nativeObj, cocos2d::EventKeyboard::
bool ret = false;
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::Event>(event);
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::EventKeyboard>((cocos2d::EventKeyboard*)event);
jsval args[2] = {
int32_to_jsval(_cx, (int32_t)keyCode),
OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, event, typeClass, "cocos2d::Event"))
OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, event, typeClass, "cocos2d::EventKeyboard"))
};
if (isPressed)
@ -1394,8 +1514,6 @@ bool ScriptingCore::handleKeybardEvent(void* nativeObj, cocos2d::EventKeyboard::
bool ScriptingCore::handleFocusEvent(void* nativeObj, cocos2d::ui::Widget* widgetLoseFocus, cocos2d::ui::Widget* widgetGetFocus)
{
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
js_proxy_t * p = jsb_get_native_proxy(nativeObj);
if (nullptr == p)
@ -1404,8 +1522,8 @@ bool ScriptingCore::handleFocusEvent(void* nativeObj, cocos2d::ui::Widget* widge
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::ui::Widget>(widgetLoseFocus);
jsval args[2] = {
OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, widgetLoseFocus, typeClass, "cocos2d::ui::Widget")),
OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(_cx, widgetGetFocus, typeClass, "cocos2d::ui::Widget"))
OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, widgetLoseFocus, typeClass, "cocos2d::ui::Widget")),
OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(_cx, widgetGetFocus, typeClass, "cocos2d::ui::Widget"))
};
bool ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "onFocusChanged", 2, args);
@ -1420,13 +1538,12 @@ int ScriptingCore::executeCustomTouchesEvent(EventTouch::EventCode eventType,
std::string funcName = getTouchesFuncName(eventType);
JS::RootedObject jsretArr(_cx, JS_NewArrayObject(this->_cx, 0));
// JS_AddNamedObjectRoot(this->_cx, &jsretArr, "touchArray");
int count = 0;
for (auto& touch : touches)
{
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::Touch>(touch);
jsval jsret = OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(this->_cx, touch, typeClass, "cocos2d::Touch"));
jsval jsret = OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(this->_cx, touch, typeClass, "cocos2d::Touch"));
JS::RootedValue jsval(_cx, jsret);
if (!JS_SetElement(this->_cx, jsretArr, count, jsval)) {
break;
@ -1436,7 +1553,6 @@ int ScriptingCore::executeCustomTouchesEvent(EventTouch::EventCode eventType,
jsval jsretArrVal = OBJECT_TO_JSVAL(jsretArr);
executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), funcName.c_str(), 1, &jsretArrVal);
// JS_RemoveObjectRoot(this->_cx, &jsretArr);
for (auto& touch : touches)
{
@ -1449,17 +1565,14 @@ int ScriptingCore::executeCustomTouchesEvent(EventTouch::EventCode eventType,
int ScriptingCore::executeCustomTouchEvent(EventTouch::EventCode eventType, Touch *touch, JSObject *obj)
{
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS::RootedValue retval(_cx);
std::string funcName = getTouchFuncName(eventType);
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::Touch>(touch);
jsval jsTouch = OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(this->_cx, touch, typeClass, "cocos2d::Touch"));
jsval jsTouch = OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(this->_cx, touch, typeClass, "cocos2d::Touch"));
executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), funcName.c_str(), 1, &jsTouch, &retval);
// Remove touch object from global hash table and unroot it.
removeJSObject(this->_cx, touch);
return 1;
@ -1471,20 +1584,16 @@ int ScriptingCore::executeCustomTouchEvent(EventTouch::EventCode eventType,
Touch *touch, JSObject *obj,
JS::MutableHandleValue retval)
{
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
std::string funcName = getTouchFuncName(eventType);
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::Touch>(touch);
jsval jsTouch = OBJECT_TO_JSVAL(jsb_ref_get_or_create_jsobject(this->_cx, touch, typeClass, "cocos2d::Touch"));
jsval jsTouch = OBJECT_TO_JSVAL(jsb_get_or_create_weak_jsobject(this->_cx, touch, typeClass, "cocos2d::Touch"));
executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), funcName.c_str(), 1, &jsTouch, retval);
// Remove touch object from global hash table and unroot it.
removeJSObject(this->_cx, touch);
return 1;
}
int ScriptingCore::sendEvent(ScriptEvent* evt)
@ -1499,8 +1608,6 @@ int ScriptingCore::sendEvent(ScriptEvent* evt)
return 0;
}
JSAutoCompartment ac(_cx, _global.ref());
switch (evt->type)
{
case kNodeEvent:
@ -2008,72 +2115,126 @@ void jsb_remove_proxy(js_proxy_t* proxy)
//
// ref_create
JSObject* jsb_ref_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
JS::HandleObject jsb_ref_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
{
JS::RootedObject proto(cx, typeClass->proto.ref());
JS::RootedObject parent(cx, typeClass->parentProto.ref());
JS::RootedObject js_obj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
js_proxy_t* newproxy = jsb_new_proxy(ref, js_obj);
JS::RootedObject jsObj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
js_proxy_t* newproxy = jsb_new_proxy(ref, jsObj);
jsb_ref_init(cx, &newproxy->obj, ref, debug);
return js_obj;
return jsObj;
}
JSObject* jsb_ref_autoreleased_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
JS::HandleObject jsb_ref_autoreleased_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
{
JS::RootedObject proto(cx, typeClass->proto.ref());
JS::RootedObject parent(cx, typeClass->parentProto.ref());
JS::RootedObject js_obj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
js_proxy_t* newproxy = jsb_new_proxy(ref, js_obj);
JS::RootedObject jsObj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
js_proxy_t* newproxy = jsb_new_proxy(ref, jsObj);
jsb_ref_autoreleased_init(cx, &newproxy->obj, ref, debug);
return js_obj;
return jsObj;
}
JS::HandleObject jsb_create_weak_jsobject(JSContext *cx, void *native, js_type_class_t *typeClass, const char* debug)
{
JS::RootedObject proto(cx, typeClass->proto.ref());
JS::RootedObject parent(cx, typeClass->parentProto.ref());
JS::RootedObject jsObj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
jsb_new_proxy(native, jsObj);
#if not CC_ENABLE_GC_FOR_NATIVE_OBJECTS
JS::AddNamedObjectRoot(cx, &proxy->obj, debug);
#else
#if COCOS2D_DEBUG
CCLOG("++++++WEAK_REF++++++ Cpp(%s): %p - JS: %p", debug, native, jsObj.get());
#endif // COCOS2D_DEBUG
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
return jsObj;
}
// get_or_create
JSObject* jsb_ref_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
JS::HandleObject jsb_ref_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
{
auto proxy = jsb_get_native_proxy(ref);
if (proxy)
return proxy->obj;
{
JS::RootedObject obj(cx, proxy->obj);
return obj;
}
// don't auto-release, don't retain.
JS::RootedObject proto(cx, typeClass->proto.ref());
JS::RootedObject parent(cx, typeClass->parentProto.ref());
JS::RootedObject js_obj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
js_proxy_t* newproxy = jsb_new_proxy(ref, js_obj);
JS::RootedObject jsObj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
js_proxy_t* newproxy = jsb_new_proxy(ref, jsObj);
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
CC_UNUSED_PARAM(newproxy);
// retain first copy, and before "owning" to prevent it
// from calling "rootObject"
ref->retain();
ref->_scriptOwned = true;
js_add_FinalizeHook(cx, jsObj);
#if COCOS2D_DEBUG
ScriptingCore::retainCount++;
CCLOG("++++++RETAINED++++++ %d Cpp(%s): %p - JS: %p", ScriptingCore::retainCount, debug, ref, jsObj.get());
#endif // COCOS2D_DEBUG
#else
// don't autorelease it
JS::AddNamedObjectRoot(cx, &newproxy->obj, debug);
#endif
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
return js_obj;
return jsObj;
}
// get_or_create: REf is already autoreleased (or created)
JSObject* jsb_ref_autoreleased_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
// get_or_create: Ref is already autoreleased (or created)
JS::HandleObject jsb_ref_autoreleased_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug)
{
auto proxy = jsb_get_native_proxy(ref);
if (proxy)
return proxy->obj;
{
JS::RootedObject obj(cx, proxy->obj);
return obj;
}
// else
return jsb_ref_autoreleased_create_jsobject(cx, ref, typeClass, debug);
}
// get_or_create: when native object isn't a ref object or when the native object life cycle don't need to be managed by js object
JS::HandleObject jsb_get_or_create_weak_jsobject(JSContext *cx, void *native, js_type_class_t *typeClass, const char* debug)
{
auto proxy = jsb_get_native_proxy(native);
if (proxy)
{
JS::RootedObject obj(cx, proxy->obj);
return obj;
}
// don't auto-release, don't retain.
JS::RootedObject proto(cx, typeClass->proto.ref());
JS::RootedObject parent(cx, typeClass->parentProto.ref());
JS::RootedObject jsObj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
proxy = jsb_new_proxy(native, jsObj);
#if not CC_ENABLE_GC_FOR_NATIVE_OBJECTS
JS::AddNamedObjectRoot(cx, &proxy->obj, debug);
#else
#if COCOS2D_DEBUG
CCLOG("++++++WEAK_REF++++++ Cpp(%s): %p - JS: %p", debug, native, jsObj.get());
#endif // COCOS2D_DEBUG
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
return jsObj;
}
// ref_init
void jsb_ref_init(JSContext* cx, JS::Heap<JSObject*> *obj, Ref* ref, const char* debug)
{
// CCLOG("jsb_ref_init: JSObject address = %p. %s", obj->get(), debug);
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
(void)cx;
(void)obj;
ref->_scriptOwned = true;
(void)ref;
JS::RootedObject jsObj(cx, *obj);
js_add_FinalizeHook(cx, jsObj);
// don't retain it, already retained
#if COCOS2D_DEBUG
ScriptingCore::retainCount++;
CCLOG("++++++RETAINED++++++ %d Cpp(%s): %p - JS: %p", ScriptingCore::retainCount, debug, ref, jsObj.get());
#endif // COCOS2D_DEBUG
#else
// autorelease it
ref->autorelease();
@ -2087,46 +2248,46 @@ void jsb_ref_autoreleased_init(JSContext* cx, JS::Heap<JSObject*> *obj, Ref* ref
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
(void)cx;
(void)obj;
// retain first copy, and before "owning" to prevent it
// from calling "rootObject"
ref->retain();
ref->_scriptOwned = true;
JS::RootedObject jsObj(cx, *obj);
js_add_FinalizeHook(cx, jsObj);
#else
// don't autorelease it, since it is already autoreleased
JS::AddNamedObjectRoot(cx, obj, debug);
#endif
}
// finalize
void jsb_ref_finalize(JSFreeOp* fop, JSObject* obj)
{
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
auto proxy = jsb_get_js_proxy(obj);
if (proxy)
{
auto ref = static_cast<cocos2d::Ref*>(proxy->ptr);
jsb_remove_proxy(proxy);
if (ref)
ref->release();
}
else
{
CCLOG("jsb_ref_finalize: BUG: proxy not found for %p (%s)", obj, JS_GetClass(obj)->name);
}
#else
// CCLOG("jsb_ref_finalize: JSObject address = %p", obj);
#endif
}
// rebind
void jsb_ref_rebind(JSContext* cx, JS::HandleObject jsobj, js_proxy_t *proxy, cocos2d::Ref* oldRef, cocos2d::Ref* newRef, const char* debug)
{
#if not CC_ENABLE_GC_FOR_NATIVE_OBJECTS
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
// Release the old reference as it have been retained by jsobj previously,
// and the jsobj won't have any chance to release it in the future
oldRef->release();
#else
JS::RemoveObjectRoot(cx, &proxy->obj);
#endif
jsb_remove_proxy(proxy);
// Rebind js obj with new action
js_proxy_t* newProxy = jsb_new_proxy(newRef, jsobj);
jsb_ref_init(cx, &newProxy->obj, newRef, debug);
jsb_new_proxy(newRef, jsobj);
}
// Register finalize hook
void jsb_register_finalize_hook(JSObject *hook, JSObject *owner)
{
_js_hook_owner_map.insert(std::make_pair(hook, owner));
}
// Remove hook owner entry in _js_hook_owner_map
JSObject *jsb_get_and_remove_hook_owner(JSObject *hook)
{
auto ownerIt = _js_hook_owner_map.find(hook);
// Found
if (ownerIt != _js_hook_owner_map.cend())
{
_js_hook_owner_map.erase(ownerIt);
return ownerIt->second;
}
return nullptr;
}

View File

@ -88,6 +88,10 @@ private:
bool _callFromScript;
ScriptingCore();
public:
#if COCOS2D_DEBUG
static int retainCount;
#endif // COCOS2D_DEBUG
~ScriptingCore();
/**@~english
@ -102,6 +106,36 @@ public:
*/
virtual cocos2d::ccScriptType getScriptType() override { return cocos2d::kScriptTypeJavascript; };
/**
* Reflect the retain relationship to script scope
*/
virtual void retainScriptObject(cocos2d::Ref* owner, cocos2d::Ref* target) override;
/**
* Add the script object to root object
*/
virtual void rootScriptObject(cocos2d::Ref* target) override;
/**
* Reflect the release relationship to script scope
*/
virtual void releaseScriptObject(cocos2d::Ref* owner, cocos2d::Ref* target) override;
/**
* Remove the script object from root object
*/
virtual void unrootScriptObject(cocos2d::Ref* target) override;
/**
* Release all children in script scope
*/
virtual void releaseAllChildrenRecursive(cocos2d::Node *node) override;
/**
* Release all native refs for the given owner in script scope
*/
virtual void releaseAllNativeRefs(cocos2d::Ref* owner) override;
/**
* @brief @~english Removes the C++ object's linked JavaScript proxy object from JavaScript context
* @param obj @~english Object to be removed
@ -438,22 +472,6 @@ public:
* @param vp @~english The arguments
*/
static bool dumpRoot(JSContext *cx, uint32_t argc, jsval *vp);
/**@~english
* Adds a js object to root so that it won't be touched by the garbage collection, it should be invoked from script environment
* Bound to `__jsc__.addGCRootObject`
* @param cx @~english The js context
* @param argc @~english The arguments count
* @param vp @~english The arguments
*/
static bool addRootJS(JSContext *cx, uint32_t argc, jsval *vp);
/**@~english
* Removes a js object from root, it should be invoked from script environment
* Bound to `__jsc__.removeGCRootObject`
* @param cx @~english The js context
* @param argc @~english The arguments count
* @param vp @~english The arguments
*/
static bool removeRootJS(JSContext *cx, uint32_t argc, jsval *vp);
/**@~english
* Check whether a js object's C++ proxy is still valid, it should be invoked from script environment
* Bound to `window.__isObjectValid`
@ -573,6 +591,9 @@ js_proxy_t* jsb_get_js_proxy(JSObject* jsObj);
void jsb_remove_proxy(js_proxy_t* nativeProxy, js_proxy_t* jsProxy);
/** removes both the native and js proxies */
void jsb_remove_proxy(js_proxy_t* proxy);
/** removes the native js object proxy and unroot the js object (if necessary),
it's often used when JS object is created by native object */
void removeJSObject(JSContext* cx, cocos2d::Ref* nativeObj);
/**
* Generic initialization function for subclasses of Ref
@ -588,20 +609,15 @@ void jsb_ref_init(JSContext* cx, JS::Heap<JSObject*> *obj, cocos2d::Ref* ref, co
void jsb_ref_autoreleased_init(JSContext* cx, JS::Heap<JSObject*> *obj, cocos2d::Ref* ref, const char* debug);
/**
* Generic finalize used by objects that are subclass of Ref
*/
void jsb_ref_finalize(JSFreeOp* fop, JSObject* obj);
/**
Disassociates oldRef from jsobj, and associates a new Ref.
Useful for the EaseActions and others
* Disassociates oldRef from jsobj, and associates a new Ref.
* Useful for the EaseActions and others
*/
void jsb_ref_rebind(JSContext* cx, JS::HandleObject jsobj, js_proxy_t *js2native_proxy, cocos2d::Ref* oldRef, cocos2d::Ref* newRef, const char* debug);
/**
Creates a new JSObject of a certain type (typeClass) and creates a proxy associated with and the Ref
* Creates a new JSObject of a certain type (typeClass) and creates a proxy associated with and the Ref
*/
JSObject* jsb_ref_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
JS::HandleObject jsb_ref_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
/**
* Creates a new JSObject of a certain type (typeClass) and creates a proxy associated with and the Ref
@ -609,20 +625,46 @@ JSObject* jsb_ref_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_clas
* This function should never be called. It is only added as way to fix
* an issue with the static auto-bindings with the "create" function
*/
JSObject* jsb_ref_autoreleased_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
JS::HandleObject jsb_ref_autoreleased_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
/**
It will try to get the associated JSObjct for ref.
If it can't find it, it will create a new one associating it to Ref.
Call this function for objects that were already created and initialized, when returning `getChild()`
* It will try to get the associated JSObjct for the native object.
* The reference created from JSObject to native object is weak because it won't retain it.
* The behavior is exactly the same with 'jsb_ref_create_jsobject' when CC_ENABLE_GC_FOR_NATIVE_OBJECTS desactivated.
*/
JSObject* jsb_ref_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
JS::HandleObject jsb_create_weak_jsobject(JSContext *cx, void *native, js_type_class_t *typeClass, const char* debug);
/**
It will try to get the associated JSObjct for ref.
If it can't find it, it will create a new one associating it to Ref
Call this function for objects that might return an already existing copy when you create them. For example, `Animation3D::create()`;
* It will try to get the associated JSObjct for ref.
* If it can't find it, it will create a new one associating it to Ref.
* Call this function for objects that were already created and initialized, when returning `getChild()`
*/
JSObject* jsb_ref_autoreleased_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
JS::HandleObject jsb_ref_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
/**
* It will try to get the associated JSObjct for ref.
* If it can't find it, it will create a new one associating it to Ref
* Call this function for objects that might return an already existing copy when you create them. For example, `Animation3D::create()`;
*/
JS::HandleObject jsb_ref_autoreleased_get_or_create_jsobject(JSContext *cx, cocos2d::Ref *ref, js_type_class_t *typeClass, const char* debug);
/**
* It will try to get the associated JSObjct for the native object.
* If it can't find it, it will create a new one associating it to the native object.
* The reference created from JSObject to native object is weak because it won't retain it.
* The behavior is exactly the same with 'jsb_ref_get_or_create_jsobject' when CC_ENABLE_GC_FOR_NATIVE_OBJECTS desactivated.
*/
JS::HandleObject jsb_get_or_create_weak_jsobject(JSContext *cx, void *native, js_type_class_t *typeClass, const char* debug);
/**
* Register finalize hook and its owner as an entry in _js_hook_owner_map,
* so that we can find the owner of a FinalizeHook in its finalize function
*/
void jsb_register_finalize_hook(JSObject *hook, JSObject *owner);
/**
* Remove the entry of finalize hook and its owner in _js_hook_owner_map
*/
JSObject *jsb_get_and_remove_hook_owner(JSObject *hook);
#endif /* __SCRIPTING_CORE_H__ */

View File

@ -422,72 +422,6 @@ bool js_cocos2dx_CCMenuItemToggle_create(JSContext *cx, uint32_t argc, jsval *vp
return false;
}
// TODO: This function is deprecated. The new API is "new Animation" instead of "Animation.create"
// There are not js tests for this function. Impossible to know weather it works Ok.
bool js_cocos2dx_CCAnimation_create(JSContext *cx, uint32_t argc, jsval *vp)
{
bool ok = true;
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (argc <= 3) {
cocos2d::Animation* ret = nullptr;
double arg1 = 0.0f;
if (argc == 2)
{
Vector<SpriteFrame*> arg0;
if (argc > 0) {
ok &= jsval_to_ccvector(cx, args.get(0), &arg0);
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
}
JS::RootedValue jsarg1(cx, args.get(1));
ok &= JS::ToNumber(cx, jsarg1, &arg1);
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
ret = new (std::nothrow) cocos2d::Animation;
ok &= ret->initWithSpriteFrames(arg0, arg1);
}
else if (argc == 3)
{
Vector<AnimationFrame*> arg0;
if (argc > 0) {
ok &= jsval_to_ccvector(cx, args.get(0), &arg0);
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
}
unsigned int loops;
JS::RootedValue jsarg1(cx, args.get(1));
ok &= JS::ToNumber(cx, jsarg1, &arg1);
ok &= jsval_to_uint32(cx, args.get(2), &loops);
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
ret = new (std::nothrow) cocos2d::Animation;
ok &= ret->initWithAnimationFrames(arg0, arg1, loops);
}
else if (argc == 1)
{
Vector<SpriteFrame*> arg0;
if (argc > 0) {
ok &= jsval_to_ccvector(cx, args.get(0), &arg0);
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
}
ret = new (std::nothrow) cocos2d::Animation;
ok &= ret->initWithSpriteFrames(arg0);
}
else if (argc == 0)
{
ret = new (std::nothrow) cocos2d::Animation;
ok = ret->init();
}
if (ok)
{
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::Animation>(ret);
// link the native object with the javascript object
JS::RootedObject jsobj(cx, jsb_ref_create_jsobject(cx, ret, typeClass, "cocos2d::Animation"));
args.rval().set(OBJECT_TO_JSVAL(jsobj));
return true;
}
}
JS_ReportError(cx, "wrong number of arguments");
return false;
}
bool js_cocos2dx_CCScene_init(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@ -507,30 +441,6 @@ bool js_cocos2dx_CCScene_init(JSContext *cx, uint32_t argc, jsval *vp)
return false;
}
// TODO: This function is deprecated. The new API is "new LayerMultiplex" instead of "LayerMultiplex.create"
// There are not js tests for this function. Impossible to know weather it works Ok.
bool js_cocos2dx_CCLayerMultiplex_create(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
Vector<Layer*> arg0;
bool ok = true;
ok &= jsvals_variadic_to_ccvector(cx, args, &arg0);
JSB_PRECONDITION2(ok, cx, false, "Error processing arguments");
cocos2d::LayerMultiplex* ret = new (std::nothrow) cocos2d::LayerMultiplex;
ok &= ret->initWithArray(arg0);
if (ok)
{
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::LayerMultiplex>(ret);
// link the native object with the javascript object
JS::RootedObject jsobj(cx, jsb_ref_create_jsobject(cx, ret, typeClass, "cocos2d::LayerMultiplex"));
args.rval().set(OBJECT_TO_JSVAL(jsobj));
return true;
}
JS_ReportError(cx, "Error creating LayerMultiplex");
return false;
}
bool js_cocos2dx_JSTouchDelegate_registerStandardDelegate(JSContext *cx, uint32_t argc, jsval *vp)
{
if (argc == 1 || argc == 2)
@ -604,6 +514,15 @@ JSObject* getObjectFromNamespace(JSContext* cx, JS::HandleObject ns, const char
return NULL;
}
void js_add_FinalizeHook(JSContext *cx, JS::HandleObject target)
{
JS::RootedObject proto(cx, jsb_FinalizeHook_prototype);
JS::RootedObject hook(cx, JS_NewObject(cx, jsb_FinalizeHook_class, proto, JS::NullPtr()));
jsb_register_finalize_hook(hook.get(), target.get());
JS::RootedValue hookVal(cx, OBJECT_TO_JSVAL(hook));
JS_SetProperty(cx, target, "__hook", hookVal);
}
jsval anonEvaluate(JSContext *cx, JS::HandleObject thisObj, const char* string) {
JS::RootedValue out(cx);
//JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
@ -2064,12 +1983,14 @@ bool js_forceGC(JSContext *cx, uint32_t argc, jsval *vp) {
bool js_cocos2dx_retain(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
#if not CC_ENABLE_GC_FOR_NATIVE_OBJECTS
JS::RootedObject obj(cx, args.thisv().toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Ref* cobj = (cocos2d::Ref *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, false, "js_cocos2dx_retain : Invalid Native Object");
cobj->retain();
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
args.rval().setUndefined();
return true;
}
@ -2077,12 +1998,14 @@ bool js_cocos2dx_retain(JSContext *cx, uint32_t argc, jsval *vp)
bool js_cocos2dx_release(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
#if not CC_ENABLE_GC_FOR_NATIVE_OBJECTS
JS::RootedObject obj(cx, args.thisv().toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Ref* cobj = (cocos2d::Ref *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, false, "js_cocos2dx_release : Invalid Native Object");
cobj->release();
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
args.rval().setUndefined();
return true;
}
@ -2971,7 +2894,8 @@ bool js_CatmullRomActions_initWithDuration(JSContext *cx, uint32_t argc, jsval *
JSB_PRECONDITION2( cobj, cx, false, "js_cocos2dx_CatmullRom_initWithDuration : Invalid Native Object");
if (argc == 2) {
double arg0;
ok &= JS::ToNumber( cx, JS::RootedValue(cx, args.get(0)), &arg0);
JS::RootedValue jsarg0(cx, args.get(0));
ok &= JS::ToNumber(cx, jsarg0, &arg0);
int num;
Point *arr;
@ -3034,7 +2958,8 @@ bool js_cocos2dx_CardinalSplineTo_initWithDuration(JSContext *cx, uint32_t argc,
double arg0;
double arg2;
ok &= JS::ToNumber( cx, JS::RootedValue(cx, args.get(0)), &arg0);
JS::RootedValue jsarg0(cx, args.get(0));
ok &= JS::ToNumber(cx, jsarg0, &arg0);
int num;
Point *arr;
@ -3044,7 +2969,8 @@ bool js_cocos2dx_CardinalSplineTo_initWithDuration(JSContext *cx, uint32_t argc,
arg1->addControlPoint(arr[i]);
}
ok &= JS::ToNumber( cx, JS::RootedValue(cx, args.get(2)), &arg2);
JS::RootedValue jsarg2(cx, args.get(2));
ok &= JS::ToNumber(cx, jsarg2, &arg2);
JSB_PRECONDITION2(ok, cx, false, "js_cocos2dx_CardinalSplineTo_initWithDuration : Error processing arguments");
bool ret = cobj->initWithDuration(arg0, arg1, arg2);
@ -3060,11 +2986,13 @@ bool js_cocos2dx_CardinalSplineTo_initWithDuration(JSContext *cx, uint32_t argc,
}
bool JSB_CCCatmullRomBy_actionWithDuration(JSContext *cx, uint32_t argc, jsval *vp) {
bool JSB_CCCatmullRomBy_actionWithDuration(JSContext *cx, uint32_t argc, jsval *vp)
{
return js_CatmullRomActions_create<cocos2d::CatmullRomBy>(cx, argc, vp);
}
bool JSB_CCCatmullRomTo_actionWithDuration(JSContext *cx, uint32_t argc, jsval *vp) {
bool JSB_CCCatmullRomTo_actionWithDuration(JSContext *cx, uint32_t argc, jsval *vp)
{
return js_CatmullRomActions_create<cocos2d::CatmullRomTo>(cx, argc, vp);
}
@ -3746,60 +3674,6 @@ bool js_cocos2dx_ccquatMultiply(JSContext *cx, uint32_t argc, jsval *vp)
return false;
}
// TODO: This function is deprecated. The new API is "new Sprite" instead of "Sprite.create"
// There are not js tests for this function. Impossible to know weather it works Ok.
bool js_cocos2dx_Sprite_create(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
bool ok = false;
cocos2d::Sprite *sprite = nullptr;
if (argc == 0)
{
sprite = new (std::nothrow) cocos2d::Sprite;
sprite->init();
ok = true;
}
else if (argc == 1)
{
std::string arg0;
ok = jsval_to_std_string(cx, args.get(0), &arg0);
if (ok)
{
sprite = new (std::nothrow) cocos2d::Sprite;
sprite->initWithFile(arg0);
}
}
else if(argc == 2)
{
std::string arg0;
ok = jsval_to_std_string(cx, args.get(0), &arg0);
cocos2d::Rect arg1;
ok |= jsval_to_ccrect(cx, args.get(1), &arg1);
if (ok)
{
sprite = new (std::nothrow) cocos2d::Sprite;
sprite->initWithFile(arg0, arg1);
}
}
if (ok)
{
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::Sprite>(sprite);
// link the native object with the javascript object
JS::RootedObject jsobj(cx, jsb_ref_create_jsobject(cx, sprite, typeClass, "cocos2d::Sprite"));
args.rval().set(OBJECT_TO_JSVAL(jsobj));
return true;
}
// else
JS_ReportError(cx, "js_cocos2dx_Sprite_create : wrong number of arguments");
return false;
}
bool js_cocos2dx_Sprite_initWithPolygon(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@ -4298,64 +4172,6 @@ bool js_cocos2dx_CCGLProgram_setUniformLocationWithMatrixfvUnion(JSContext *cx,
return false;
}
bool js_cocos2dx_CCGLProgram_create(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
bool ok = true;
if(argc != 2) {
JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 2);
return false;
}
const char *arg0, *arg1;
std::string arg0_tmp; ok &= jsval_to_std_string(cx, args.get(0), &arg0_tmp); arg0 = arg0_tmp.c_str();
std::string arg1_tmp; ok &= jsval_to_std_string(cx, args.get(1), &arg1_tmp); arg1 = arg1_tmp.c_str();
GLProgram* ret = new (std::nothrow) cocos2d::GLProgram;
ok = ret->initWithFilenames(arg0, arg1);
if (ok)
{
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::GLProgram>(ret);
// link the native object with the javascript object
JS::RootedObject jsobj(cx, jsb_ref_create_jsobject(cx, ret, typeClass, "cocos2d::GLProgram"));
args.rval().set(OBJECT_TO_JSVAL(jsobj));
return true;
}
JS_ReportError(cx, "Error creating GLProgram");
return false;
}
// TODO: This function doesn't have test. I was impossible to test it
bool js_cocos2dx_CCGLProgram_createWithString(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
bool ok = true;
if(argc != 2) {
JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 2);
return false;
}
const char *arg0, *arg1;
std::string arg0_tmp; ok &= jsval_to_std_string(cx, args.get(0), &arg0_tmp); arg0 = arg0_tmp.c_str();
std::string arg1_tmp; ok &= jsval_to_std_string(cx, args.get(1), &arg1_tmp); arg1 = arg1_tmp.c_str();
GLProgram* ret = new (std::nothrow) GLProgram;
ok = ret->initWithByteArrays(arg0, arg1);
if (ok)
{
js_type_class_t *typeClass = js_get_type_from_native<cocos2d::GLProgram>(ret);
// link the native object with the javascript object
JS::RootedObject jsobj(cx, jsb_ref_create_jsobject(cx, ret, typeClass, "cocos2d::GLProgram"));
args.rval().set(OBJECT_TO_JSVAL(jsobj));
return true;
}
return true;
}
bool js_cocos2dx_CCGLProgram_getProgram(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@ -4447,42 +4263,6 @@ bool js_cocos2dx_GLProgramState_setUniformVec4(JSContext *cx, uint32_t argc, jsv
return false;
}
#define js_cocos2dx_CCCamera_getXYZ(funcName) \
bool ok = true; \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
JS::RootedObject obj(cx, args.thisv().toObjectOrNull()); \
js_proxy_t *proxy = jsb_get_js_proxy(obj); \
cocos2d::Camera* cobj = (cocos2d::Camera *)(proxy ? proxy->ptr : NULL); \
JSB_PRECONDITION2( cobj, cx, false, "Invalid Native Object"); \
if (argc == 0) { \
float x; \
float y; \
float z; \
cobj->funcName(&x, &y, &z); \
JS::RootedObject tmp(cx, JS_NewObject(cx, NULL, NULL, NULL)); \
\
do \
{ \
if (NULL == tmp) break; \
\
ok = JS_DefineProperty(cx, tmp, "x", DOUBLE_TO_JSVAL(x), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && \
JS_DefineProperty(cx, tmp, "y", DOUBLE_TO_JSVAL(y), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && \
JS_DefineProperty(cx, tmp, "z", DOUBLE_TO_JSVAL(z), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); \
\
if (ok) { \
args.rval().set(OBJECT_TO_JSVAL(tmp)); \
return true; \
} \
} while (false); \
\
args.rval().setUndefined(); \
return true; \
} \
\
JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 0); \
return false;
bool js_cocos2dx_SpriteBatchNode_getDescendants(JSContext *cx, uint32_t argc, jsval *vp)
{
@ -5169,7 +4949,6 @@ void js_register_cocos2dx_EventKeyboard(JSContext *cx, JS::HandleObject global)
jsb_cocos2d_EventKeyboard_class->enumerate = JS_EnumerateStub;
jsb_cocos2d_EventKeyboard_class->resolve = JS_ResolveStub;
jsb_cocos2d_EventKeyboard_class->convert = JS_ConvertStub;
jsb_cocos2d_EventKeyboard_class->finalize = jsb_ref_finalize;
jsb_cocos2d_EventKeyboard_class->flags = JSCLASS_HAS_RESERVED_SLOTS(2);
static JSPropertySpec properties[] = {
@ -5813,6 +5592,91 @@ bool js_cocos2dx_ComponentJS_getScriptObject(JSContext *cx, uint32_t argc, jsval
return false;
}
JSClass *jsb_FinalizeHook_class;
JSObject *jsb_FinalizeHook_prototype;
static bool jsb_FinalizeHook_constructor(JSContext *cx, uint32_t argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
// Create new object
JS::RootedObject proto(cx, jsb_FinalizeHook_prototype);
JS::RootedObject obj(cx, JS_NewObject(cx, jsb_FinalizeHook_class, proto, JS::NullPtr()));
// Register arguments[0] as owner
if (!args.get(0).isNullOrUndefined())
{
jsb_register_finalize_hook(obj.get(), args.get(0).toObjectOrNull());
}
args.rval().set(OBJECT_TO_JSVAL(obj));
return true;
}
void jsb_FinalizeHook_finalize(JSFreeOp *fop, JSObject *obj)
{
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JS::RootedObject jsobj(cx, obj);
JSObject *ownerPtr = jsb_get_and_remove_hook_owner(obj);
if (ownerPtr)
{
JS::RootedObject owner(cx, ownerPtr);
CCLOGINFO("jsbindings: finalizing JS object via Finalizehook %p", owner.get());
js_proxy_t* nproxy = nullptr;
js_proxy_t* jsproxy = nullptr;
jsproxy = jsb_get_js_proxy(owner);
if (jsproxy)
{
cocos2d::Ref *refObj = static_cast<cocos2d::Ref *>(jsproxy->ptr);
nproxy = jsb_get_native_proxy(jsproxy->ptr);
jsb_remove_proxy(nproxy, jsproxy);
if (refObj)
{
int count = refObj->getReferenceCount();
if (count == 1)
{
refObj->autorelease();
}
else
{
CC_SAFE_RELEASE(refObj);
}
#if COCOS2D_DEBUG
ScriptingCore::retainCount--;
CCLOG("------RELEASED------ %d Native %p ref count: %d JSObj %p", ScriptingCore::retainCount, refObj, count-1, ownerPtr);
#endif // COCOS2D_DEBUG
}
#if COCOS2D_DEBUG
else {
CCLOG("A non ref object have registered finalize hook: %p", nproxy->ptr);
}
#endif // COCOS2D_DEBUG
}
#if COCOS2D_DEBUG
else {
CCLOG("jsbindings: Failed to remove proxy for js object: %p, it may cause memory leak and future crash", ownerPtr);
}
#endif // COCOS2D_DEBUG
}
}
void jsb_register_FinalizeHook(JSContext *cx, JS::HandleObject global) {
jsb_FinalizeHook_class = (JSClass *)calloc(1, sizeof(JSClass));
jsb_FinalizeHook_class->name = "FinalizeHook";
jsb_FinalizeHook_class->addProperty = JS_PropertyStub;
jsb_FinalizeHook_class->delProperty = JS_DeletePropertyStub;
jsb_FinalizeHook_class->getProperty = JS_PropertyStub;
jsb_FinalizeHook_class->setProperty = JS_StrictPropertyStub;
jsb_FinalizeHook_class->enumerate = JS_EnumerateStub;
jsb_FinalizeHook_class->resolve = JS_ResolveStub;
jsb_FinalizeHook_class->convert = JS_ConvertStub;
jsb_FinalizeHook_class->finalize = jsb_FinalizeHook_finalize;
jsb_FinalizeHook_class->flags = JSCLASS_HAS_RESERVED_SLOTS(2);
jsb_FinalizeHook_prototype = JS_InitClass(cx, global,
JS::NullPtr(), // parent proto
jsb_FinalizeHook_class,
jsb_FinalizeHook_constructor, 0, // constructor
NULL, NULL, NULL, NULL);
}
void register_cocos2dx_js_core(JSContext* cx, JS::HandleObject global)
{
JS::RootedObject ccObj(cx);
@ -5822,6 +5686,9 @@ void register_cocos2dx_js_core(JSContext* cx, JS::HandleObject global)
get_or_create_js_obj(cx, global, "cc", &ccObj);
get_or_create_js_obj(cx, global, "jsb", &jsbObj);
// Memory management related
jsb_register_FinalizeHook(cx, jsbObj);
js_register_cocos2dx_PolygonInfo(cx, jsbObj);
js_register_cocos2dx_AutoPolygon(cx, jsbObj);
@ -5987,9 +5854,6 @@ void register_cocos2dx_js_core(JSContext* cx, JS::HandleObject global)
tmpObj.set(jsb_cocos2d_CatmullRomTo_prototype);
JS_DefineFunction(cx, tmpObj, "initWithDuration", JSB_CatmullRomBy_initWithDuration, 2, JSPROP_ENUMERATE | JSPROP_PERMANENT);
JS_GetProperty(cx, ccObj, "Sprite", &tmpVal);
tmpObj = tmpVal.toObjectOrNull();
JS_DefineFunction(cx, tmpObj, "create", js_cocos2dx_Sprite_create, 0, JSPROP_READONLY | JSPROP_PERMANENT);
tmpObj.set(jsb_cocos2d_Sprite_prototype);
JS_DefineFunction(cx, tmpObj, "initWithPolygon", js_cocos2dx_Sprite_initWithPolygon, 1, JSPROP_ENUMERATE | JSPROP_PERMANENT);
JS_DefineFunction(cx, tmpObj, "setPolygonInfo", js_cocos2dx_Sprite_setPolygonInfo, 1, JSPROP_ENUMERATE | JSPROP_PERMANENT);
@ -6032,14 +5896,8 @@ void register_cocos2dx_js_core(JSContext* cx, JS::HandleObject global)
JS_GetProperty(cx, ccObj, "Spawn", &tmpVal);
tmpObj = tmpVal.toObjectOrNull();
JS_DefineFunction(cx, tmpObj, "create", js_cocos2dx_CCSpawn_create, 0, JSPROP_READONLY | JSPROP_PERMANENT);
//JS_GetProperty(cx, ccObj, "Animation", &tmpVal);
//tmpObj = tmpVal.toObjectOrNull();
//JS_DefineFunction(cx, tmpObj, "create", js_cocos2dx_CCAnimation_create, 0, JSPROP_READONLY | JSPROP_PERMANENT);
tmpObj.set(jsb_cocos2d_Scene_prototype);
JS_DefineFunction(cx, tmpObj, "init", js_cocos2dx_CCScene_init, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE);
JS_GetProperty(cx, ccObj, "LayerMultiplex", &tmpVal);
tmpObj = tmpVal.toObjectOrNull();
JS_DefineFunction(cx, tmpObj, "create", js_cocos2dx_CCLayerMultiplex_create, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_GetProperty(cx, ccObj, "CallFunc", &tmpVal);
tmpObj = tmpVal.toObjectOrNull();
@ -6047,11 +5905,6 @@ void register_cocos2dx_js_core(JSContext* cx, JS::HandleObject global)
tmpObj.set(jsb_cocos2d_CallFuncN_prototype);
JS_DefineFunction(cx, tmpObj, "initWithFunction", js_cocos2dx_CallFunc_initWithFunction, 1, JSPROP_ENUMERATE | JSPROP_PERMANENT);
JS_GetProperty(cx, ccObj, "GLProgram", &tmpVal);
tmpObj = tmpVal.toObjectOrNull();
JS_DefineFunction(cx, tmpObj, "create", js_cocos2dx_CCGLProgram_create, 1, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(cx, tmpObj, "createWithString", js_cocos2dx_CCGLProgram_createWithString, 1, JSPROP_READONLY | JSPROP_PERMANENT);
tmpObj.set(jsb_cocos2d_Camera_prototype);
JS_DefineFunction(cx, tmpObj, "unproject", js_cocos2dx_Camera_unproject, 1, JSPROP_ENUMERATE | JSPROP_PERMANENT);
JS_DefineFunction(cx, tmpObj, "isVisibleInFrustum", js_cocos2dx_Camera_isVisibleInFrustum, 1, JSPROP_ENUMERATE | JSPROP_PERMANENT);

View File

@ -53,9 +53,11 @@ typedef struct jsCallFuncTarget_proxy {
extern schedFunc_proxy_t *_schedFunc_target_ht;
extern schedTarget_proxy_t *_schedObj_target_ht;
extern callfuncTarget_proxy_t *_callfuncTarget_native_ht;
extern JSClass *jsb_FinalizeHook_class;
extern JSObject *jsb_FinalizeHook_prototype;
/**
* You don't need to manage the returned pointer. They live for the whole life of
* the app.
@ -81,67 +83,22 @@ inline js_type_class_t *js_get_type_from_native(T* native_obj) {
return found ? typeProxyIter->second : nullptr;
}
/**
* The returned pointer should be deleted using jsb_remove_proxy. Most of the
* time you do that in the C++ destructor.
*/
template<class T>
inline js_proxy_t *js_get_or_create_proxy(JSContext *cx, T *native_obj) {
js_proxy_t *proxy = jsb_get_native_proxy(native_obj);
if (!proxy) {
js_type_class_t *typeProxy = js_get_type_from_native<T>(native_obj);
// Return NULL if can't find its type rather than making an assert.
// assert(typeProxy);
if (!typeProxy) {
CCLOGINFO("Could not find the type of native object.");
return NULL;
}
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS::RootedObject proto(cx, typeProxy->proto.ref().get());
JS::RootedObject parent(cx, typeProxy->parentProto.ref().get());
JS::RootedObject js_obj(cx, JS_NewObject(cx, typeProxy->jsclass, proto, parent));
proxy = jsb_new_proxy(native_obj, js_obj);
#ifdef DEBUG
JS::AddNamedObjectRoot(cx, &proxy->obj, typeid(*native_obj).name());
#else
JS::AddObjectRoot(cx, &proxy->obj);
#endif
return proxy;
} else {
return proxy;
}
return NULL;
}
/**
* Gets or creates a JSObject based on native_obj.
If native_obj is subclass of Ref, it will use the jsb_ref functions.
Otherwise it will Root the newly created JSObject
* If native_obj is subclass of Ref, it will use the jsb_ref functions.
* Otherwise it will Root the newly created JSObject
*/
template<class T>
JSObject* js_get_or_create_jsobject(JSContext *cx, typename std::enable_if<!std::is_base_of<cocos2d::Ref,T>::value,T>::type *native_obj)
{
// CCLOG("js_get_or_create_jsobject NO REF: %s", typeid(native_obj).name());
js_proxy_t *proxy = jsb_get_native_proxy(native_obj);
if (!proxy)
{
js_type_class_t* typeClass = js_get_type_from_native<T>(native_obj);
JS::RootedObject proto(cx, typeClass->proto.ref().get());
JS::RootedObject parent(cx, typeClass->parentProto.ref().get());
JS::RootedObject js_obj(cx, JS_NewObject(cx, typeClass->jsclass, proto, parent));
proxy = jsb_new_proxy(native_obj, js_obj);
JS::AddNamedObjectRoot(cx, &proxy->obj, typeid(*native_obj).name());
}
return proxy->obj;
return jsb_get_or_create_weak_jsobject(cx, native_obj, typeClass, typeid(*native_obj).name());
}
/**
* Gets or creates a JSObject based on native_obj.
If native_obj is subclass of Ref, it will use the jsb_ref functions.
Otherwise it will Root the newly created JSObject
* If native_obj is subclass of Ref, it will use the jsb_ref functions.
* Otherwise it will Root the newly created JSObject
*/
template<class T>
JSObject* js_get_or_create_jsobject(JSContext *cx, typename std::enable_if<std::is_base_of<cocos2d::Ref,T>::value,T>::type *native_obj)
@ -150,6 +107,15 @@ JSObject* js_get_or_create_jsobject(JSContext *cx, typename std::enable_if<std::
return jsb_ref_get_or_create_jsobject(cx, native_obj, typeClass, typeid(*native_obj).name());
}
/**
* Add a FinalizeHook object to the target object.
* When the target object get garbage collected, its FinalizeHook's finalize function will be invoked.
* In the finalize function, it mainly remove native/js proxys, release/delete the native object.
* IMPORTANT: For Ref objects, please remember to retain the native object to correctly manage its reference count.
*/
void js_add_FinalizeHook(JSContext *cx, JS::HandleObject target);
JS::Value anonEvaluate(JSContext *cx, JS::HandleObject thisObj, const char* string);
void register_cocos2dx_js_core(JSContext* cx, JS::HandleObject obj);

View File

@ -236,17 +236,20 @@ var ClassManager = {
//
cc.Class = function(){};
cc.Class.extend = function (prop) {
var _super = this.prototype;
var _super = this.prototype,
prototype, Class, classId,
className = prop._className || "",
name, desc;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = Object.create(_super);
prototype = Object.create(_super);
initializing = false;
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// Copy the properties over onto the new prototype
for (var name in prop) {
for (name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
@ -269,35 +272,75 @@ cc.Class.extend = function (prop) {
prop[name];
}
// The dummy class constructor
function Class() {
Class = function () {
if (!initializing) {
this.__instanceId = ClassManager.getNewInstanceId();
if (!this.ctor) {
if (this.__nativeObj)
cc.log("No ctor function found! Please check whether `classes_need_extend` section in `ini` file like which in `tools/tojs/cocos2dx.ini`");
}
else {
this.ctor.apply(this, arguments);
}
}
this.ctor && this.ctor.apply(this, arguments);
}
};
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = cc.Class.extend;
var classId = ClassManager.getNewID();
classId = ClassManager.getNewID();
ClassManager[classId] = _super;
var desc = { writable: true, enumerable: false, configurable: true };
desc = { writable: true, enumerable: false, configurable: true };
Class.id = classId;
desc.value = classId;
Object.defineProperty(prototype, '__pid', desc);
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
var __registeCount = 0;
jsb.registerNativeRef = function (owner, target) {
if (typeof owner === "object" && typeof target === "object") {
var refs = owner.__nativeRefs;
if (!refs) {
refs = owner.__nativeRefs = [];
}
var index = refs.indexOf(target);
if (index === -1) {
__registeCount++;
// cc.log("##########registered " + target + " : " + __registeCount);
owner.__nativeRefs.push(target);
}
}
};
jsb.unregisterNativeRef = function (owner, target) {
if (typeof owner === "object" && typeof target === "object") {
var refs = owner.__nativeRefs;
if (!refs) {
return;
}
var index = refs.indexOf(target);
if (index !== -1) {
__registeCount--;
// cc.log("##------##unregistered " + target + " : " + __registeCount);
owner.__nativeRefs.splice(index, 1);
}
}
};
jsb.unregisterAllNativeRefs = function (owner) {
if (!owner) return;
owner.__nativeRefs.length = 0;
};
jsb.unregisterChildRefsForNode = function (node, recursive) {
if (!(node instanceof cc.Node)) return;
recursive = !!recursive;
var children = node.getChildren(), i, l, child;
for (i = 0, l = children.length; i < l; ++i) {
child = children[i];
jsb.unregisterNativeRef(node, child);
if (recursive) {
jsb.unregisterChildRefsForNode(child, recursive);
}
}
};