Files
LayaNative2.0/Conch/source/webglPlus/jsc/JSCUtil.h
T
2020-11-11 16:17:13 +08:00

345 lines
12 KiB
C++

#ifndef _JSCUtil_h
#define _JSCUtil_h
#include <unordered_map>
#include <string>
#include <JavaScriptCore/JavaScriptCore.h>
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<typename T>
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<T>* GetInstance()
{
if (!s_instance)
{
s_instance = new JSCBinder<T>();
}
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<T>::GetInstance()->m_bIsGlobal )
{
return NULL;
}
return JSCBinder<T>::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<T>::GetInstance()->getTypeID()));
}
}
static bool hasProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName)
{
char* strPropertyName = JSCUtil::toCString(ctx, propertyName);
if (JSCBinder<T>::GetInstance()->findFunction(strPropertyName))
{
return true;
}
if (JSCBinder<T>::GetInstance()->findGetProperty(strPropertyName))
{
return true;
}
if (JSCBinder<T>::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<T>::GetInstance()->findGetProperty(strPropertyName);
if(callAsFunction)
{
return callAsFunction(ctx, object, propertyName, exception);
}
return JSCBinder<T>::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<T>::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<T>::newWrap;
m_ClassDefine.finalize = JSCBinder<T>::destroyWrap;
m_ClassDefine.hasProperty = JSCBinder<T>::hasProperty;
m_ClassDefine.hasInstance = JSCBinder<T>::isInstanceOf;
m_ClassDefine.getProperty = JSCBinder<T>::getProperty;
m_ClassDefine.setProperty = JSCBinder<T>::setProperty;
//m_ClassDefine.callAsFunction = JSCBinder<T>::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<std::string, JSObjectGetPropertyCallback> GetPropertyMap;
typedef typename GetPropertyMap::value_type GetPropertyMapValue;
typedef typename GetPropertyMap::iterator GetPropertyMapItr;
typedef std::pair<GetPropertyMapItr, bool> GetPropertyMapRes;
GetPropertyMap m_GetPropertyMap;
typedef std::unordered_map<std::string, JSObjectSetPropertyCallback> SetPropertyMap;
typedef typename SetPropertyMap::value_type SetPropertyMapValue;
typedef typename SetPropertyMap::iterator SetPropertyMapItr;
typedef std::pair<SetPropertyMapItr, bool> SetPropertyMapRes;
SetPropertyMap m_SetPropertyMap;
typedef std::unordered_map<std::string, JSValueRef> FunctionMap;
typedef typename FunctionMap::value_type FunctionMapValue;
typedef typename FunctionMap::iterator FunctionMapItr;
typedef std::pair<FunctionMapItr, bool> FunctionMapRes;
FunctionMap m_FunctionMap;
bool m_bIsGlobal;
JSClassDefinition m_ClassDefine;
JSClassRef m_ClassObject;
unsigned int m_iTypeID;
static JSCBinder<T>* s_instance;
};
template<typename T> JSCBinder<T>* JSCBinder<T>::s_instance = NULL;
}
#endif