#ifndef _JSCProxyClass_h #define _JSCProxyClass_h #include #include "JSCProxyTransfer.h" #include "JSCProxyFunction.h" #include "JSCProxyType.h" namespace laya { class JSCGlobal { public: JSCGlobal(){} ~JSCGlobal(){ reset(); } template void addProperty( const std::string& name, T value ){ assert( !name.empty()); JSContextRef pCtx = __TlsData::GetInstance()->GetCurContext(); JSStringRef pJSStrName = JSStringCreateWithUTF8CString( name.c_str() ); JSValueRef pVal = __TransferToJs::ToJs( value ); JSObjectSetProperty( pCtx, JSContextGetGlobalObject( pCtx ), pJSStrName, pVal, kJSPropertyAttributeDontDelete|kJSPropertyAttributeReadOnly, nullptr ); JSStringRelease( pJSStrName ); } template void addFunction( const std::string& name, T fun ){ assert( !name.empty() ); IJSCFunction* pJSCFunction = new JSCFunction( fun ); JSContextRef pCtx = __TlsData::GetInstance()->GetCurContext(); JSStringRef pjsName = JSStringCreateWithUTF8CString( name.c_str() ); JSObjectRef pFunc = JSObjectMakeFunctionWithCallback(pCtx, pjsName, JSObjectCallAsFunctionCallback); JSObjectSetProperty(pCtx, JSContextGetGlobalObject(pCtx), pjsName, pFunc, kJSPropertyAttributeDontDelete|kJSPropertyAttributeReadOnly, nullptr); JSStringRelease( pjsName ); FunctionMapRes rs = m_FunctionMap.insert(FunctionMapValue((unsigned long)pFunc, pJSCFunction)); assert( rs.second ); } IJSCFunction* getFunction(unsigned long func){ FunctionMapItr itrFun = m_FunctionMap.find( func ); if ( itrFun != m_FunctionMap.end() ){ return itrFun->second; } else { return nullptr; } } static JSValueRef JSObjectCallAsFunctionCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){ IJSCFunction* pJSCFunction = JSCGlobal::getInstance()->getFunction( (unsigned long)function ); if ( pJSCFunction == nullptr ){ __JsThrow::Throw(exception,"JSCGlobal::JSObjectCallAsFunctionCallback can't find function"); return nullptr; } return pJSCFunction->call(ctx, function, thisObject, argumentCount, arguments, exception); } static JSCGlobal* getInstance(){ static JSCGlobal instance; return &instance; } void reset(){ FunctionMapItr iter = m_FunctionMap.begin(); for (; iter != m_FunctionMap.end(); iter++){ delete iter->second; } m_FunctionMap.clear(); } private: typedef std::unordered_map FunctionMap; typedef FunctionMap::value_type FunctionMapValue; typedef FunctionMap::iterator FunctionMapItr; typedef std::pair FunctionMapRes; FunctionMap m_FunctionMap; }; template class JSCClass { 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); } class FuncEntry { enum { Max_Arg_Size = 12, Invalid_size = -1, }; IJSCCallback *m_funcs[Max_Arg_Size+1]; int m_iMaxArgSize; public: FuncEntry(){ m_iMaxArgSize = Invalid_size; memset( m_funcs, 0, sizeof(m_funcs) ); } ~FuncEntry(){ reset(); } void reset(){ for(int i=0;i<=Max_Arg_Size;++i){ if( 0 != m_funcs[i] ){ delete m_funcs[i]; m_funcs[i] = 0; } } } void add( IJSCCallback *p_fn ){ assert(p_fn != nullptr && p_fn->getNumArgs() <= Max_Arg_Size && m_funcs[p_fn->getNumArgs()] == nullptr ); if( m_iMaxArgSize < p_fn->getNumArgs() ) m_iMaxArgSize = p_fn->getNumArgs(); m_funcs[p_fn->getNumArgs()] = p_fn; } IJSCCallback *get( int p_iArgNum ){ if( Invalid_size == m_iMaxArgSize ){ return 0; } if( p_iArgNum > m_iMaxArgSize ) p_iArgNum = m_iMaxArgSize; for(;p_iArgNum>=0;p_iArgNum--){ if( 0 != m_funcs[p_iArgNum] ) return m_funcs[p_iArgNum]; } return 0; } }; enum { CallbackType_Property, CallbackType_Function, }; struct CallbackDefine { int m_iType; std::string m_name; IJSCCallback* m_pSetter; IJSCCallback* m_pGetter; FuncEntry m_Method; JSValueRef m_pCallAsFunction; CallbackDefine(const std::string& name, IJSCCallback* setter, IJSCCallback* getter) :m_name(name), m_iType(CallbackType_Property), m_pSetter(setter), m_pGetter(getter),m_pCallAsFunction(nullptr){ } CallbackDefine(const std::string& name,IJSCCallback* method) :m_name(name), m_iType(CallbackType_Function), m_pSetter(nullptr), m_pGetter(nullptr),m_pCallAsFunction(nullptr){ m_Method.add(method); } void addMethod( IJSCCallback *p_pfn ){ m_Method.add(p_pfn); } IJSCCallback *getMethod( size_t p_iArgNum ){ return m_Method.get( (int)p_iArgNum ); } ~CallbackDefine(){ if (m_pGetter){ delete m_pGetter; m_pGetter = nullptr; } if (m_pSetter){ delete m_pSetter; m_pSetter = nullptr; } m_Method.reset(); JSContextRef ctx = __TlsData::GetInstance()->GetCurContext(); if (ctx != nullptr && m_pCallAsFunction != nullptr){ JSValueUnprotect(ctx, m_pCallAsFunction); } } }; struct FixedProperty { private: JSValueRef m_pszName; JSValueRef m_pValue; public: template explicit FixedProperty( const char *p_pszName, _Tp p_Val ) { JSContextRef pCtx = __TlsData::GetInstance()->GetCurContext(); JSStringRef pszName = JSStringCreateWithUTF8CString(p_pszName); m_pszName = JSValueMakeString(pCtx, pszName); JSStringRelease(pszName); m_pValue = __TransferToJs<_Tp>::ToJs(p_Val); JSValueProtect( pCtx, m_pszName ); JSValueProtect( pCtx, m_pValue ); } ~FixedProperty() { JSContextRef pCtx = __TlsData::GetInstance()->GetCurContext(); if( 0 != pCtx ) { if( 0 != m_pszName ) { JSValueUnprotect( pCtx, m_pszName ); } if( 0 != m_pValue ) { JSValueUnprotect( pCtx, m_pValue ); } } } JSStringRef GetName() { return JSValueToStringCopy(__TlsData::GetInstance()->GetCurContext(), m_pszName, 0); } JSValueRef GetValue() { return m_pValue; } }; typedef std::vector FixedProperties; typedef typename FixedProperties::iterator FixedPropertiesIter; FixedProperties m_FixedProperties; JSCClass(){ m_bIsGlobal = false; m_ClassDefine = kJSClassDefinitionEmpty; m_ClassObject = nullptr; m_iTypeID = __hash_BKDR(typeid(T).name()); } ~JSCClass(){ _reset(); } unsigned int getTypeID(){ return m_iTypeID; } static JSCClass* getInstance(){ static JSCClass instance; return &instance; } template void addFixedProperty(const std::string& name,const P& val){ assert(!name.empty()); m_FixedProperties.push_back(new FixedProperty(name.c_str(),val)); } template void addProperty( const std::string& name, G getter, S setter){ assert( !name.empty() ); IJSCCallback* pGetter = new JSCCallback(getter); IJSCCallback* pSetter = nullptr; if (setter != nullptr){ pSetter = new JSCCallback(setter); } PropertyMapRes rs = m_PropertyMap.insert(PropertyMapValue(name,new CallbackDefine(name,pSetter,pGetter))); assert(rs.second && "JSCClass::addProperty name is dumplicate value"); } void addConstructor( IJSCCallback* constructor ){ m_Constructor.add(constructor); } template void addMethod( const std::string& name, M method ){ PropertyMapItr iter = m_PropertyMap.find(name); if ( iter != m_PropertyMap.end() ){ assert( iter->second->m_iType == CallbackType_Function ); iter->second->addMethod(new JSCCallback(method)); return; } CallbackDefine* pProperty = new CallbackDefine(name,new JSCCallback(method)); JSStringRef pName = JSStringCreateWithUTF8CString(name.c_str()); JSContextRef pCtx = __TlsData::GetInstance()->GetCurContext(); pProperty->m_pCallAsFunction = JSObjectMakeFunctionWithCallback(pCtx, pName, JSCClass::callAsFunctionCallback); JSStringRelease(pName); JSValueProtect(pCtx, pProperty->m_pCallAsFunction); PropertyMapRes rsp = m_PropertyMap.insert(PropertyMapValue(name,pProperty)); assert(rsp.second); FunctionMapRes rsf = m_FunctionMap.insert(FunctionMapValue((unsigned long)pProperty->m_pCallAsFunction,pProperty)); assert(rsf.second); } void finish( const std::string& name ){ m_bIsGlobal = false; finishImpl( name, nullptr); } void finishToGlobal( const std::string& name, T* pIns){ assert(pIns != nullptr); m_bIsGlobal = false; finishImpl( name, pIns); m_bIsGlobal = true; } JSObjectRef transferObjPtrToJS( T *p_pIns ){ assert( !m_bIsGlobal ); JSContextRef pCtx = __TlsData::GetInstance()->GetCurContext(); JSObjectRef pRet = JSObjectMake( pCtx, m_ClassObject, p_pIns ); p_pIns->mpJsThis = pRet; JSValueRef pProperty = JSValueMakeNumber(pCtx, (double)getTypeID()); JSStringRef pszName = JSStringCreateWithUTF8CString(__Js_class_typeid_property); JSObjectSetProperty(pCtx, pRet, pszName, pProperty, kJSPropertyAttributeReadOnly|kJSPropertyAttributeDontDelete, 0); JSStringRelease(pszName); if( m_FixedProperties.size() ){ FixedPropertiesIter iter; for(iter=m_FixedProperties.begin();iter!=m_FixedProperties.end();iter++){ JSStringRef sName = (*iter)->GetName(); JSObjectSetProperty(pCtx, pRet, sName, (*iter)->GetValue(), kJSPropertyAttributeReadOnly/*|kJSPropertyAttributeDontDelete*/, 0); //kJSPropertyAttributeDontDelete 就意味着这个值不能改了 fuck JSStringRelease(sName); } } return pRet; } static void reset(){ JSCClass::getInstance()->_reset(); } private: static JSObjectRef newWrap(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){ if( JSCClass::getInstance()->m_bIsGlobal ){ __JsThrow::Throw(exception,"can't new a global object"); return NULL; } IJSCCallback *pfn = JSCClass::getInstance()->m_Constructor.get( (int)argumentCount ); if( 0 == pfn ){ return JSCClass::getInstance()->transferObjPtrToJS(new T); } else{ return pfn->constructor(ctx, constructor, argumentCount, arguments, exception); } } 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; } bool bRet = __CheckClassType::IsTypeOf((JSObjectRef)possibleInstance); return bRet; } static bool hasProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName){ std::string strPropertyName = __TransferToCpp::ToCpp(propertyName); resetJsStrBuf(); if( !strPropertyName.length() ){ return false; } return (0 != JSCClass::getInstance()->findProperty(strPropertyName.c_str())); } static JSValueRef getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception){ T *pThis = (T *)JSObjectGetPrivate( object ); if( 0 == pThis ){ __JsThrow::Throw(exception,"not a global object"); return NULL; } std::string strPropertyName = __TransferToCpp::ToCpp(propertyName); resetJsStrBuf(); if( !strPropertyName.length() ){ __JsThrow::Throw(exception,"GetProperty PropertyName is null"); return NULL; } CallbackDefine *pProperty = JSCClass::getInstance()->findProperty(strPropertyName.c_str()); if( 0 == pProperty ){ __JsThrow::Throw(exception,"GetProperty can't find property"); return NULL; } JSValueRef pRet = NULL; if( CallbackType_Property == pProperty->m_iType ){ // is property pRet = pProperty->m_pGetter->call(ctx, nullptr, object, 0, nullptr, nullptr);//????????????????????? } else { // is function pRet = pProperty->m_pCallAsFunction; } return pRet; } static bool setProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception){ T *pThis = (T *)JSObjectGetPrivate( object ); if( 0 == pThis ){ __JsThrow::Throw(exception,"not a global object"); return false; } std::string sPropertyName = __TransferToCpp::ToCpp(propertyName); resetJsStrBuf(); if( sPropertyName.empty() ){ __JsThrow::Throw(exception,"SetProperty PropertyName is null"); return false; } CallbackDefine *pProperty = JSCClass::getInstance()->findProperty(sPropertyName); if( 0 == pProperty ){ return false; } if( CallbackType_Property != pProperty->m_iType ) { if( pProperty->m_iType == CallbackType_Function ){ JSObjectRef _obj = JSValueToObject(ctx, value, 0); if( JSObjectIsFunction(ctx,_obj) ){ getInstance()->JSDefineFunction[sPropertyName] = _obj; return true; } } __JsThrow::Throw(exception,"SetProperty property is function object"); return false; } if( 0 == pProperty->m_pSetter ){ __JsThrow::Throw(exception,"SetProperty property is read only"); return false; } JSValueRef arguments[1]; arguments[0] = value; pProperty->m_pSetter->call(ctx, nullptr, object, 1, arguments, nullptr); return true; } static JSValueRef callAsFunctionCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){ T *pThis = (T *)JSObjectGetPrivate( thisObject ); if( nullptr == pThis ){ __JsThrow::Throw(exception,"CallAsFunctionCallback this is null"); return JSValueMakeUndefined(ctx); } CallbackDefine *pProperty = JSCClass::getInstance()->findFunction((unsigned long)function); if( pProperty != NULL ){ JSDefineFunctionIter it = getInstance()->JSDefineFunction.find(pProperty->m_name); if( it != getInstance()->JSDefineFunction.end() ){ return JSObjectCallAsFunction(ctx,it->second,thisObject,argumentCount,arguments,0); } } if( nullptr == pProperty ){ __JsThrow::Throw(exception,"CallAsFunctionCallback can't find function"); return JSValueMakeUndefined(ctx); } IJSCCallback *pMethod = pProperty->getMethod(argumentCount); if( 0 != pMethod ){ //__JsThrow::GetInstance()->UpdateException(exception); return pMethod->call(ctx, function, thisObject, argumentCount, arguments, exception); } else { __JsThrow::Throw(exception,"callAsFunctionCallback can't find function"); return JSValueMakeUndefined(ctx); } } private: void _reset(){ if( m_FixedProperties.size() ){ FixedPropertiesIter iter; for(iter=m_FixedProperties.begin();iter!=m_FixedProperties.end();iter++){ delete *iter; } m_FixedProperties.clear(); } for (PropertyMapItr itr = m_PropertyMap.begin(); itr != m_PropertyMap.end(); itr++ ){ delete itr->second; } m_PropertyMap.clear(); m_FunctionMap.clear(); m_bIsGlobal = false; m_ClassDefine = kJSClassDefinitionEmpty; if ( m_ClassObject != nullptr ){ JSClassRelease(m_ClassObject); m_ClassObject = nullptr; } JSDefineFunction.clear(); m_Constructor.reset(); } void finishImpl( 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 = JSCClass::newWrap; m_ClassDefine.finalize = JSCClass::destroyWrap; m_ClassDefine.hasProperty = JSCClass::hasProperty; m_ClassDefine.hasInstance = JSCClass::isInstanceOf; m_ClassDefine.getProperty = JSCClass::getProperty; m_ClassDefine.setProperty = JSCClass::setProperty; m_ClassDefine.callAsFunction = JSCClass::callAsFunctionCallback; m_ClassObject = JSClassCreate(&m_ClassDefine); JSContextRef pCtx = __TlsData::GetInstance()->GetCurContext(); JSStringRef jsName = JSStringCreateWithUTF8CString(name.c_str()); JSObjectRef myObject; if( 0 != p_pIns ){ myObject = transferObjPtrToJS( p_pIns ); p_pIns->mpJsThis = myObject; } else { myObject = JSObjectMake(pCtx, m_ClassObject, 0); } JSObjectSetProperty( pCtx, JSContextGetGlobalObject(pCtx), jsName, myObject, kJSPropertyAttributeNone, NULL );//??? JSStringRelease(jsName); } CallbackDefine *findFunction( unsigned long p_ulObj ){ FunctionMapItr iter = m_FunctionMap.find(p_ulObj); if( iter == m_FunctionMap.end() ){ return nullptr; } else { return (*iter).second; } } CallbackDefine *findProperty( const std::string& name ){ PropertyMapItr iter = m_PropertyMap.find(name); if( iter == m_PropertyMap.end() ){ return nullptr; } else { return (*iter).second; } } private: typedef std::unordered_map PropertyMap; typedef typename PropertyMap::value_type PropertyMapValue; typedef typename PropertyMap::iterator PropertyMapItr; typedef std::pair PropertyMapRes; PropertyMap m_PropertyMap; 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; typedef std::unordered_map::iterator JSDefineFunctionIter; std::unordered_map JSDefineFunction; unsigned int m_iTypeID; FuncEntry m_Constructor; }; } #endif