From e1bc6640f4dd58e39fb49adcb26a20ce900a950b Mon Sep 17 00:00:00 2001 From: pandamicro Date: Thu, 17 Dec 2015 21:53:40 +0800 Subject: [PATCH] Implement full GC relied memory model --- cocos/base/CCRef.cpp | 23 - cocos/base/CCScriptSupport.h | 30 ++ .../js-bindings/manual/ScriptingCore.cpp | 435 ++++++++++++------ .../js-bindings/manual/ScriptingCore.h | 112 +++-- .../js-bindings/manual/cocos2d_specifics.cpp | 373 +++++---------- .../js-bindings/manual/cocos2d_specifics.hpp | 70 +-- .../js-bindings/script/jsb_prepare.js | 93 +++- 7 files changed, 604 insertions(+), 532 deletions(-) diff --git a/cocos/base/CCRef.cpp b/cocos/base/CCRef.cpp index eed860e602..aeae043c58 100644 --- a/cocos/base/CCRef.cpp +++ b/cocos/base/CCRef.cpp @@ -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) diff --git a/cocos/base/CCScriptSupport.h b/cocos/base/CCScriptSupport.h index d525744e3e..86c5e151cc 100644 --- a/cocos/base/CCScriptSupport.h +++ b/cocos/base/CCScriptSupport.h @@ -641,6 +641,36 @@ public: * @js NA */ 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. diff --git a/cocos/scripting/js-bindings/manual/ScriptingCore.cpp b/cocos/scripting/js-bindings/manual/ScriptingCore.cpp index 04a29dd4af..30a8f4b667 100644 --- a/cocos/scripting/js-bindings/manual/ScriptingCore.cpp +++ b/cocos/scripting/js-bindings/manual/ScriptingCore.cpp @@ -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 g_queue; @@ -98,7 +102,7 @@ static void serverEntryPoint(unsigned int port); std::unordered_map _js_global_type_map; static std::unordered_map _native_js_global_map; static std::unordered_map _js_native_global_map; - +std::unordered_map _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::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& 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 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 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(touches[0]); - typeClassEvent = js_get_type_from_native(event); + typeClassEvent = js_get_type_from_native((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(touch); - js_type_class_t *typeClassEvent = js_get_type_from_native(event); + js_type_class_t *typeClassEvent = js_get_type_from_native((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(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*)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(event); + js_type_class_t *typeClass = js_get_type_from_native((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(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(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(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(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) @@ -1498,8 +1607,6 @@ int ScriptingCore::sendEvent(ScriptEvent* evt) restartVM(); return 0; } - - JSAutoCompartment ac(_cx, _global.ref()); switch (evt->type) { @@ -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 *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 *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(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; } diff --git a/cocos/scripting/js-bindings/manual/ScriptingCore.h b/cocos/scripting/js-bindings/manual/ScriptingCore.h index bb03a5fac8..a3cda14fe9 100644 --- a/cocos/scripting/js-bindings/manual/ScriptingCore.h +++ b/cocos/scripting/js-bindings/manual/ScriptingCore.h @@ -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` @@ -493,7 +511,7 @@ public: * @return @~english The global object */ bool isFunctionOverridedInJS(JS::HandleObject obj, const std::string& name, JSNative native); - + /** * Roots the associated JSObj. * The GC won't collected rooted objects. This function is only called @@ -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 *obj, cocos2d::Ref* ref, co void jsb_ref_autoreleased_init(JSContext* cx, JS::Heap *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__ */ diff --git a/cocos/scripting/js-bindings/manual/cocos2d_specifics.cpp b/cocos/scripting/js-bindings/manual/cocos2d_specifics.cpp index 626e7a5a82..d807041ca1 100644 --- a/cocos/scripting/js-bindings/manual/cocos2d_specifics.cpp +++ b/cocos/scripting/js-bindings/manual/cocos2d_specifics.cpp @@ -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 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 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 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(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 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(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,8 +2958,9 @@ 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; ok &= jsval_to_ccarray_of_CCPoint(cx, args.get(1), &arr, &num); @@ -3043,8 +2968,9 @@ bool js_cocos2dx_CardinalSplineTo_initWithDuration(JSContext *cx, uint32_t argc, for( int i=0; i < num;i++) { 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(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(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(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(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(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(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,25 +5896,14 @@ 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(); JS_DefineFunction(cx, tmpObj, "create", js_callFunc, 1, JSPROP_READONLY | JSPROP_PERMANENT); 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); diff --git a/cocos/scripting/js-bindings/manual/cocos2d_specifics.hpp b/cocos/scripting/js-bindings/manual/cocos2d_specifics.hpp index f15e6cb0b2..208b30bf1f 100644 --- a/cocos/scripting/js-bindings/manual/cocos2d_specifics.hpp +++ b/cocos/scripting/js-bindings/manual/cocos2d_specifics.hpp @@ -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 -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(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 JSObject* js_get_or_create_jsobject(JSContext *cx, typename std::enable_if::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(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; + js_type_class_t* typeClass = js_get_type_from_native(native_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 JSObject* js_get_or_create_jsobject(JSContext *cx, typename std::enable_if::value,T>::type *native_obj) @@ -150,6 +107,15 @@ JSObject* js_get_or_create_jsobject(JSContext *cx, typename std::enable_if