mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2025-09-24 09:47:55 +00:00
初始化
This commit is contained in:
43
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Base.h
Normal file
43
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Base.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#ifndef UINT64_C
|
||||
#define UINT64_C(value) __CONCAT(value, ULL)
|
||||
#endif
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/Initialization.h"
|
||||
#include "js/Conversions.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <assert.h>
|
||||
|
||||
#include "HelperMacros.h"
|
237
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.cpp
Normal file
237
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "Class.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
namespace se {
|
||||
|
||||
// --- Global Lookup for Constructor Functions
|
||||
|
||||
namespace {
|
||||
// std::unordered_map<std::string, Class *> __clsMap;
|
||||
JSContext* __cx = nullptr;
|
||||
|
||||
bool empty_constructor(JSContext* cx, uint32_t argc, JS::Value* vp)
|
||||
{
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Class*> __allClasses;
|
||||
}
|
||||
|
||||
Class::Class()
|
||||
: _parent(nullptr)
|
||||
, _proto(nullptr)
|
||||
, _parentProto(nullptr)
|
||||
, _ctor(nullptr)
|
||||
, _finalizeOp(nullptr)
|
||||
{
|
||||
memset(&_jsCls, 0, sizeof(_jsCls));
|
||||
memset(&_classOps, 0, sizeof(_classOps));
|
||||
|
||||
__allClasses.push_back(this);
|
||||
}
|
||||
|
||||
Class::~Class()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Class* Class::create(const char* className, Object* obj, Object* parentProto, JSNative ctor)
|
||||
{
|
||||
Class* cls = new Class();
|
||||
if (cls != nullptr && !cls->init(className, obj, parentProto, ctor))
|
||||
{
|
||||
delete cls;
|
||||
cls = nullptr;
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
bool Class::init(const char* clsName, Object* parent, Object *parentProto, JSNative ctor)
|
||||
{
|
||||
_name = clsName;
|
||||
|
||||
_parent = parent;
|
||||
if (_parent != nullptr)
|
||||
_parent->incRef();
|
||||
|
||||
_parentProto = parentProto;
|
||||
if (_parentProto != nullptr)
|
||||
_parentProto->incRef();
|
||||
|
||||
_ctor = ctor;
|
||||
if (_ctor == nullptr)
|
||||
{
|
||||
_ctor = empty_constructor;
|
||||
}
|
||||
|
||||
// SE_LOGD("Class init ( %s ) ...\n", clsName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Class::destroy()
|
||||
{
|
||||
SAFE_DEC_REF(_parent);
|
||||
SAFE_DEC_REF(_proto);
|
||||
SAFE_DEC_REF(_parentProto);
|
||||
}
|
||||
|
||||
bool Class::install()
|
||||
{
|
||||
// assert(__clsMap.find(_name) == __clsMap.end());
|
||||
//
|
||||
// __clsMap.emplace(_name, this);
|
||||
|
||||
_jsCls.name = _name;
|
||||
if (_finalizeOp != nullptr)
|
||||
{
|
||||
_jsCls.flags = JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE; //IDEA: Use JSCLASS_BACKGROUND_FINALIZE to improve GC performance
|
||||
_classOps.finalize = _finalizeOp;
|
||||
}
|
||||
else
|
||||
{
|
||||
_jsCls.flags = JSCLASS_HAS_PRIVATE;
|
||||
}
|
||||
|
||||
_jsCls.cOps = &_classOps;
|
||||
|
||||
JSObject* parentObj = _parentProto != nullptr ? _parentProto->_getJSObject() : nullptr;
|
||||
JS::RootedObject parent(__cx, _parent->_getJSObject());
|
||||
JS::RootedObject parentProto(__cx, parentObj);
|
||||
|
||||
_funcs.push_back(JS_FS_END);
|
||||
_properties.push_back(JS_PS_END);
|
||||
_staticFuncs.push_back(JS_FS_END);
|
||||
_staticProperties.push_back(JS_PS_END);
|
||||
|
||||
JSObject* jsobj = JS_InitClass(__cx, parent, parentProto, &_jsCls, _ctor, 0, _properties.data(), _funcs.data(), _staticProperties.data(), _staticFuncs.data());
|
||||
if (jsobj != nullptr)
|
||||
{
|
||||
_proto = Object::_createJSObject(nullptr, jsobj);
|
||||
// SE_LOGD("_proto: %p, name: %s\n", _proto, _name);
|
||||
_proto->root();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Class::defineFunction(const char *name, JSNative func)
|
||||
{
|
||||
JSFunctionSpec cb = JS_FN(name, func, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE);
|
||||
_funcs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineProperty(const char *name, JSNative getter, JSNative setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, JSPROP_ENUMERATE | JSPROP_PERMANENT);
|
||||
_properties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticFunction(const char *name, JSNative func)
|
||||
{
|
||||
JSFunctionSpec cb = JS_FN(name, func, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE);
|
||||
_staticFuncs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticProperty(const char *name, JSNative getter, JSNative setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, JSPROP_ENUMERATE | JSPROP_PERMANENT);
|
||||
_staticProperties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFinalizeFunction(JSFinalizeOp func)
|
||||
{
|
||||
_finalizeOp = func;
|
||||
return true;
|
||||
}
|
||||
|
||||
// JSObject* Class::_createJSObject(const std::string &clsName, Class** outCls)
|
||||
// {
|
||||
// auto iter = __clsMap.find(clsName);
|
||||
// if (iter == __clsMap.end())
|
||||
// {
|
||||
// *outCls = nullptr;
|
||||
// return nullptr;
|
||||
// }
|
||||
//
|
||||
// Class* thiz = iter->second;
|
||||
// JS::RootedObject obj(__cx, _createJSObjectWithClass(thiz));
|
||||
// *outCls = thiz;
|
||||
// return obj;
|
||||
// }
|
||||
|
||||
JSObject* Class::_createJSObjectWithClass(Class* cls)
|
||||
{
|
||||
JSObject* proto = cls->_proto != nullptr ? cls->_proto->_getJSObject() : nullptr;
|
||||
JS::RootedObject jsProto(__cx, proto);
|
||||
JS::RootedObject obj(__cx, JS_NewObjectWithGivenProto(__cx, &cls->_jsCls, jsProto));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Class::setContext(JSContext* cx)
|
||||
{
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
Object *Class::getProto()
|
||||
{
|
||||
return _proto;
|
||||
}
|
||||
|
||||
JSFinalizeOp Class::_getFinalizeCb() const
|
||||
{
|
||||
return _finalizeOp;
|
||||
}
|
||||
|
||||
void Class::cleanup()
|
||||
{
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
cls->destroy();
|
||||
}
|
||||
|
||||
se::ScriptEngine::getInstance()->addAfterCleanupHook([](){
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
delete cls;
|
||||
}
|
||||
__allClasses.clear();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
154
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.hpp
Normal file
154
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.hpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* se::Class represents a definition of how to create a native binding object.
|
||||
*/
|
||||
class Class final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a class used for creating relevant native binding objects.
|
||||
* @param[in] className A null-terminated UTF8 string containing the class's name.
|
||||
* @param[in] obj The object that current class proto object attaches to. Should not be nullptr.
|
||||
* @param[in] parentProto The parent proto object that current class inherits from. Passing nullptr means a new class has no parent.
|
||||
* @param[in] ctor A callback to invoke when your constructor is used in a 'new' expression. Pass nullptr to use the default object constructor.
|
||||
* @return A class instance used for creating relevant native binding objects.
|
||||
* @note Don't need to delete the pointer return by this method, it's managed internally.
|
||||
*/
|
||||
static Class* create(const char* className, Object* obj, Object* parentProto, JSNative ctor);
|
||||
|
||||
/**
|
||||
* @brief Defines a member function with a callback. Each objects created by class will have this function property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the function name.
|
||||
* @param[in] func A callback to invoke when the property is called as a function.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFunction(const char *name, JSNative func);
|
||||
|
||||
/**
|
||||
* @brief Defines a property with accessor callbacks. Each objects created by class will have this property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the property name.
|
||||
* @param[in] getter A callback to invoke when the property is read.
|
||||
* @param[in] setter A callback to invoke when the property is set.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineProperty(const char *name, JSNative getter, JSNative setter);
|
||||
|
||||
/**
|
||||
* @brief Defines a static function with a callback. Only JavaScript constructor object will have this function.
|
||||
* @param[in] name A null-terminated UTF8 string containing the function name.
|
||||
* @param[in] func A callback to invoke when the constructor's property is called as a function.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineStaticFunction(const char *name, JSNative func);
|
||||
|
||||
/**
|
||||
* @brief Defines a static property with accessor callbacks. Only JavaScript constructor object will have this property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the property name.
|
||||
* @param[in] getter A callback to invoke when the constructor's property is read.
|
||||
* @param[in] setter A callback to invoke when the constructor's property is set.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineStaticProperty(const char *name, JSNative getter, JSNative setter);
|
||||
|
||||
/**
|
||||
* @brief Defines the finalize function with a callback.
|
||||
* @param[in] func The callback to invoke when a JavaScript object is garbage collected.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFinalizeFunction(JSFinalizeOp func);
|
||||
|
||||
/**
|
||||
* @brief Installs class to JavaScript VM.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note After this method, an object could be created by `var foo = new Foo();`.
|
||||
*/
|
||||
bool install();
|
||||
|
||||
/**
|
||||
* @brief Gets the proto object of this class.
|
||||
* @return The proto object of this class.
|
||||
* @note Don't need to be released in user code.
|
||||
*/
|
||||
Object* getProto();
|
||||
|
||||
/**
|
||||
* @brief Gets the class name.
|
||||
* @return The class name.
|
||||
*/
|
||||
const char* getName() const { return _name; }
|
||||
|
||||
// Private API used in wrapper
|
||||
JSFinalizeOp _getFinalizeCb() const;
|
||||
//
|
||||
private:
|
||||
Class();
|
||||
~Class();
|
||||
|
||||
bool init(const char* clsName, Object* obj, Object* parentProto, JSNative ctor);
|
||||
void destroy();
|
||||
|
||||
// static JSObject* _createJSObject(const std::string &clsName, Class** outCls);
|
||||
static JSObject* _createJSObjectWithClass(Class* cls);
|
||||
|
||||
static void setContext(JSContext* cx);
|
||||
static void cleanup();
|
||||
|
||||
const char* _name;
|
||||
Object* _parent;
|
||||
Object* _proto;
|
||||
Object* _parentProto;
|
||||
|
||||
JSNative _ctor;
|
||||
|
||||
JSClass _jsCls;
|
||||
JSClassOps _classOps;
|
||||
|
||||
std::vector<JSFunctionSpec> _funcs;
|
||||
std::vector<JSFunctionSpec> _staticFuncs;
|
||||
std::vector<JSPropertySpec> _properties;
|
||||
std::vector<JSPropertySpec> _staticProperties;
|
||||
JSFinalizeOp _finalizeOp;
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
@@ -0,0 +1,203 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
extern uint32_t __jsbInvocationCount;
|
||||
|
||||
#define SAFE_INC_REF(obj) if (obj != nullptr) obj->incRef()
|
||||
#define SAFE_DEC_REF(obj) if ((obj) != nullptr) { (obj)->decRef(); (obj) = nullptr; }
|
||||
|
||||
#define _SE(name) name##Registry
|
||||
|
||||
|
||||
#define SE_DECLARE_FUNC(funcName) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp)
|
||||
|
||||
#define SE_BIND_FUNC(funcName) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
JS::RootedObject _thizObj(_cx, _thiz.toObjectOrNull()); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_cx, _thizObj); \
|
||||
se::State state(nativeThisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::setReturnValue(_cx, state.rval(), _argv); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSFreeOp* _fop, JSObject* _obj);
|
||||
|
||||
#define SE_BIND_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSFreeOp* _fop, JSObject* _obj) \
|
||||
{ \
|
||||
void* nativeThisObject = JS_GetPrivate(_obj); \
|
||||
bool ret = false; \
|
||||
if (nativeThisObject == nullptr) \
|
||||
return;\
|
||||
se::State state(nativeThisObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_CTOR(funcName, cls, finalizeCb) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
se::Object* thisObject = se::Object::createObjectWithClass(cls); \
|
||||
_argv.rval().setObject(*thisObject->_getJSObject()); \
|
||||
se::State state(thisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (ret) \
|
||||
{ \
|
||||
se::Value _property; \
|
||||
bool _found = false; \
|
||||
_found = thisObject->getProperty("_ctor", &_property); \
|
||||
if (_found) _property.toObject()->call(args, thisObject); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_SUB_CLS_CTOR(funcName, cls, finalizeCb) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
se::Object* thisObject = se::Object::_createJSObject(cls, _thiz.toObjectOrNull()); \
|
||||
thisObject->_setFinalizeCallback(finalizeCb##Registry); \
|
||||
se::State state(thisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (ret) \
|
||||
{ \
|
||||
se::Value _property; \
|
||||
bool _found = false; \
|
||||
_found = thisObject->getProperty("_ctor", &_property); \
|
||||
if (_found) _property.toObject()->call(args, thisObject); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_GET(funcName) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
JS::RootedObject _thizObj(_cx, _thiz.toObjectOrNull()); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_cx, _thizObj); \
|
||||
se::State state(nativeThisObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::setReturnValue(_cx, state.rval(), _argv); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_SET(funcName) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned _argc, JS::Value *_vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(_argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
JS::RootedObject _thizObj(_cx, _thiz.toObjectOrNull()); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_cx, _thizObj); \
|
||||
se::Value data; \
|
||||
se::internal::jsToSeValue(_cx, _argv[0], &data); \
|
||||
se::ValueArray args; \
|
||||
args.push_back(std::move(data)); \
|
||||
se::State state(nativeThisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_TYPE_NAME(t) typeid(t).name()
|
||||
|
||||
#define SE_QUOTEME_(x) #x
|
||||
#define SE_QUOTEME(x) SE_QUOTEME_(x)
|
||||
|
||||
#define SE_REPORT_ERROR(fmt, ...) \
|
||||
SE_LOGD("ERROR (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__); \
|
||||
JS_ReportErrorUTF8(se::ScriptEngine::getInstance()->_getContext(), fmt, ##__VA_ARGS__)
|
||||
|
||||
#if COCOS2D_DEBUG > 0
|
||||
|
||||
#define SE_ASSERT(cond, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
SE_LOGE("ASSERT (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__); \
|
||||
assert(false); \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
#else
|
||||
|
||||
#define SE_ASSERT(cond, fmt, ...)
|
||||
|
||||
#endif // #if COCOS2D_DEBUG > 0
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
722
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.cpp
Normal file
722
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.cpp
Normal file
@@ -0,0 +1,722 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "Object.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "Utils.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "../MappingUtils.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
std::unordered_map<Object*, void*> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
namespace {
|
||||
JSContext *__cx = nullptr;
|
||||
|
||||
void get_or_create_js_obj(JSContext* cx, JS::HandleObject obj, const std::string &name, JS::MutableHandleObject jsObj)
|
||||
{
|
||||
JS::RootedValue nsval(cx);
|
||||
JS_GetProperty(cx, obj, name.c_str(), &nsval);
|
||||
if (nsval.isNullOrUndefined()) {
|
||||
jsObj.set(JS_NewPlainObject(cx));
|
||||
nsval = JS::ObjectValue(*jsObj);
|
||||
JS_SetProperty(cx, obj, name.c_str(), nsval);
|
||||
} else {
|
||||
jsObj.set(nsval.toObjectOrNull());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object::Object()
|
||||
: _root(nullptr)
|
||||
, _privateData(nullptr)
|
||||
, _cls(nullptr)
|
||||
, _finalizeCb(nullptr)
|
||||
, _rootCount(0)
|
||||
{
|
||||
_currentVMId = ScriptEngine::getInstance()->getVMId();
|
||||
}
|
||||
|
||||
Object::~Object()
|
||||
{
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
unprotect();
|
||||
}
|
||||
|
||||
auto iter = __objectMap.find(this);
|
||||
if (iter != __objectMap.end())
|
||||
{
|
||||
__objectMap.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::init(Class* cls, JSObject* obj)
|
||||
{
|
||||
_cls = cls;
|
||||
_heap = obj;
|
||||
|
||||
assert(__objectMap.find(this) == __objectMap.end());
|
||||
__objectMap.emplace(this, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Object* Object::_createJSObject(Class* cls, JSObject* obj)
|
||||
{
|
||||
Object* ret = new Object();
|
||||
if (!ret->init(cls, obj))
|
||||
{
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object* Object::createPlainObject()
|
||||
{
|
||||
Object* obj = Object::_createJSObject(nullptr, JS_NewPlainObject(__cx));
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createObjectWithClass(Class* cls)
|
||||
{
|
||||
JSObject* jsobj = Class::_createJSObjectWithClass(cls);
|
||||
Object* obj = Object::_createJSObject(cls, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::getObjectWithPtr(void* ptr)
|
||||
{
|
||||
Object* obj = nullptr;
|
||||
auto iter = NativePtrToObjectMap::find(ptr);
|
||||
if (iter != NativePtrToObjectMap::end())
|
||||
{
|
||||
obj = iter->second;
|
||||
obj->incRef();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayObject(size_t length)
|
||||
{
|
||||
JS::RootedObject jsobj(__cx, JS_NewArrayObject(__cx, length));
|
||||
Object* obj = Object::_createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayBufferObject(void* data, size_t byteLength)
|
||||
{
|
||||
JS::RootedObject jsobj(__cx, JS_NewArrayBuffer(__cx, (uint32_t)byteLength));
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
uint8_t* tmpData = JS_GetArrayBufferData(jsobj, &isShared, nogc);
|
||||
if (data)
|
||||
{
|
||||
memcpy((void*)tmpData, (const void*)data, byteLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset((void*)tmpData, 0, byteLength);
|
||||
}
|
||||
Object* obj = Object::_createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createTypedArray(TypedArrayType type, void* data, size_t byteLength)
|
||||
{
|
||||
if (type == TypedArrayType::NONE)
|
||||
{
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED)
|
||||
{
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject* arr = nullptr;
|
||||
void* tmpData = nullptr;
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8:
|
||||
arr = JS_NewInt8Array(__cx, (uint32_t)byteLength);
|
||||
tmpData = JS_GetInt8ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::INT16:
|
||||
arr = JS_NewInt16Array(__cx, (uint32_t)byteLength/2);
|
||||
tmpData = JS_GetInt16ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::INT32:
|
||||
arr = JS_NewInt32Array(__cx, (uint32_t)byteLength/4);
|
||||
tmpData = JS_GetInt32ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::UINT8:
|
||||
arr = JS_NewUint8Array(__cx, (uint32_t)byteLength);
|
||||
tmpData = JS_GetUint8ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::UINT16:
|
||||
arr = JS_NewUint16Array(__cx, (uint32_t)byteLength/2);
|
||||
tmpData = JS_GetUint16ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::UINT32:
|
||||
arr = JS_NewUint32Array(__cx, (uint32_t)byteLength/4);
|
||||
tmpData = JS_GetUint32ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::FLOAT32:
|
||||
arr = JS_NewFloat32Array(__cx, (uint32_t)byteLength/4);
|
||||
tmpData = JS_GetFloat32ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::FLOAT64:
|
||||
arr = JS_NewFloat64Array(__cx, (uint32_t)byteLength/8);
|
||||
tmpData = JS_GetFloat64ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
|
||||
//If data has content,then will copy data into buffer,or will only clear buffer.
|
||||
if (data) {
|
||||
memcpy(tmpData, (const void*)data, byteLength);
|
||||
}else{
|
||||
memset(tmpData, 0, byteLength);
|
||||
}
|
||||
|
||||
Object* obj = Object::_createJSObject(nullptr, arr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createUint8TypedArray(uint8_t* data, size_t dataCount)
|
||||
{
|
||||
return createTypedArray(TypedArrayType::UINT8, data, dataCount);
|
||||
}
|
||||
|
||||
Object* Object::createJSONObject(const std::string& jsonStr)
|
||||
{
|
||||
Value strVal(jsonStr);
|
||||
JS::RootedValue jsStr(__cx);
|
||||
internal::seToJsValue(__cx, strVal, &jsStr);
|
||||
JS::RootedValue jsObj(__cx);
|
||||
JS::RootedString rootedStr(__cx, jsStr.toString());
|
||||
Object* obj = nullptr;
|
||||
if (JS_ParseJSON(__cx, rootedStr, &jsObj))
|
||||
{
|
||||
obj = Object::_createJSObject(nullptr, jsObj.toObjectOrNull());
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Object::_setFinalizeCallback(JSFinalizeOp finalizeCb)
|
||||
{
|
||||
_finalizeCb = finalizeCb;
|
||||
}
|
||||
|
||||
bool Object::getProperty(const char* name, Value* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
data->setUndefined();
|
||||
|
||||
JSObject* jsobj = _getJSObject();
|
||||
if (jsobj == nullptr)
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, jsobj);
|
||||
|
||||
bool found = false;
|
||||
bool ok = JS_HasProperty(__cx, object, name, &found);
|
||||
|
||||
if (!ok || !found)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedValue rcValue(__cx);
|
||||
ok = JS_GetProperty(__cx, object, name, &rcValue);
|
||||
|
||||
if (ok && data)
|
||||
{
|
||||
internal::jsToSeValue(__cx, rcValue, data);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::setProperty(const char* name, const Value& v)
|
||||
{
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
|
||||
JS::RootedValue value(__cx);
|
||||
internal::seToJsValue(__cx, v, &value);
|
||||
return JS_SetProperty(__cx, object, name, value);
|
||||
}
|
||||
|
||||
bool Object::defineProperty(const char *name, JSNative getter, JSNative setter)
|
||||
{
|
||||
JS::RootedObject jsObj(__cx, _getJSObject());
|
||||
return JS_DefineProperty(__cx, jsObj, name, JS::UndefinedHandleValue, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_SHARED, getter, setter);
|
||||
}
|
||||
|
||||
bool Object::call(const ValueArray& args, Object* thisObject, Value* rval/* = nullptr*/)
|
||||
{
|
||||
assert(isFunction());
|
||||
|
||||
JS::AutoValueVector jsarr(__cx);
|
||||
jsarr.reserve(args.size());
|
||||
internal::seToJsArgs(__cx, args, &jsarr);
|
||||
|
||||
JS::RootedObject contextObject(__cx);
|
||||
if (thisObject != nullptr)
|
||||
{
|
||||
contextObject.set(thisObject->_getJSObject());
|
||||
}
|
||||
|
||||
JSObject* funcObj = _getJSObject();
|
||||
JS::RootedValue func(__cx, JS::ObjectValue(*funcObj));
|
||||
JS::RootedValue rcValue(__cx);
|
||||
|
||||
bool ok = JS_CallFunctionValue(__cx, contextObject, func, jsarr, &rcValue);
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (rval != nullptr)
|
||||
internal::jsToSeValue(__cx, rcValue, rval);
|
||||
}
|
||||
else
|
||||
{
|
||||
se::ScriptEngine::getInstance()->clearException();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::defineFunction(const char *funcName, JSNative func)
|
||||
{
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
bool ok = JS_DefineFunction(__cx, object, funcName, func, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::getArrayLength(uint32_t* length) const
|
||||
{
|
||||
assert(length != nullptr);
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
if (JS_GetArrayLength(__cx, object, length))
|
||||
return true;
|
||||
|
||||
*length = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::getArrayElement(uint32_t index, Value* data) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
JS::RootedValue rcValue(__cx);
|
||||
if (JS_GetElement(__cx, object, index, &rcValue))
|
||||
{
|
||||
internal::jsToSeValue(__cx, rcValue, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
data->setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::setArrayElement(uint32_t index, const Value& data)
|
||||
{
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedValue jsval(__cx);
|
||||
internal::seToJsValue(__cx, data, &jsval);
|
||||
JS::RootedObject thisObj(__cx, _getJSObject());
|
||||
return JS_SetElement(__cx, thisObj, index, jsval);
|
||||
}
|
||||
|
||||
bool Object::isFunction() const
|
||||
{
|
||||
return JS_ObjectIsFunction(__cx, _getJSObject());
|
||||
}
|
||||
|
||||
bool Object::_isNativeFunction(JSNative func) const
|
||||
{
|
||||
JSObject* obj = _getJSObject();
|
||||
return JS_ObjectIsFunction(__cx, obj) && JS_IsNativeFunction(obj, func);
|
||||
}
|
||||
|
||||
bool Object::isTypedArray() const
|
||||
{
|
||||
return JS_IsTypedArrayObject(_getJSObject());
|
||||
}
|
||||
|
||||
Object::TypedArrayType Object::getTypedArrayType() const
|
||||
{
|
||||
TypedArrayType ret = TypedArrayType::NONE;
|
||||
JSObject* obj = _getJSObject();
|
||||
if (JS_IsInt8Array(obj))
|
||||
ret = TypedArrayType::INT8;
|
||||
else if (JS_IsInt16Array(obj))
|
||||
ret = TypedArrayType::INT16;
|
||||
else if (JS_IsInt32Array(obj))
|
||||
ret = TypedArrayType::INT32;
|
||||
else if (JS_IsUint8Array(obj))
|
||||
ret = TypedArrayType::UINT8;
|
||||
else if (JS_IsUint8ClampedArray(obj))
|
||||
ret = TypedArrayType::UINT8_CLAMPED;
|
||||
else if (JS_IsUint16Array(obj))
|
||||
ret = TypedArrayType::UINT16;
|
||||
else if (JS_IsUint32Array(obj))
|
||||
ret = TypedArrayType::UINT32;
|
||||
else if (JS_IsFloat32Array(obj))
|
||||
ret = TypedArrayType::FLOAT32;
|
||||
else if (JS_IsFloat64Array(obj))
|
||||
ret = TypedArrayType::FLOAT64;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getTypedArrayData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(JS_IsArrayBufferViewObject(_getJSObject()));
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
*ptr = (uint8_t*)JS_GetArrayBufferViewData(_getJSObject(), &isShared, nogc);
|
||||
*length = JS_GetArrayBufferViewByteLength(_getJSObject());
|
||||
return (*ptr != nullptr);
|
||||
}
|
||||
|
||||
bool Object::isArray() const
|
||||
{
|
||||
JS::RootedValue value(__cx, JS::ObjectValue(*_getJSObject()));
|
||||
bool isArray = false;
|
||||
return JS_IsArrayObject(__cx, value, &isArray) && isArray;
|
||||
}
|
||||
|
||||
bool Object::isArrayBuffer() const
|
||||
{
|
||||
return JS_IsArrayBufferObject(_getJSObject());
|
||||
}
|
||||
|
||||
bool Object::getArrayBufferData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(isArrayBuffer());
|
||||
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
*ptr = (uint8_t*)JS_GetArrayBufferData(_getJSObject(), &isShared, nogc);
|
||||
*length = JS_GetArrayBufferByteLength(_getJSObject());
|
||||
return (*ptr != nullptr);
|
||||
}
|
||||
|
||||
bool Object::getAllKeys(std::vector<std::string>* allKeys) const
|
||||
{
|
||||
assert(allKeys != nullptr);
|
||||
JS::RootedObject jsobj(__cx, _getJSObject());
|
||||
JS::Rooted<JS::IdVector> props(__cx, JS::IdVector(__cx));
|
||||
if (!JS_Enumerate(__cx, jsobj, &props))
|
||||
return false;
|
||||
|
||||
std::vector<std::string> keys;
|
||||
for (size_t i = 0, length = props.length(); i < length; ++i)
|
||||
{
|
||||
JS::RootedId id(__cx, props[i]);
|
||||
JS::RootedValue keyVal(__cx);
|
||||
JS_IdToValue(__cx, id, &keyVal);
|
||||
|
||||
if (JSID_IS_STRING(id))
|
||||
{
|
||||
JS::RootedString rootedKeyVal(__cx, keyVal.toString());
|
||||
allKeys->push_back(internal::jsToStdString(__cx, rootedKeyVal));
|
||||
}
|
||||
else if (JSID_IS_INT(id))
|
||||
{
|
||||
char buf[50] = {0};
|
||||
snprintf(buf, sizeof(buf), "%d", keyVal.toInt32());
|
||||
allKeys->push_back(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Object::getPrivateData() const
|
||||
{
|
||||
if (_privateData == nullptr)
|
||||
{
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
const_cast<Object*>(this)->_privateData = internal::getPrivate(__cx, obj);
|
||||
}
|
||||
return _privateData;
|
||||
}
|
||||
|
||||
void Object::setPrivateData(void* data)
|
||||
{
|
||||
assert(_privateData == nullptr);
|
||||
assert(NativePtrToObjectMap::find(data) == NativePtrToObjectMap::end());
|
||||
assert(_cls != nullptr);
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
internal::setPrivate(__cx, obj, data, _finalizeCb);
|
||||
|
||||
NativePtrToObjectMap::emplace(data, this);
|
||||
_privateData = data;
|
||||
}
|
||||
|
||||
void Object::clearPrivateData(bool clearMapping)
|
||||
{
|
||||
if (_privateData != nullptr)
|
||||
{
|
||||
if (clearMapping)
|
||||
NativePtrToObjectMap::erase(_privateData);
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
internal::clearPrivate(__cx, obj);
|
||||
_privateData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Object::setContext(JSContext *cx)
|
||||
{
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
// static
|
||||
void Object::cleanup()
|
||||
{
|
||||
for (const auto& e : __objectMap)
|
||||
{
|
||||
e.first->reset();
|
||||
}
|
||||
|
||||
ScriptEngine::getInstance()->addAfterCleanupHook([](){
|
||||
__objectMap.clear();
|
||||
const auto& instance = NativePtrToObjectMap::instance();
|
||||
for (const auto& e : instance)
|
||||
{
|
||||
e.second->decRef();
|
||||
}
|
||||
NativePtrToObjectMap::clear();
|
||||
NonRefNativePtrCreatedByCtorMap::clear();
|
||||
__cx = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
JSObject* Object::_getJSObject() const
|
||||
{
|
||||
return isRooted() ? _root->get() : _heap.get();
|
||||
}
|
||||
|
||||
void Object::root()
|
||||
{
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
protect();
|
||||
}
|
||||
++_rootCount;
|
||||
}
|
||||
|
||||
void Object::unroot()
|
||||
{
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
--_rootCount;
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
unprotect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Object::protect()
|
||||
{
|
||||
assert(_root == nullptr);
|
||||
assert(_heap != JS::GCPolicy<JSObject*>::initial());
|
||||
|
||||
_root = new JS::PersistentRootedObject(__cx, _heap);
|
||||
_heap = JS::GCPolicy<JSObject*>::initial();
|
||||
}
|
||||
|
||||
void Object::unprotect()
|
||||
{
|
||||
if (_root == nullptr)
|
||||
return;
|
||||
|
||||
assert(_currentVMId == ScriptEngine::getInstance()->getVMId());
|
||||
assert(_heap == JS::GCPolicy<JSObject*>::initial());
|
||||
_heap = *_root;
|
||||
delete _root;
|
||||
_root = nullptr;
|
||||
}
|
||||
|
||||
void Object::reset()
|
||||
{
|
||||
if (_root != nullptr)
|
||||
{
|
||||
delete _root;
|
||||
_root = nullptr;
|
||||
}
|
||||
|
||||
_heap = JS::GCPolicy<JSObject*>::initial();
|
||||
}
|
||||
|
||||
/* Tracing makes no sense in the rooted case, because JS::PersistentRooted
|
||||
* already takes care of that. */
|
||||
void Object::trace(JSTracer* tracer, void* data)
|
||||
{
|
||||
assert(!isRooted());
|
||||
JS::TraceEdge(tracer, &_heap, "ccobj tracing");
|
||||
}
|
||||
|
||||
/* If not tracing, then you must call this method during GC in order to
|
||||
* update the object's location if it was moved, or null it out if it was
|
||||
* finalized. If the object was finalized, returns true. */
|
||||
bool Object::updateAfterGC(void* data)
|
||||
{
|
||||
assert(!isRooted());
|
||||
bool isGarbageCollected = false;
|
||||
internal::PrivateData* internalData = nullptr;
|
||||
|
||||
JSObject* oldPtr = _heap.unbarrieredGet();
|
||||
if (_heap.unbarrieredGet() != nullptr)
|
||||
JS_UpdateWeakPointerAfterGC(&_heap);
|
||||
|
||||
JSObject* newPtr = _heap.unbarrieredGet();
|
||||
|
||||
// IDEA: test to see ggc
|
||||
if (oldPtr != nullptr && newPtr != nullptr)
|
||||
{
|
||||
assert(oldPtr == newPtr);
|
||||
}
|
||||
isGarbageCollected = (newPtr == nullptr);
|
||||
if (isGarbageCollected && internalData != nullptr)
|
||||
{
|
||||
free(internalData);
|
||||
}
|
||||
return isGarbageCollected;
|
||||
}
|
||||
|
||||
bool Object::isRooted() const
|
||||
{
|
||||
return _rootCount > 0;
|
||||
}
|
||||
|
||||
bool Object::strictEquals(Object* o) const
|
||||
{
|
||||
JSObject* thisObj = _getJSObject();
|
||||
JSObject* oThisObj = o->_getJSObject();
|
||||
if ((thisObj == nullptr || oThisObj == nullptr) && thisObj != oThisObj)
|
||||
return false;
|
||||
|
||||
assert(thisObj);
|
||||
assert(oThisObj);
|
||||
JS::RootedValue v1(__cx, JS::ObjectValue(*_getJSObject()));
|
||||
JS::RootedValue v2(__cx, JS::ObjectValue(*o->_getJSObject()));
|
||||
bool same = false;
|
||||
bool ok = JS_SameValue(__cx, v1, v2, &same);
|
||||
return ok && same;
|
||||
}
|
||||
|
||||
bool Object::attachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("registerNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::detachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("unregisterNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Object::toString() const
|
||||
{
|
||||
std::string ret;
|
||||
if (isFunction() || isArray() || isTypedArray())
|
||||
{
|
||||
JS::RootedValue val(__cx, JS::ObjectOrNullValue(_getJSObject()));
|
||||
internal::forceConvertJsValueToStdString(__cx, val, &ret);
|
||||
}
|
||||
else if (isArrayBuffer())
|
||||
{
|
||||
ret = "[object ArrayBuffer]";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "[object Object]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
399
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.hpp
Normal file
399
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.hpp
Normal file
@@ -0,0 +1,399 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "Base.h"
|
||||
#include "../Value.hpp"
|
||||
#include "../RefCounter.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Class;
|
||||
|
||||
/**
|
||||
* se::Object represents JavaScript Object.
|
||||
*/
|
||||
class Object final : public RefCounter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a JavaScript Object like `{} or new Object()`.
|
||||
* @return A JavaScript Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createPlainObject();
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Array Object like `[] or new Array()`.
|
||||
* @param[in] length The initical length of array.
|
||||
* @return A JavaScript Array Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createArrayObject(size_t length);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Typed Array Object with uint8 format from an existing pointer.
|
||||
* @param[in] bytes A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A JavaScript Typed Array Object whose backing store is the same as the one pointed data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
* @deprecated This method is deprecated, please use `se::Object::createTypedArray` instead.
|
||||
*/
|
||||
SE_DEPRECATED_ATTRIBUTE static Object* createUint8TypedArray(uint8_t* data, size_t byteLength);
|
||||
|
||||
enum class TypedArrayType
|
||||
{
|
||||
NONE,
|
||||
INT8,
|
||||
INT16,
|
||||
INT32,
|
||||
UINT8,
|
||||
UINT8_CLAMPED,
|
||||
UINT16,
|
||||
UINT32,
|
||||
FLOAT32,
|
||||
FLOAT64
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Typed Array Object with specified format from an existing pointer,
|
||||
if provide a null pointer,then will create a empty JavaScript Typed Array Object.
|
||||
* @param[in] type The format of typed array.
|
||||
* @param[in] data A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A JavaScript Typed Array Object whose backing store is the same as the one pointed data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createTypedArray(TypedArrayType type, void* data, size_t byteLength);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Array Buffer object from an existing pointer.
|
||||
* @param[in] bytes A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A Array Buffer Object whose backing store is the same as the one pointed to data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createArrayBufferObject(void* data, size_t byteLength);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Object from a JSON formatted string.
|
||||
* @param[in] jsonStr The utf-8 string containing the JSON string to be parsed.
|
||||
* @return A JavaScript Object containing the parsed value, or nullptr if the input is invalid.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createJSONObject(const std::string& jsonStr);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Native Binding Object from an existing se::Class instance.
|
||||
* @param[in] cls The se::Class instance which stores native callback informations.
|
||||
* @return A JavaScript Native Binding Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createObjectWithClass(Class* cls);
|
||||
|
||||
/**
|
||||
* @brief Gets a se::Object from an existing native object pointer.
|
||||
* @param[in] ptr The native object pointer associated with the se::Object
|
||||
* @return A JavaScript Native Binding Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* getObjectWithPtr(void* ptr);
|
||||
|
||||
/**
|
||||
* @brief Gets a property from an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[out] value The property's value if object has the property, otherwise the undefined value.
|
||||
* @return true if object has the property, otherwise false.
|
||||
*/
|
||||
bool getProperty(const char* name, Value* value);
|
||||
|
||||
/**
|
||||
* @brief Sets a property to an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[in] value A value to be used as the property's value.
|
||||
* @return true if the property is set successfully, otherwise false.
|
||||
*/
|
||||
bool setProperty(const char* name, const Value& value);
|
||||
|
||||
/**
|
||||
* @brief Defines a property with native accessor callbacks for an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[in] getter The native callback for getter.
|
||||
* @param[in] setter The native callback for setter.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineProperty(const char *name, JSNative getter, JSNative setter);
|
||||
|
||||
/**
|
||||
* @brief Defines a function with a native callback for an object.
|
||||
* @param[in] funcName A utf-8 string containing the function name.
|
||||
* @param[in] func The native callback triggered by JavaScript code.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFunction(const char *funcName, JSNative func);
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object can be called as a function.
|
||||
* @return true if object can be called as a function, otherwise false.
|
||||
*/
|
||||
bool isFunction() const;
|
||||
|
||||
/**
|
||||
* @brief Calls an object as a function.
|
||||
* @param[in] args A se::Value array of arguments to pass to the function. Pass se::EmptyValueArray if argumentCount is 0.
|
||||
* @param[in] thisObject The object to use as "this," or NULL to use the global object as "this."
|
||||
* @param[out] rval The se::Value that results from calling object as a function, passing nullptr if return value is ignored.
|
||||
* @return true if object is a function and there isn't any errors, otherwise false.
|
||||
*/
|
||||
bool call(const ValueArray& args, Object* thisObject, Value* rval = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is an array.
|
||||
* @return true if object is an array, otherwise false.
|
||||
*/
|
||||
bool isArray() const;
|
||||
|
||||
/**
|
||||
* @brief Gets array length of an array object.
|
||||
* @param[out] length The array length to be stored. It's set to 0 if there is an error.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayLength(uint32_t* length) const;
|
||||
|
||||
/**
|
||||
* @brief Gets an element from an array object by numeric index.
|
||||
* @param[in] index An integer value for index.
|
||||
* @param[out] data The se::Value to be stored for the element in certain index.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayElement(uint32_t index, Value* data) const;
|
||||
|
||||
/**
|
||||
* @brief Sets an element to an array object by numeric index.
|
||||
* @param[in] index An integer value for index.
|
||||
* @param[in] data The se::Value to be set to array with certain index.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool setArrayElement(uint32_t index, const Value& data);
|
||||
|
||||
/** @brief Tests whether an object is a typed array.
|
||||
* @return true if object is a typed array, otherwise false.
|
||||
*/
|
||||
bool isTypedArray() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the type of a typed array object.
|
||||
* @return The type of a typed array object.
|
||||
*/
|
||||
TypedArrayType getTypedArrayType() const;
|
||||
|
||||
/**
|
||||
* @brief Gets backing store of a typed array object.
|
||||
* @param[out] ptr A temporary pointer to the backing store of a JavaScript Typed Array object.
|
||||
* @param[out] length The byte length of a JavaScript Typed Array object.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getTypedArrayData(uint8_t** ptr, size_t* length) const;
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is an array buffer object.
|
||||
* @return true if object is an array buffer object, otherwise false.
|
||||
*/
|
||||
bool isArrayBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief Gets buffer data of an array buffer object.
|
||||
* @param[out] ptr A pointer to the data buffer that serves as the backing store for a JavaScript Typed Array object.
|
||||
* @param[out] length The number of bytes in a JavaScript data object.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayBufferData(uint8_t** ptr, size_t* length) const;
|
||||
|
||||
/**
|
||||
* @brief Gets all property names of an object.
|
||||
* @param[out] allKeys A string vector to store all property names.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getAllKeys(std::vector<std::string>* allKeys) const;
|
||||
|
||||
/**
|
||||
* @brief Sets a pointer to private data on an object.
|
||||
* @param[in] data A void* to set as the object's private data.
|
||||
* @note This method will associate private data with se::Object by std::unordered_map::emplace.
|
||||
* It's used for search a se::Object via a void* private data.
|
||||
*/
|
||||
void setPrivateData(void* data);
|
||||
|
||||
/**
|
||||
* @brief Gets an object's private data.
|
||||
* @return A void* that is the object's private data, if the object has private data, otherwise nullptr.
|
||||
*/
|
||||
void* getPrivateData() const;
|
||||
|
||||
/**
|
||||
* @brief Clears private data of an object.
|
||||
* @param clearMapping Whether to clear the mapping of native object & se::Object.
|
||||
*/
|
||||
void clearPrivateData(bool clearMapping = true);
|
||||
|
||||
/**
|
||||
* @brief Roots an object from garbage collection.
|
||||
* @note Use this method when you want to store an object in a global or on the heap, where the garbage collector will not be able to discover your reference to it.
|
||||
* An object may be rooted multiple times and must be unrooted an equal number of times before becoming eligible for garbage collection.
|
||||
*/
|
||||
void root();
|
||||
|
||||
/**
|
||||
* @brief Unroots an object from garbage collection.
|
||||
* @note An object may be rooted multiple times and must be unrooted an equal number of times before becoming eligible for garbage collection.
|
||||
*/
|
||||
void unroot();
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is rooted.
|
||||
* @return true if it has been already rooted, otherwise false.
|
||||
*/
|
||||
bool isRooted() const;
|
||||
|
||||
/**
|
||||
* @brief Tests whether two objects are strict equal, as compared by the JS === operator.
|
||||
* @param[in] o The object to be tested with this object.
|
||||
* @return true if the two values are strict equal, otherwise false.
|
||||
*/
|
||||
bool strictEquals(Object* o) const;
|
||||
|
||||
/**
|
||||
* @brief Attaches an object to current object.
|
||||
* @param[in] obj The object to be attached.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will set `obj` as a property of current object, therefore the lifecycle of child object will depend on current object,
|
||||
* which means `obj` may be garbage collected only after current object is garbage collected.
|
||||
* It's normally used in binding a native callback method. For example:
|
||||
|
||||
```javascript
|
||||
var self = this;
|
||||
someObject.setCallback(function(){}, self);
|
||||
```
|
||||
|
||||
```c++
|
||||
static bool SomeObject_setCallback(se::State& s)
|
||||
{
|
||||
SomeObject* cobj = (SomeObject*)s.nativeThisObject();
|
||||
const auto& args = s.args();
|
||||
size_t argc = args.size();
|
||||
if (argc == 2) {
|
||||
std::function<void()> arg0;
|
||||
do {
|
||||
if (args[0].isObject() && args[0].toObject()->isFunction())
|
||||
{
|
||||
se::Value jsThis(args[1]);
|
||||
se::Value jsFunc(args[0]);
|
||||
|
||||
jsThis.toObject()->attachObject(jsFunc.toObject());
|
||||
|
||||
auto lambda = [=]() -> void {
|
||||
...
|
||||
// Call jsFunc stuff...
|
||||
...
|
||||
};
|
||||
arg0 = lambda;
|
||||
}
|
||||
else
|
||||
{
|
||||
arg0 = nullptr;
|
||||
}
|
||||
} while(false);
|
||||
SE_PRECONDITION2(ok, false, "Error processing arguments");
|
||||
cobj->setCallback(arg0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
||||
*/
|
||||
bool attachObject(Object* obj);
|
||||
|
||||
/**
|
||||
* @brief Detaches an object from current object.
|
||||
* @param[in] obj The object to be detached.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note The attached object will not be released if current object is not garbage collected.
|
||||
*/
|
||||
bool detachObject(Object* obj);
|
||||
|
||||
/**
|
||||
* @brief Returns the string for describing current object.
|
||||
* @return The string for describing current object.
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
// Private API used in wrapper
|
||||
static Object* _createJSObject(Class* cls, JSObject* obj);
|
||||
void _setFinalizeCallback(JSFinalizeOp finalizeCb);
|
||||
bool _isNativeFunction(JSNative func) const;
|
||||
JSObject* _getJSObject() const;
|
||||
Class* _getClass() const { return _cls; }
|
||||
//
|
||||
|
||||
private:
|
||||
Object();
|
||||
bool init(Class* cls, JSObject* obj);
|
||||
virtual ~Object();
|
||||
|
||||
static void setContext(JSContext* cx);
|
||||
static void cleanup();
|
||||
|
||||
void trace(JSTracer* tracer, void* data);
|
||||
bool updateAfterGC(void* data);
|
||||
|
||||
void protect();
|
||||
void unprotect();
|
||||
void reset();
|
||||
|
||||
JS::Heap<JSObject*> _heap; /* should be untouched if in rooted mode */
|
||||
JS::PersistentRootedObject* _root; /* should be null if not in rooted mode */
|
||||
|
||||
void* _privateData;
|
||||
|
||||
Class* _cls;
|
||||
JSFinalizeOp _finalizeCb;
|
||||
|
||||
uint32_t _rootCount;
|
||||
uint32_t _currentVMId;
|
||||
|
||||
friend class ScriptEngine;
|
||||
};
|
||||
|
||||
extern std::unordered_map<Object*, void*> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
1261
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/ScriptEngine.cpp
Normal file
1261
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/ScriptEngine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,310 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
class Class;
|
||||
class Value;
|
||||
|
||||
extern Class* __jsb_CCPrivateData_class;
|
||||
|
||||
/**
|
||||
* A stack-allocated class that governs a number of local handles.
|
||||
* It's only implemented for v8 wrapper now.
|
||||
* Other script engine wrappers have empty implementation for this class.
|
||||
* It's used at the beginning of executing any wrapper API.
|
||||
*/
|
||||
class AutoHandleScope
|
||||
{
|
||||
public:
|
||||
AutoHandleScope();
|
||||
~AutoHandleScope();
|
||||
};
|
||||
|
||||
/**
|
||||
* ScriptEngine is a sington which represents a context of JavaScript VM.
|
||||
*/
|
||||
class ScriptEngine final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Gets or creates the instance of script engine.
|
||||
* @return The script engine instance.
|
||||
*/
|
||||
static ScriptEngine* getInstance();
|
||||
|
||||
/**
|
||||
* @brief Destroys the instance of script engine.
|
||||
*/
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* @brief Gets the global object of JavaScript VM.
|
||||
* @return The se::Object stores the global JavaScript object.
|
||||
*/
|
||||
Object* getGlobalObject();
|
||||
|
||||
typedef bool (*RegisterCallback)(Object*);
|
||||
|
||||
/**
|
||||
* @brief Adds a callback for registering a native binding module.
|
||||
* @param[in] cb A callback for registering a native binding module.
|
||||
* @note This method just add a callback to a vector, callbacks is invoked in `start` method.
|
||||
*/
|
||||
void addRegisterCallback(RegisterCallback cb);
|
||||
|
||||
/**
|
||||
* @brief Starts the script engine.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will invoke all callbacks of native binding modules by the order of registration.
|
||||
*/
|
||||
bool start();
|
||||
|
||||
/**
|
||||
* @brief Initializes script engine.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will create JavaScript context and global object.
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeInitHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterInitHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Cleanups script engine.
|
||||
* @note This method will removes all objects in JavaScript VM even whose are rooted, then shutdown JavaScript VMf.
|
||||
*/
|
||||
void cleanup();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked before cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeCleanupHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked after cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterCleanupHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Executes a utf-8 string buffer which contains JavaScript code.
|
||||
* @param[in] scriptStr A utf-8 string buffer, if it isn't null-terminated, parameter `length` should be assigned and > 0.
|
||||
* @param[in] length The length of parameter `scriptStr`, it will be set to string length internally if passing < 0 and parameter `scriptStr` is null-terminated.
|
||||
* @param[in] rval The se::Value that results from evaluating script. Passing nullptr if you don't care about the result.
|
||||
* @param[in] fileName A string containing a URL for the script's source file. This is used by debuggers and when reporting exceptions. Pass NULL if you do not care to include source file information.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool evalString(const char* scriptStr, ssize_t length = -1, Value* rval = nullptr, const char* fileName = nullptr);
|
||||
|
||||
/**
|
||||
* Delegate class for file operation
|
||||
*/
|
||||
class FileOperationDelegate
|
||||
{
|
||||
public:
|
||||
FileOperationDelegate()
|
||||
: onGetDataFromFile(nullptr)
|
||||
, onGetStringFromFile(nullptr)
|
||||
, onCheckFileExist(nullptr)
|
||||
, onGetFullPath(nullptr)
|
||||
{}
|
||||
|
||||
bool isValid() const {
|
||||
return onGetDataFromFile != nullptr
|
||||
&& onGetStringFromFile != nullptr
|
||||
&& onCheckFileExist != nullptr
|
||||
&& onGetFullPath != nullptr; }
|
||||
|
||||
// path, buffer, buffer size
|
||||
std::function<void(const std::string&, const std::function<void(const uint8_t*, size_t)>& )> onGetDataFromFile;
|
||||
// path, return file string content.
|
||||
std::function<std::string(const std::string&)> onGetStringFromFile;
|
||||
// path
|
||||
std::function<bool(const std::string&)> onCheckFileExist;
|
||||
// path, return full path
|
||||
std::function<std::string(const std::string&)> onGetFullPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sets the delegate for file operation.
|
||||
* @param delegate[in] The delegate instance for file operation.
|
||||
*/
|
||||
void setFileOperationDelegate(const FileOperationDelegate& delegate);
|
||||
|
||||
/**
|
||||
* @brief Gets the delegate for file operation.
|
||||
* @return The delegate for file operation
|
||||
*/
|
||||
const FileOperationDelegate& getFileOperationDelegate() const;
|
||||
|
||||
/**
|
||||
* @brief Executes a file which contains JavaScript code.
|
||||
* @param[in] path Script file path.
|
||||
* @param[in] rval The se::Value that results from evaluating script. Passing nullptr if you don't care about the result.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool runScript(const std::string& path, Value* rval = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is doing garbage collection.
|
||||
* @return true if it's in garbage collection, otherwise false.
|
||||
*/
|
||||
bool isGarbageCollecting();
|
||||
|
||||
/**
|
||||
* @brief Performs a JavaScript garbage collection.
|
||||
*/
|
||||
void garbageCollect() { JS_GC( _cx ); }
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is being cleaned up.
|
||||
* @return true if it's in cleaning up, otherwise false.
|
||||
*/
|
||||
bool isInCleanup() { return _isInCleanup; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is valid.
|
||||
* @return true if it's valid, otherwise false.
|
||||
*/
|
||||
bool isValid() { return _isValid; }
|
||||
|
||||
/**
|
||||
* @brief Clears all exceptions.
|
||||
*/
|
||||
void clearException();
|
||||
|
||||
using ExceptionCallback = std::function<void(const char*, const char*, const char*)>; // location, message, stack
|
||||
|
||||
/**
|
||||
* @brief Sets the callback function while an exception is fired.
|
||||
* @param[in] cb The callback function to notify that an exception is fired.
|
||||
*/
|
||||
void setExceptionCallback(const ExceptionCallback& cb);
|
||||
|
||||
/**
|
||||
* @brief Gets the start time of script engine.
|
||||
* @return The start time of script engine.
|
||||
*/
|
||||
const std::chrono::steady_clock::time_point& getStartTime() const { return _startTime; }
|
||||
|
||||
/**
|
||||
* @brief Enables JavaScript debugger
|
||||
* @param[in] serverAddr The address of debugger server.
|
||||
* @param[in] port The port of debugger server will use.
|
||||
* @param[in] isWait Whether wait debugger attach when loading.
|
||||
*/
|
||||
void enableDebugger(const std::string& serverAddr, uint32_t port, bool isWait = false);
|
||||
|
||||
/**
|
||||
* @brief Tests whether JavaScript debugger is enabled
|
||||
* @return true if JavaScript debugger is enabled, otherwise false.
|
||||
*/
|
||||
bool isDebuggerEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief Main loop update trigger, it's need to invoked in main thread every frame.
|
||||
*/
|
||||
void mainLoopUpdate();
|
||||
|
||||
/**
|
||||
* @brief Gets script virtual machine instance ID. Default value is 1, increase by 1 if `init` is invoked.
|
||||
*/
|
||||
uint32_t getVMId() const { return _vmId; }
|
||||
|
||||
// Private API used in wrapper
|
||||
JSContext* _getContext() { return _cx; }
|
||||
void _setGarbageCollecting(bool isGarbageCollecting);
|
||||
void _debugProcessInput(const std::string& str);
|
||||
//
|
||||
private:
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
|
||||
static void onWeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data);
|
||||
static void onWeakPointerZoneGroupCallback(JSContext* cx, void* data);
|
||||
|
||||
bool getScript(const std::string& path, JS::MutableHandleScript script);
|
||||
bool compileScript(const std::string& path, JS::MutableHandleScript script);
|
||||
|
||||
JSContext* _cx;
|
||||
JSCompartment* _oldCompartment;
|
||||
|
||||
Object* _globalObj;
|
||||
Object* _debugGlobalObj;
|
||||
|
||||
FileOperationDelegate _fileOperationDelegate;
|
||||
|
||||
std::vector<RegisterCallback> _registerCallbackArray;
|
||||
std::chrono::steady_clock::time_point _startTime;
|
||||
|
||||
std::vector<std::function<void()>> _beforeInitHookArray;
|
||||
std::vector<std::function<void()>> _afterInitHookArray;
|
||||
|
||||
std::vector<std::function<void()>> _beforeCleanupHookArray;
|
||||
std::vector<std::function<void()>> _afterCleanupHookArray;
|
||||
|
||||
ExceptionCallback _exceptionCallback;
|
||||
// name ~> JSScript map
|
||||
std::unordered_map<std::string, JS::PersistentRootedScript*> _filenameScriptMap;
|
||||
|
||||
std::string _debuggerServerAddr;
|
||||
uint32_t _debuggerServerPort;
|
||||
|
||||
uint32_t _vmId;
|
||||
|
||||
bool _isGarbageCollecting;
|
||||
bool _isValid;
|
||||
bool _isInCleanup;
|
||||
bool _isErrorHandleWorking;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
|
33
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/SeApi.h
Normal file
33
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/SeApi.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "../Value.hpp"
|
||||
#include "../State.hpp"
|
||||
#include "HelperMacros.h"
|
||||
#include "Utils.hpp"
|
280
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.cpp
Normal file
280
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "Utils.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "Class.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
void forceConvertJsValueToStdString(JSContext* cx, JS::HandleValue jsval, std::string* ret)
|
||||
{
|
||||
assert(ret != nullptr);
|
||||
JS::RootedString jsStr(cx, JS::ToString(cx, jsval));
|
||||
*ret = jsToStdString(cx, jsStr);
|
||||
}
|
||||
|
||||
std::string jsToStdString(JSContext* cx, JS::HandleString jsStr)
|
||||
{
|
||||
char* str = JS_EncodeStringToUTF8(cx, jsStr);
|
||||
std::string ret(str);
|
||||
JS_free(cx, str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jsToSeArgs(JSContext* cx, int argc, const JS::CallArgs& argv, ValueArray* outArr)
|
||||
{
|
||||
outArr->reserve(argc);
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
Value v;
|
||||
jsToSeValue(cx, argv[i], &v);
|
||||
outArr->push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsArgs(JSContext* cx, const ValueArray& args, JS::AutoValueVector* outArr)
|
||||
{
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
JS::RootedValue v(cx);
|
||||
seToJsValue(cx, arg, &v);
|
||||
outArr->append(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsValue(JSContext* cx, const Value& arg, JS::MutableHandleValue outVal)
|
||||
{
|
||||
switch( arg.getType())
|
||||
{
|
||||
case Value::Type::Number:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setDouble(arg.toNumber());
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::String:
|
||||
{
|
||||
JS::UTF8Chars utf8Str(arg.toString().c_str(), arg.toString().length());
|
||||
JSString* string = JS_NewStringCopyUTF8N(cx, utf8Str);
|
||||
JS::RootedValue value(cx);
|
||||
value.setString(string);
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Boolean:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setBoolean(arg.toBoolean());
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Object:
|
||||
{
|
||||
JS::RootedValue value(cx, JS::ObjectValue(*arg.toObject()->_getJSObject()));
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Null:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setNull();
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Undefined:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setUndefined();
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void jsToSeValue(JSContext* cx, JS::HandleValue jsval, Value* v)
|
||||
{
|
||||
if (jsval.isNumber())
|
||||
{
|
||||
v->setNumber(jsval.toNumber());
|
||||
}
|
||||
else if (jsval.isString())
|
||||
{
|
||||
JS::RootedString jsstr(cx, jsval.toString());
|
||||
v->setString(jsToStdString(cx, jsstr));
|
||||
}
|
||||
else if (jsval.isBoolean())
|
||||
{
|
||||
v->setBoolean(jsval.toBoolean());
|
||||
}
|
||||
else if (jsval.isObject())
|
||||
{
|
||||
Object* object = nullptr;
|
||||
|
||||
JS::RootedObject jsobj(cx, jsval.toObjectOrNull());
|
||||
void* nativeObj = getPrivate(cx, jsobj);
|
||||
|
||||
if (nativeObj != nullptr)
|
||||
{
|
||||
object = Object::getObjectWithPtr(nativeObj);
|
||||
}
|
||||
|
||||
if (object == nullptr)
|
||||
{
|
||||
object = Object::_createJSObject(nullptr, jsval.toObjectOrNull());
|
||||
}
|
||||
v->setObject(object, true);
|
||||
object->decRef();
|
||||
}
|
||||
else if (jsval.isNull())
|
||||
{
|
||||
v->setNull();
|
||||
}
|
||||
else if (jsval.isUndefined())
|
||||
{
|
||||
v->setUndefined();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void setReturnValue(JSContext* cx, const Value& data, const JS::CallArgs& argv)
|
||||
{
|
||||
JS::RootedValue rval(cx);
|
||||
seToJsValue(cx, data, &rval);
|
||||
argv.rval().set(rval);
|
||||
}
|
||||
|
||||
const char* KEY_PRIVATE_DATA = "__cc_private_data";
|
||||
|
||||
bool hasPrivate(JSContext* cx, JS::HandleObject obj)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* cls = JS_GetClass(obj);
|
||||
found = !!(cls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
JS::RootedObject jsobj(cx, obj);
|
||||
if (JS_HasProperty(cx, jsobj, KEY_PRIVATE_DATA, &found) && found)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void* getPrivate(JSContext* cx, JS::HandleObject obj)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* cls = JS_GetClass(obj);
|
||||
found = !!(cls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (found)
|
||||
{
|
||||
return JS_GetPrivate(obj);
|
||||
}
|
||||
|
||||
if (JS_HasProperty(cx, obj, KEY_PRIVATE_DATA, &found) && found)
|
||||
{
|
||||
JS::RootedValue jsData(cx);
|
||||
if (JS_GetProperty(cx, obj, KEY_PRIVATE_DATA, &jsData))
|
||||
{
|
||||
PrivateData* privateData = (PrivateData*)JS_GetPrivate(jsData.toObjectOrNull());
|
||||
return privateData->data;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setPrivate(JSContext* cx, JS::HandleObject obj, void* data, JSFinalizeOp finalizeCb)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* jsCls = JS_GetClass(obj);
|
||||
found = !!(jsCls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (found)
|
||||
{
|
||||
JS_SetPrivate(obj, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(finalizeCb);
|
||||
Object* privateObj = Object::createObjectWithClass(__jsb_CCPrivateData_class);
|
||||
PrivateData* privateData = (PrivateData*)malloc(sizeof(PrivateData));
|
||||
privateData->data = data;
|
||||
privateData->finalizeCb = finalizeCb;
|
||||
JS_SetPrivate(privateObj->_getJSObject(), privateData);
|
||||
|
||||
JS::RootedValue privateVal(cx, JS::ObjectValue(*privateObj->_getJSObject()));
|
||||
JS_SetProperty(cx, obj, KEY_PRIVATE_DATA, privateVal);
|
||||
privateObj->decRef();
|
||||
}
|
||||
}
|
||||
|
||||
void clearPrivate(JSContext* cx, JS::HandleObject obj)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* cls = JS_GetClass(obj);
|
||||
found = !!(cls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (found)
|
||||
{
|
||||
JS_SetPrivate(obj, nullptr);
|
||||
}
|
||||
else if (JS_HasProperty(cx, obj, KEY_PRIVATE_DATA, &found) && found)
|
||||
{
|
||||
JS::RootedValue jsData(cx);
|
||||
assert(JS_GetProperty(cx, obj, KEY_PRIVATE_DATA, &jsData));
|
||||
|
||||
PrivateData* privateData = (PrivateData*)JS_GetPrivate(jsData.toObjectOrNull());
|
||||
free(privateData);
|
||||
JS_SetPrivate(jsData.toObjectOrNull(), nullptr);
|
||||
bool ok = JS_DeleteProperty(cx, obj, KEY_PRIVATE_DATA);
|
||||
assert(ok);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace se { namespace internal {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
66
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.hpp
Normal file
66
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
#include "../Value.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Class;
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct PrivateData
|
||||
{
|
||||
void* data;
|
||||
JSFinalizeOp finalizeCb;
|
||||
};
|
||||
|
||||
void forceConvertJsValueToStdString(JSContext* cx, JS::HandleValue jsval, std::string* ret);
|
||||
std::string jsToStdString(JSContext* cx, JS::HandleString jsStr);
|
||||
|
||||
void jsToSeArgs(JSContext* cx, int argc, const JS::CallArgs& argv, ValueArray* outArr);
|
||||
void jsToSeValue(JSContext *cx, JS::HandleValue jsval, Value* v);
|
||||
void seToJsArgs(JSContext* cx, const ValueArray& args, JS::AutoValueVector* outArr);
|
||||
void seToJsValue(JSContext* cx, const Value& v, JS::MutableHandleValue outVal);
|
||||
|
||||
void setReturnValue(JSContext* cx, const Value& data, const JS::CallArgs& argv);
|
||||
|
||||
bool hasPrivate(JSContext* cx, JS::HandleObject obj);
|
||||
void* getPrivate(JSContext* cx, JS::HandleObject obj);
|
||||
void setPrivate(JSContext* cx, JS::HandleObject obj, void* data, JSFinalizeOp finalizeCb);
|
||||
void clearPrivate(JSContext* cx, JS::HandleObject obj);
|
||||
|
||||
} // namespace internal {
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
Reference in New Issue
Block a user