no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/Array.h"
#include "js/ArrayBuffer.h"
#include "js/BigInt.h"
#include "js/BuildId.h"
#include "js/CompilationAndEvaluation.h"
#include "js/ContextOptions.h"
#include "js/Conversions.h"
#include "js/Equality.h"
#include "js/GCAPI.h"
#include "js/Initialization.h"
#include "js/JSON.h"
#include "js/LocaleSensitive.h"
#include "js/MemoryMetrics.h"
#include "js/SourceText.h"
#include "js/Warnings.h"
#include "js/experimental/TypedData.h"
#include "mozilla/Unused.h"
#include "../PrivateObject.h"
#include "HelperMacros.h"
#include <assert.h>
#include <chrono>
#include <functional>
#include <initializer_list>
#include <unordered_map>
#include <vector>
#include "base/std/container/string.h"

View File

@@ -0,0 +1,242 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.h"
#include "Object.h"
#include "ScriptEngine.h"
#include "Utils.h"
#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;
}
ccstd::vector<Class *> __allClasses;
} // namespace
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 = ccnew Class();
if (cls != nullptr && !cls->init(className, obj, parentProto, ctor)) {
delete cls;
cls = nullptr;
}
return cls;
}
Class *Class::create(const std::initializer_list<const char *> &classPath, se::Object *parent, Object *parentProto, JSNative ctor) {
se::AutoHandleScope scope;
se::Object *currentParent = parent;
se::Value tmp;
for (auto i = 0; i < classPath.size() - 1; i++) {
bool ok = currentParent->getProperty(*(classPath.begin() + i), &tmp);
CC_ASSERT(ok); // class or namespace in path is not defined
currentParent = tmp.toObject();
}
return create(*(classPath.end() - 1), currentParent, parentProto, ctor);
}
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);
}
/* static */
void Class::onTraceCallback(JSTracer *trc, JSObject *obj) {
auto *seObj = reinterpret_cast<Object *>(internal::SE_JS_GetPrivate(obj, 1));
if (seObj != nullptr) {
JS::TraceEdge(trc, &seObj->_heap, "seObj");
}
}
bool Class::install() {
// assert(__clsMap.find(_name) == __clsMap.end());
//
// __clsMap.emplace(_name, this);
_jsCls.name = _name;
_jsCls.flags = JSCLASS_USERBIT1 | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_FOREGROUND_FINALIZE; //IDEA: Use JSCLASS_BACKGROUND_FINALIZE to improve GC performance
if (_finalizeOp != nullptr) {
_classOps.finalize = _finalizeOp;
} else {
_classOps.finalize = [](JSFreeOp *fop, JSObject *obj) {};
}
_classOps.trace = Class::onTraceCallback;
_jsCls.cOps = &_classOps;
JSObject *parentObj = _parentProto != nullptr ? _parentProto->_getJSObject() : nullptr;
JS::RootedObject parentProto(__cx, parentObj);
JS::RootedObject parent(__cx, _parent->_getJSObject());
_funcs.push_back(JS_FS_END);
_properties.push_back(JS_PS_END);
_staticFuncs.push_back(JS_FS_END);
_staticProperties.push_back(JS_PS_END);
JS::RootedObject jsobj(__cx, 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_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);
_properties.push_back(property);
return true;
}
bool Class::defineProperty(const std::initializer_list<const char *> &names, JSNative getter, JSNative setter) {
bool ret = true;
for (const auto *name : names) {
ret &= defineProperty(name, getter, setter);
}
return ret;
}
bool Class::defineStaticFunction(const char *name, JSNative func) {
JSFunctionSpec cb = JS_FN(name, func, 0, 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);
_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;
// }
void Class::_createJSObjectWithClass(Class *cls, JS::MutableHandleObject outObj) {
JSObject *proto = cls->_proto != nullptr ? cls->_proto->_getJSObject() : nullptr;
JS::RootedObject jsProto(__cx, proto);
outObj.set(JS_NewObjectWithGivenProto(__cx, &cls->_jsCls, jsProto));
}
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

View File

@@ -0,0 +1,157 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.h"
#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);
static Class *create(const std::initializer_list<const char *> &classPath, se::Object *parent, 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);
bool defineProperty(const std::initializer_list<const char *> &names, 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 void _createJSObjectWithClass(Class *cls, JS::MutableHandleObject outObj);
static void setContext(JSContext *cx);
static void cleanup();
static void onTraceCallback(JSTracer *trc, JSObject *obj);
const char *_name;
Object *_parent;
Object *_proto;
Object *_parentProto;
JSNative _ctor;
JSClass _jsCls;
JSClassOps _classOps;
ccstd::vector<JSFunctionSpec> _funcs;
ccstd::vector<JSFunctionSpec> _staticFuncs;
ccstd::vector<JSPropertySpec> _properties;
ccstd::vector<JSPropertySpec> _staticProperties;
JSFinalizeOp _finalizeOp;
friend class ScriptEngine;
friend class Object;
};
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM

View File

@@ -0,0 +1,81 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "HelperMacros.h"
#if defined(RECORD_JSB_INVOKING)
namespace {
bool cmp(const std::pair<const char *, std::tuple<int, uint64_t>> &a, const std::pair<const char *, std::tuple<int, uint64_t>> &b) {
return std::get<1>(a.second) > std::get<1>(b.second);
}
unsigned int __jsbInvocationCount; // NOLINT(readability-identifier-naming)
ccstd::unordered_map<char const *, std::tuple<int, uint64_t>> __jsbFunctionInvokedRecords; // NOLINT(readability-identifier-naming)
} // namespace
JsbInvokeScopeT::JsbInvokeScopeT(const char *functionName) : _functionName(functionName) {
_start = std::chrono::high_resolution_clock::now();
__jsbInvocationCount++;
}
JsbInvokeScopeT::~JsbInvokeScopeT() {
auto &ref = __jsbFunctionInvokedRecords[_functionName];
std::get<0>(ref) += 1;
std::get<1>(ref) += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - _start).count();
}
#endif
void printJSBInvokeAtFrame(int n) {
#if defined(RECORD_JSB_INVOKING)
static int cnt = 0;
cnt += 1;
if (cnt % n == 0) {
printJSBInvoke();
}
#endif
}
void clearRecordJSBInvoke() {
#if defined(RECORD_JSB_INVOKING)
__jsbInvocationCount = 0;
__jsbFunctionInvokedRecords.clear();
#endif
}
void printJSBInvoke() {
#if defined(RECORD_JSB_INVOKING)
static ccstd::vector<std::pair<const char *, std::tuple<int, uint64_t>>> pairs;
for (const auto &it : __jsbFunctionInvokedRecords) {
pairs.emplace_back(it); //NOLINT
}
std::sort(pairs.begin(), pairs.end(), cmp);
cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::LEVEL_DEBUG, "Start print JSB function record info....... %d times", __jsbInvocationCount);
for (const auto &pair : pairs) {
cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::LEVEL_DEBUG, "\t%s takes %.3lf ms, invoked %u times,", pair.first, std::get<1>(pair.second) / 1000000.0, std::get<0>(pair.second));
}
pairs.clear();
cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::LEVEL_DEBUG, "End print JSB function record info.......\n");
#endif
}

View File

@@ -0,0 +1,249 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "../ValueArrayPool.h"
#include "../config.h"
//#define RECORD_JSB_INVOKING
#ifndef CC_DEBUG
#undef RECORD_JSB_INVOKING
#endif
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
#if defined(RECORD_JSB_INVOKING)
class JsbInvokeScopeT {
public:
JsbInvokeScopeT(const char *functionName);
~JsbInvokeScopeT();
private:
const char *_functionName;
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
};
#define JsbInvokeScope(arg) JsbInvokeScopeT invokeScope(arg); // NOLINT(readability-identifier-naming)
#else
// NOLINTNEXTLINE(readability-identifier-naming)
#define JsbInvokeScope(arg) \
do { \
} while (0)
#endif
template <typename T, typename STATE>
constexpr inline T *SE_THIS_OBJECT(STATE &s) { // NOLINT(readability-identifier-naming)
return reinterpret_cast<T *>(s.nativeThisObject());
}
template <typename T>
constexpr typename std::enable_if<std::is_enum<T>::value, char *>::type SE_UNDERLYING_TYPE_NAME() { // NOLINT(readability-identifier-naming)
return typeid(std::underlying_type_t<T>).name();
}
template <typename T>
constexpr typename std::enable_if<!std::is_enum<T>::value, char *>::type SE_UNDERLYING_TYPE_NAME() { // NOLINT(readability-identifier-naming)
return typeid(T).name();
}
void clearRecordJSBInvoke();
void printJSBInvoke();
void printJSBInvokeAtFrame(int n);
#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) { \
JsbInvokeScope(#funcName); \
bool ret = false; \
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
JS::RootedObject _thizObj(_cx); \
_argv.computeThis(_cx, &_thizObj); \
bool needDeleteValueArray{false}; \
se::ValueArray &args = se::gValueArrayPool.get(argc, needDeleteValueArray); \
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray}; \
se::internal::jsToSeArgs(_cx, argc, _argv, args); \
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::getPrivate(_cx, _thizObj, 0)); \
se::Object *thisObject = reinterpret_cast<se::Object *>(se::internal::getPrivate(_cx, _thizObj, 1)); \
se::State state(thisObject, privateObject, 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_BIND_FUNC_FAST(funcName) \
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp) { \
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
JS::RootedObject _thizObj(_cx); \
_argv.computeThis(_cx, &_thizObj); \
auto *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::SE_JS_GetPrivate(_thizObj, 0)); \
funcName(privateObject->getRaw()); \
_argv.rval().setUndefined(); \
return true; \
}
#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) { \
JsbInvokeScope(#funcName); \
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::SE_JS_GetPrivate(_obj, 0)); \
se::Object *seObj = static_cast<se::Object *>(se::internal::SE_JS_GetPrivate(_obj, 1)); \
bool ret = false; \
if (privateObject == nullptr) \
return; \
se::State state(privateObject); \
ret = funcName(state); \
if (!ret) { \
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
} \
if (seObj->isClearMappingInFinalizer() && privateObject != nullptr) { \
void *nativeObj = privateObject->getRaw(); \
auto iter = se::NativePtrToObjectMap::find(nativeObj); \
if (iter != se::NativePtrToObjectMap::end()) { \
se::NativePtrToObjectMap::erase(iter); \
} \
} \
seObj->decRef(); \
}
#define SE_BIND_CTOR(funcName, cls, finalizeCb) \
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp) { \
JsbInvokeScope(#funcName); \
bool ret = false; \
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
bool needDeleteValueArray{false}; \
se::ValueArray &args = se::gValueArrayPool.get(argc, needDeleteValueArray); \
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray}; \
se::internal::jsToSeArgs(_cx, argc, _argv, args); \
se::Object *thisObject = se::Object::_createJSObjectForConstructor(cls, _argv); \
thisObject->_setFinalizeCallback(finalizeCb##Registry); \
_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_PROP_GET_IMPL(funcName, postFix) \
bool funcName##postFix##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp) { \
JsbInvokeScope(#funcName); \
bool ret = false; \
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
JS::RootedObject _thizObj(_cx); \
_argv.computeThis(_cx, &_thizObj); \
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::getPrivate(_cx, _thizObj, 0)); \
se::Object *thisObject = reinterpret_cast<se::Object *>(se::internal::getPrivate(_cx, _thizObj, 1)); \
se::State state(thisObject, privateObject); \
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_GET(funcName) SE_BIND_PROP_GET_IMPL(funcName, )
#define SE_BIND_FUNC_AS_PROP_GET(funcName) SE_BIND_PROP_GET_IMPL(funcName, _asGetter)
#define SE_BIND_PROP_SET_IMPL(funcName, postFix) \
bool funcName##postFix##Registry(JSContext *_cx, unsigned _argc, JS::Value *_vp) { \
JsbInvokeScope(#funcName); \
bool ret = false; \
JS::CallArgs _argv = JS::CallArgsFromVp(_argc, _vp); \
JS::RootedObject _thizObj(_cx); \
_argv.computeThis(_cx, &_thizObj); \
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::getPrivate(_cx, _thizObj, 0)); \
se::Object *thisObject = reinterpret_cast<se::Object *>(se::internal::getPrivate(_cx, _thizObj, 1)); \
bool needDeleteValueArray{false}; \
se::ValueArray &args = se::gValueArrayPool.get(1, needDeleteValueArray); \
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray}; \
se::Value &data{args[0]}; \
se::internal::jsToSeValue(_cx, _argv[0], &data); \
se::State state(thisObject, privateObject, args); \
ret = funcName(state); \
if (!ret) { \
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
} \
return ret; \
}
#define SE_BIND_PROP_SET(funcName) SE_BIND_PROP_SET_IMPL(funcName, )
#define SE_BIND_FUNC_AS_PROP_SET(funcName) SE_BIND_PROP_SET_IMPL(funcName, _asSetter)
#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 CC_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 CC_DEBUG > 0
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM

View File

@@ -0,0 +1,780 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
#include "../MappingUtils.h"
#include "Class.h"
#include "ScriptEngine.h"
#include "Utils.h"
namespace se {
std::unordered_map<Object *, void *> __objectMap; // Currently, the value `void*` is always nullptr
namespace {
JSContext *__cx = nullptr;
} // namespace
Object::Object()
: _privateObject(nullptr),
_cls(nullptr),
_finalizeCb(nullptr),
_rootCount(0) {
_currentVMId = ScriptEngine::getInstance()->getVMId();
}
Object::~Object() {
if (_cls == nullptr) {
unroot();
}
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);
if (_cls == nullptr) {
root();
}
return true;
}
Object *Object::_createJSObject(Class *cls, JSObject *obj) {
Object *ret = ccnew Object();
if (!ret->init(cls, obj)) {
delete ret;
ret = nullptr;
}
return ret;
}
Object *Object::_createJSObjectForConstructor(Class *cls, const JS::CallArgs &args) {
Object *ret = ccnew Object();
JS::RootedObject obj(__cx, JS_NewObjectForConstructor(__cx, &cls->_jsCls, args));
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) {
JS::RootedObject jsobj(__cx);
Class::_createJSObjectWithClass(cls, &jsobj);
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(const void *data, size_t byteLength) {
Object *obj = nullptr;
if (byteLength > 0 && data != nullptr) {
mozilla::UniquePtr<uint8_t[], JS::FreePolicy> jsBuf(
js_pod_arena_malloc<uint8_t>(js::ArrayBufferContentsArena, byteLength));
if (!jsBuf)
return nullptr;
memcpy(jsBuf.get(), data, byteLength);
JS::RootedObject jsobj(__cx, JS::NewArrayBufferWithContents(__cx, byteLength, jsBuf.get()));
if (jsobj) {
// If JS::NewArrayBufferWithContents returns non-null, the ownership of
// the data is transfered to obj, so we release the ownership here.
mozilla::Unused << jsBuf.release();
obj = Object::_createJSObject(nullptr, jsobj);
}
} else {
JS::RootedObject jsobj(__cx, JS::NewArrayBuffer(__cx, byteLength));
if (jsobj) {
obj = Object::_createJSObject(nullptr, jsobj);
}
}
return obj;
}
/* static */
Object *Object::createExternalArrayBufferObject(void *contents, size_t byteLength, BufferContentsFreeFunc freeFunc, void *freeUserData /* = nullptr*/) {
struct BackingStoreUserData {
BufferContentsFreeFunc freeFunc;
void *freeUserData;
size_t byteLength;
};
auto *userData = ccnew BackingStoreUserData();
userData->freeFunc = freeFunc;
userData->freeUserData = freeUserData;
userData->byteLength = byteLength;
Object *obj = nullptr;
JS::RootedObject jsobj(__cx, JS::NewExternalArrayBuffer(
__cx, byteLength, contents,
[](void *data, void *deleterData) {
auto *userData = reinterpret_cast<BackingStoreUserData *>(deleterData);
userData->freeFunc(data, userData->byteLength, userData->freeUserData);
delete userData;
},
userData));
if (jsobj) {
obj = Object::_createJSObject(nullptr, jsobj);
}
return obj;
}
Object *Object::createTypedArray(TypedArrayType type, const 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;
}
#define CREATE_TYPEDARRAY(_type_, _data, _byteLength, count) \
{ \
void *tmpData = nullptr; \
JS::RootedObject arr(__cx, JS_New##_type_##Array(__cx, (uint32_t)(count))); \
bool isShared = false; \
if (_data != nullptr) { \
JS::AutoCheckCannotGC nogc; \
tmpData = JS_Get##_type_##ArrayData(arr, &isShared, nogc); \
memcpy(tmpData, (const void *)_data, (_byteLength)); \
} \
Object *obj = Object::_createJSObject(nullptr, arr); \
return obj; \
}
switch (type) {
case TypedArrayType::INT8:
CREATE_TYPEDARRAY(Int8, data, byteLength, byteLength);
case TypedArrayType::INT16:
CREATE_TYPEDARRAY(Int16, data, byteLength, byteLength / 2);
case TypedArrayType::INT32:
CREATE_TYPEDARRAY(Int32, data, byteLength, byteLength / 4);
case TypedArrayType::UINT8:
CREATE_TYPEDARRAY(Uint8, data, byteLength, byteLength);
case TypedArrayType::UINT16:
CREATE_TYPEDARRAY(Uint16, data, byteLength, byteLength / 2);
case TypedArrayType::UINT32:
CREATE_TYPEDARRAY(Uint32, data, byteLength, byteLength / 4);
case TypedArrayType::FLOAT32:
CREATE_TYPEDARRAY(Float32, data, byteLength, byteLength / 4);
case TypedArrayType::FLOAT64:
CREATE_TYPEDARRAY(Float64, data, byteLength, byteLength / 8);
default:
assert(false); // Should never go here.
break;
}
return nullptr;
#undef CREATE_TYPEDARRAY
}
/* static */
Object *Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj) {
return Object::createTypedArrayWithBuffer(type, obj, 0);
}
/* static */
Object *Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset) {
size_t byteLength{0};
uint8_t *skip{nullptr};
obj->getTypedArrayData(&skip, &byteLength);
return Object::createTypedArrayWithBuffer(type, obj, offset, byteLength - offset);
}
/* static */
Object *Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset, 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;
}
assert(obj->isArrayBuffer());
JS::RootedObject jsobj(__cx, obj->_getJSObject());
switch (type) {
case TypedArrayType::INT8: {
JS::RootedObject typeArray(__cx, JS_NewInt8ArrayWithBuffer(__cx, jsobj, offset, byteLength));
return Object::_createJSObject(nullptr, typeArray);
}
case TypedArrayType::INT16: {
JS::RootedObject typeArray(__cx, JS_NewInt16ArrayWithBuffer(__cx, jsobj, offset, byteLength / 2));
return Object::_createJSObject(nullptr, typeArray);
}
case TypedArrayType::INT32: {
JS::RootedObject typeArray(__cx, JS_NewInt32ArrayWithBuffer(__cx, jsobj, offset, byteLength / 4));
return Object::_createJSObject(nullptr, typeArray);
}
case TypedArrayType::UINT8: {
JS::RootedObject typeArray(__cx, JS_NewUint8ArrayWithBuffer(__cx, jsobj, offset, byteLength));
return Object::_createJSObject(nullptr, typeArray);
}
case TypedArrayType::UINT16: {
JS::RootedObject typeArray(__cx, JS_NewUint16ArrayWithBuffer(__cx, jsobj, offset, byteLength / 2));
return Object::_createJSObject(nullptr, typeArray);
}
case TypedArrayType::UINT32: {
JS::RootedObject typeArray(__cx, JS_NewUint32ArrayWithBuffer(__cx, jsobj, offset, byteLength / 4));
return Object::_createJSObject(nullptr, typeArray);
}
case TypedArrayType::FLOAT32: {
JS::RootedObject typeArray(__cx, JS_NewFloat32ArrayWithBuffer(__cx, jsobj, offset, byteLength / 4));
return Object::_createJSObject(nullptr, typeArray);
}
case TypedArrayType::FLOAT64: {
JS::RootedObject typeArray(__cx, JS_NewFloat64ArrayWithBuffer(__cx, jsobj, offset, byteLength / 8));
return Object::_createJSObject(nullptr, typeArray);
}
default:
assert(false); // Should never go here.
break;
}
return nullptr;
}
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, bool cachePropertyName) {
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, getter, setter, JSPROP_ENUMERATE);
}
bool Object::defineOwnProperty(const char *name, const se::Value &value, bool writable, bool enumerable, bool configurable) {
JS::RootedObject jsObj(__cx, _getJSObject());
JS::RootedValue jsVal(__cx);
internal::seToJsValue(__cx, value, &jsVal);
unsigned attrs = 0;
if (!writable) {
attrs |= JSPROP_READONLY;
}
if (enumerable) {
attrs |= JSPROP_ENUMERATE;
}
if (!configurable) {
attrs |= JSPROP_PERMANENT;
}
return JS_DefineProperty(__cx, jsObj, name, jsVal, attrs);
}
bool Object::call(const ValueArray &args, Object *thisObject, Value *rval /* = nullptr*/) {
assert(isFunction());
JS::RootedValueVector 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);
JSAutoRealm autoRealm(__cx, func.toObjectOrNull());
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());
JSFunction *jsFunc = JS_DefineFunction(__cx, object, funcName, func, 0, JSPROP_ENUMERATE);
return jsFunc != nullptr;
}
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(_getJSObject());
}
bool Object::_isNativeFunction(JSNative func) const {
JSObject *obj = _getJSObject();
return JS_ObjectIsFunction(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;
if (ptr != nullptr) {
*ptr = (uint8_t *)JS_GetArrayBufferViewData(_getJSObject(), &isShared, nogc);
}
if (length != nullptr) {
*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;
if (ptr != nullptr) {
*ptr = (uint8_t *)JS::GetArrayBufferData(_getJSObject(), &isShared, nogc);
}
if (length != nullptr) {
*length = JS::GetArrayBufferByteLength(_getJSObject());
}
return (*ptr != nullptr);
}
bool Object::getAllKeys(ccstd::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;
ccstd::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::setPrivateObject(PrivateObjectBase *data) {
assert(_privateObject == nullptr);
#if CC_DEBUG
//assert(NativePtrToObjectMap::find(data->getRaw()) == NativePtrToObjectMap::end());
auto it = NativePtrToObjectMap::find(data->getRaw());
if (it != NativePtrToObjectMap::end()) {
auto *pri = it->second->getPrivateObject();
SE_LOGE("Already exists object %s/[%s], trying to add %s/[%s]\n", pri->getName(), typeid(*pri).name(), data->getName(), typeid(*data).name());
#if JSB_TRACK_OBJECT_CREATION
SE_LOGE(" previous object created at %s\n", it->second->_objectCreationStackFrame.c_str());
#endif
assert(false);
}
#endif
JS::RootedObject obj(__cx, _getJSObject());
internal::setPrivate(__cx, obj, data, this, &_internalData, _finalizeCb); //TODO(cjh): how to use _internalData?
NativePtrToObjectMap::emplace(data->getRaw(), this);
_privateObject = data;
}
PrivateObjectBase *Object::getPrivateObject() const {
if (_privateObject == nullptr) {
JS::RootedObject obj(__cx, _getJSObject());
const_cast<Object *>(this)->_privateObject = static_cast<PrivateObjectBase *>(internal::getPrivate(__cx, obj, 0));
}
return _privateObject;
}
void Object::clearPrivateData(bool clearMapping) {
if (_privateObject != nullptr) {
if (clearMapping) {
NativePtrToObjectMap::erase(_privateObject->getRaw());
}
JS::RootedObject obj(__cx, _getJSObject());
internal::clearPrivate(__cx, obj);
delete _privateObject;
_privateObject = 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();
__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::SafelyInitialized<JSObject *>::create());
_root.init(__cx, _heap);
_heap.set(JS::SafelyInitialized<JSObject *>::create());
}
void Object::unprotect() {
if (!_root.initialized())
return;
assert(_currentVMId == ScriptEngine::getInstance()->getVMId());
assert(_heap == JS::SafelyInitialized<JSObject *>::create());
_heap = _root.get();
_root.reset();
}
void Object::reset() {
_root.reset();
_heap = JS::SafelyInitialized<JSObject *>::create();
}
void Object::onTraceCallback(JSTracer *trc, void *data) {
auto *thiz = reinterpret_cast<Object *>(data);
thiz->trace(trc);
}
/* Tracing makes no sense in the rooted case, because JS::PersistentRooted
* already takes care of that. */
void Object::trace(JSTracer *tracer) {
assert(!isRooted());
JS::TraceEdge(tracer, &_heap, "seobj 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(JSTracer *trc, void *data) {
assert(!isRooted());
bool isGarbageCollected = false;
internal::PrivateData *internalData = nullptr;
JSObject *oldPtr = _heap.unbarrieredGet();
if (_heap.unbarrieredGet() != nullptr)
JS_UpdateWeakPointerAfterGC(trc, &_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

View File

@@ -0,0 +1,458 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
#include "../RefCounter.h"
#include "../Value.h"
#include "Base.h"
namespace se {
class Class;
namespace internal {
struct PrivateData;
}
/**
* 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, const void *data, size_t byteLength);
/**
* @brief Creates a JavaScript Typed Array Object with a se::Object, which is a ArrayBuffer,
if provide a null pointer,then will create a empty JavaScript Typed Array Object.
* @param[in] type The format of typed array.
* @param[in] obj A ArrayBuffer to TypedArray.
* @param[in] offset Offset of ArrayBuffer to create with.
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
* @return A JavaScript Typed Array Object which refers to the ArrayBuffer Object, or nullptr if there is an error.
* @note The return value (non-null) has to be released manually.
*/
static Object *createTypedArrayWithBuffer(TypedArrayType type, const Object *obj);
static Object *createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset);
static Object *createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset, 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(const void *data, size_t byteLength);
using BufferContentsFreeFunc = void (*)(void *contents, size_t byteLength, void *userData);
static Object *createExternalArrayBufferObject(void *contents, size_t nbytes, BufferContentsFreeFunc freeFunc, void *freeUserData = nullptr);
/**
* @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.
*/
inline bool getProperty(const char *name, Value *data) {
return getProperty(name, data, false);
}
bool getProperty(const char *name, Value *data, bool cachePropertyName);
inline bool getProperty(const std::string &name, Value *value) {
return getProperty(name.c_str(), 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);
inline bool setProperty(const std::string &name, const Value &value) {
return setProperty(name.c_str(), 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);
bool defineOwnProperty(const char *name, const se::Value &value, bool writable = true, bool enumerable = true, bool configurable = true);
/**
* @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(ccstd::vector<std::string> *allKeys) const;
void setPrivateObject(PrivateObjectBase *data);
PrivateObjectBase *getPrivateObject() const;
/**
* @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.
*/
inline void *getPrivateData() const {
return _privateObject ? _privateObject->getRaw() : nullptr;
}
/**
* @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.
*/
template <typename T>
inline void setPrivateData(T *data) {
static_assert(!std::is_void<T>::value, "void * is not allowed for private data");
setPrivateObject(se::make_shared_private_object(data));
}
template <typename T>
inline T *getTypedPrivateData() const {
return reinterpret_cast<T *>(getPrivateData());
}
/**
* @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 Sets whether to clear the mapping of native object & se::Object in finalizer
*/
inline void setClearMappingInFinalizer(bool v) { _clearMappingInFinalizer = v; }
inline bool isClearMappingInFinalizer() const { return _clearMappingInFinalizer; }
/**
* @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);
static Object *_createJSObjectForConstructor(Class *cls, const JS::CallArgs &args);
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();
static void onTraceCallback(JSTracer *trc, void *data);
void trace(JSTracer *tracer);
bool updateAfterGC(JSTracer *trc, 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 */
PrivateObjectBase *_privateObject{nullptr};
internal::PrivateData *_internalData{nullptr};
Class *_cls{nullptr};
JSFinalizeOp _finalizeCb{nullptr};
uint32_t _rootCount{0};
uint32_t _currentVMId{0};
bool _clearMappingInFinalizer{true};
friend class ScriptEngine;
friend class Class;
};
extern std::unordered_map<Object *, void *> __objectMap; // Currently, the value `void*` is always nullptr
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,347 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
#include "Base.h"
namespace se {
class Object;
class Class;
class Value;
/**
* 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 Adds a callback for registering a native binding module, which will not be removed by ScriptEngine::cleanup.
* @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 addPermanentRegisterCallback(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);
/**
* @brief Compile script file into v8::ScriptCompiler::CachedData and save to file.
* @param[in] path The path of script file.
* @param[in] pathBc The location where bytecode file should be written to. The path should be ends with ".bc", which indicates a bytecode file.
* @return true if succeed, otherwise false.
*/
bool saveByteCodeToFile(const std::string &path, const std::string &pathBc) {
assert(false);
return false;
} //cjh
/**
* @brief Grab a snapshot of the current JavaScript execution stack.
* @return current stack trace string
*/
std::string getCurrentStackTrace() { return ""; } //cjh
/**
* 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 Throw JS exception
*/
void throwException(const std::string &errorMessage) { assert(false); } //cjh
/**
* @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 Sets the callback function while an exception is fired in JS.
* @param[in] cb The callback function to notify that an exception is fired.
*/
void setJSExceptionCallback(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; }
/**
* @brief Fast version of call script function, faster than Object::call
*/
bool callFunction(Object *targetObj, const char *funcName, uint32_t argc, Value *args, Value *rval = nullptr);
/**
* @brief Handle all exceptions throwed by promise
*/
void handlePromiseExceptions();
// 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(JSTracer *trc, JS::Compartment *comp, void *data);
static void onWeakPointerZoneGroupCallback(JSTracer *trc, void *data);
bool getScript(const std::string &path, JS::MutableHandleScript script);
bool compileScript(const std::string &path, JS::MutableHandleScript script);
JSContext *_cx;
JS::Realm *_oldCompartment;
Object *_globalObj;
Object *_debugGlobalObj;
FileOperationDelegate _fileOperationDelegate;
ccstd::vector<RegisterCallback> _registerCallbackArray;
ccstd::vector<RegisterCallback> _permRegisterCallbackArray;
std::chrono::steady_clock::time_point _startTime;
ccstd::vector<std::function<void()>> _beforeInitHookArray;
ccstd::vector<std::function<void()>> _afterInitHookArray;
ccstd::vector<std::function<void()>> _beforeCleanupHookArray;
ccstd::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

View File

@@ -0,0 +1,34 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "../State.h"
#include "../Value.h"
#include "Class.h"
#include "HelperMacros.h"
#include "Object.h"
#include "ScriptEngine.h"
#include "Utils.h"

View File

@@ -0,0 +1,207 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
#include "Class.h"
#include "Object.h"
#include "ScriptEngine.h"
namespace se {
namespace internal {
void *SE_JS_GetPrivate(JSObject *obj, uint32_t slot) {
assert(slot >= 0 && slot < 2);
const auto &v = JS::GetReservedSlot(obj, slot);
return v.isNullOrUndefined() ? nullptr : v.toPrivate();
}
void SE_JS_SetPrivate(JSObject *obj, uint32_t slot, void *data) {
assert(slot >= 0 && slot < 2);
JS::SetReservedSlot(obj, slot, JS::PrivateValue(data));
}
bool isJSBClass(JSObject *obj) {
const JSClass *cls = JS::GetClass(obj);
return (cls->flags & (JSCLASS_HAS_RESERVED_SLOTS(2)) && (cls->flags & JSCLASS_USERBIT1));
}
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) {
JS::UniqueChars str = JS_EncodeStringToUTF8(cx, jsStr);
std::string ret(str.get());
return ret;
}
void jsToSeArgs(JSContext *cx, int argc, const JS::CallArgs &argv, ValueArray &outArr) {
for (int i = 0; i < argc; ++i) {
jsToSeValue(cx, argv[i], &outArr[i]);
}
}
void seToJsArgs(JSContext *cx, const ValueArray &args, JS::RootedValueVector *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;
case Value::Type::BigInt: {
JS::RootedValue value(cx);
JS::BigInt *bi = JS::NumberToBigInt(cx, arg.toUint64());
outVal.setBigInt(bi);
} 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());
PrivateObjectBase *privateObject = static_cast<PrivateObjectBase *>(internal::getPrivate(cx, jsobj, 0));
void *nativeObj = privateObject ? privateObject->getRaw() : nullptr;
bool needRoot = false;
if (nativeObj != nullptr) {
object = Object::getObjectWithPtr(nativeObj);
}
if (object == nullptr) {
object = Object::_createJSObject(nullptr, jsobj);
needRoot = true;
}
v->setObject(object, needRoot);
object->decRef();
} else if (jsval.isNull()) {
v->setNull();
} else if (jsval.isUndefined()) {
v->setUndefined();
} else if (jsval.isBigInt()) {
v->setUint64(JS::ToBigUint64(jsval.toBigInt()));
} 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);
}
bool hasPrivate(JSContext *cx, JS::HandleObject obj) {
return isJSBClass(obj);
}
void *getPrivate(JSContext *cx, JS::HandleObject obj, uint32_t slot) {
bool found = isJSBClass(obj);
if (found) {
return SE_JS_GetPrivate(obj, slot);
}
return nullptr;
}
void setPrivate(JSContext *cx, JS::HandleObject obj, PrivateObjectBase *data, Object *seObj, PrivateData **outInternalData, JSFinalizeOp finalizeCb) {
bool found = isJSBClass(obj);
assert(found);
if (found) {
SE_JS_SetPrivate(obj, 0, data);
SE_JS_SetPrivate(obj, 1, seObj);
if (outInternalData != nullptr) {
*outInternalData = nullptr;
}
}
}
void clearPrivate(JSContext *cx, JS::HandleObject obj) {
bool found = isJSBClass(obj);
if (found) {
SE_JS_SetPrivate(obj, 0, nullptr);
SE_JS_SetPrivate(obj, 1, nullptr);
}
}
} // namespace internal
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM

View File

@@ -0,0 +1,70 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
#include "Base.h"
#include "../Value.h"
namespace se {
class Class;
namespace internal {
struct PrivateData {
PrivateObjectBase *data{nullptr};
Object *seObj{nullptr};
JSFinalizeOp finalizeCb{nullptr};
};
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::RootedValueVector *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, uint32_t slot);
void setPrivate(JSContext *cx, JS::HandleObject obj, PrivateObjectBase *data, Object *seObj, PrivateData **outInternalData, JSFinalizeOp finalizeCb);
void clearPrivate(JSContext *cx, JS::HandleObject obj);
void *SE_JS_GetPrivate(JSObject *obj, uint32_t slot);
void SE_JS_SetPrivate(JSObject *obj, uint32_t slot, void *data);
} // namespace internal
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM