#ifndef _JSCUtil_h #define _JSCUtil_h #include #include #include namespace laya { class JSCUtil { public: static JSContextRef s_ctx; static bool extractJSAB(JSContextRef ctx,JSValueRef jsval, char*& data, int& len); static char* toCString(JSContextRef ctx,JSValueRef value); static char* toCString(JSContextRef ctx,JSStringRef value); }; template class JSCBinder { public: inline unsigned int __hash_BKDR(const char *p_str) { if(0 == p_str) return 0; unsigned int seed = 131; // 31 131 1313 13131 131313 etc.. unsigned int hash = 0; for(;0!=*p_str;) { hash = hash * seed + (*p_str++); } return (hash & 0x7FFFFFFF); } JSCBinder() { m_bIsGlobal = false; m_ClassDefine = kJSClassDefinitionEmpty; m_ClassObject = nullptr; m_iTypeID = __hash_BKDR(typeid(T).name()); } ~JSCBinder() { _reset(); } unsigned int getTypeID() { return m_iTypeID; } void addProperty(const std::string& name, JSObjectGetPropertyCallback getter, JSObjectSetPropertyCallback setter = NULL) { assert(!name.empty()); if (getter) { GetPropertyMapRes rs = m_GetPropertyMap.insert(GetPropertyMapValue(name, getter)); assert(rs.second); } if (setter) { SetPropertyMapRes rs = m_SetPropertyMap.insert(SetPropertyMapValue(name, setter)); assert(rs.second); } } void begin(JSContextRef ctx) { JSCUtil::s_ctx = ctx; } void addMethod(const std::string& name, JSObjectCallAsFunctionCallback method) { JSStringRef pName = JSStringCreateWithUTF8CString(name.c_str()); JSContextRef pCtx = JSCUtil::s_ctx; JSValueRef callAsFunction = JSObjectMakeFunctionWithCallback(pCtx, pName, method); JSStringRelease(pName); JSValueProtect(pCtx, callAsFunction); FunctionMapRes rsf = m_FunctionMap.insert(FunctionMapValue(name, callAsFunction)); assert(rsf.second); } void end(JSObjectRef object, const std::string& name) { m_bIsGlobal = false; endImpl(object, name, nullptr); } void endToGlobal(JSObjectRef object, const std::string& name, T* pIns) { assert(pIns != nullptr); m_bIsGlobal = false; endImpl(object, name, pIns); m_bIsGlobal = true; } JSObjectRef transferObjPtrToJS(JSContextRef pCtx, T* p_pIns) { assert( !m_bIsGlobal ); JSObjectRef pRet = JSObjectMake(pCtx, m_ClassObject, p_pIns); JSValueRef pProperty = JSValueMakeNumber(pCtx, (double)getTypeID()); JSStringRef pszName = JSStringCreateWithUTF8CString("jsc__cppclstypeid"); JSObjectSetProperty(pCtx, pRet, pszName, pProperty, kJSPropertyAttributeReadOnly|kJSPropertyAttributeDontDelete, 0); JSStringRelease(pszName); return pRet; } static JSCBinder* GetInstance() { if (!s_instance) { s_instance = new JSCBinder(); } return s_instance; } static void ReleaseInstance() { if (s_instance) { delete s_instance; s_instance = NULL; } } private: static JSObjectRef newWrap(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { if( JSCBinder::GetInstance()->m_bIsGlobal ) { return NULL; } return JSCBinder::GetInstance()->transferObjPtrToJS(ctx, new T); } static void destroyWrap(JSObjectRef object) { T *p = (T *)JSObjectGetPrivate(object); delete p; } static bool isInstanceOf(JSContextRef ctx, JSObjectRef constructor, JSValueRef possibleInstance, JSValueRef* exception) { if(!JSValueIsObject(ctx, possibleInstance)) { return false; } JSObjectRef p_pObj = JSValueToObject(ctx, possibleInstance ,NULL); JSStringRef pszName = JSStringCreateWithUTF8CString("jsc__cppclstypeid"); JSValueRef pProperty = JSObjectGetProperty(ctx, p_pObj, pszName, 0); JSStringRelease(pszName); if(0 == pProperty) { return false; } else { int nID = (int)JSValueToNumber(ctx,pProperty, 0); return (nID == (JSCBinder::GetInstance()->getTypeID())); } } static bool hasProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName) { char* strPropertyName = JSCUtil::toCString(ctx, propertyName); if (JSCBinder::GetInstance()->findFunction(strPropertyName)) { return true; } if (JSCBinder::GetInstance()->findGetProperty(strPropertyName)) { return true; } if (JSCBinder::GetInstance()->findSetProperty(strPropertyName)) { return true; } return false; } static JSValueRef getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { T* pThis = (T*)JSObjectGetPrivate(object); if(NULL == pThis ) { return NULL; } char* strPropertyName = JSCUtil::toCString(ctx, propertyName); JSObjectGetPropertyCallback callAsFunction = JSCBinder::GetInstance()->findGetProperty(strPropertyName); if(callAsFunction) { return callAsFunction(ctx, object, propertyName, exception); } return JSCBinder::GetInstance()->findFunction(strPropertyName); } static bool setProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) { T* pThis = (T*)JSObjectGetPrivate(object); if(0 == pThis) { return false; } char* strPropertyName = JSCUtil::toCString(ctx, propertyName); JSObjectSetPropertyCallback callAsFunction = JSCBinder::GetInstance()->findSetProperty(strPropertyName); if( NULL == callAsFunction) { return false; } JSValueRef arguments[1]; arguments[0] = value; callAsFunction(ctx, object, propertyName, value, exception); return true; } private: void _reset() { JSContextRef ctx = JSCUtil::s_ctx; for (FunctionMapItr itr = m_FunctionMap.begin(); itr != m_FunctionMap.end(); itr++) { JSValueUnprotect(ctx, itr->second); } m_FunctionMap.clear(); m_SetPropertyMap.clear(); m_GetPropertyMap.clear(); m_bIsGlobal = false; m_ClassDefine = kJSClassDefinitionEmpty; if (m_ClassObject != nullptr) { JSClassRelease(m_ClassObject); m_ClassObject = nullptr; } } void endImpl(JSObjectRef object, const std::string& name, T *p_pIns) { assert(!name.empty()); m_ClassDefine = kJSClassDefinitionEmpty; m_ClassDefine.attributes = kJSClassAttributeNone; m_ClassDefine.className = name.c_str(); m_ClassDefine.callAsConstructor = JSCBinder::newWrap; m_ClassDefine.finalize = JSCBinder::destroyWrap; m_ClassDefine.hasProperty = JSCBinder::hasProperty; m_ClassDefine.hasInstance = JSCBinder::isInstanceOf; m_ClassDefine.getProperty = JSCBinder::getProperty; m_ClassDefine.setProperty = JSCBinder::setProperty; //m_ClassDefine.callAsFunction = JSCBinder::callAsFunctionCallback; m_ClassObject = JSClassCreate(&m_ClassDefine); JSContextRef pCtx = JSCUtil::s_ctx; JSStringRef jsName = JSStringCreateWithUTF8CString(name.c_str()); JSObjectRef myObject; if( 0 != p_pIns ) { myObject = transferObjPtrToJS(pCtx, p_pIns); } else { myObject = JSObjectMake(pCtx, m_ClassObject, 0); } JSObjectSetProperty( pCtx, object, jsName, myObject, kJSPropertyAttributeNone, NULL ); JSStringRelease(jsName); } JSValueRef findFunction(const std::string& name) { FunctionMapItr iter = m_FunctionMap.find(name); if(iter == m_FunctionMap.end()) { return nullptr; } else { return (*iter).second; } } JSObjectGetPropertyCallback findGetProperty(const std::string& name) { GetPropertyMapItr iter = m_GetPropertyMap.find(name); if(iter == m_GetPropertyMap.end()) { return nullptr; } else { return (*iter).second; } } JSObjectSetPropertyCallback findSetProperty(const std::string& name) { SetPropertyMapItr iter = m_SetPropertyMap.find(name); if(iter == m_SetPropertyMap.end()) { return nullptr; } else { return (*iter).second; } } private: typedef std::unordered_map GetPropertyMap; typedef typename GetPropertyMap::value_type GetPropertyMapValue; typedef typename GetPropertyMap::iterator GetPropertyMapItr; typedef std::pair GetPropertyMapRes; GetPropertyMap m_GetPropertyMap; typedef std::unordered_map SetPropertyMap; typedef typename SetPropertyMap::value_type SetPropertyMapValue; typedef typename SetPropertyMap::iterator SetPropertyMapItr; typedef std::pair SetPropertyMapRes; SetPropertyMap m_SetPropertyMap; typedef std::unordered_map FunctionMap; typedef typename FunctionMap::value_type FunctionMapValue; typedef typename FunctionMap::iterator FunctionMapItr; typedef std::pair FunctionMapRes; FunctionMap m_FunctionMap; bool m_bIsGlobal; JSClassDefinition m_ClassDefine; JSClassRef m_ClassObject; unsigned int m_iTypeID; static JSCBinder* s_instance; }; template JSCBinder* JSCBinder::s_instance = NULL; } #endif