Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix restart crash on HarmonyOS NEXT platform when using JSVM #18300

Merged
merged 5 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 17 additions & 25 deletions native/cocos/bindings/jswrapper/jsvm/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,15 @@

namespace se {
std::unique_ptr<std::unordered_map<Object*, void*>> __objectMap; // Currently, the value `void*` is always nullptr
std::set<Object*> Object::objBaseSet = {};

bool Object::restarting = false;

Object::Object() {}
Object::~Object() {
if(restarting) {
objBaseSet.insert(this);
}
if (__objectMap) {
__objectMap->erase(this);
}

delete _privateObject;
_privateObject = nullptr;
}

Object* Object::createObjectWithClass(Class* cls) {
Expand Down Expand Up @@ -652,7 +649,6 @@ std::string Object::toString() const {
}

void Object::root() {
JSVM_Status status;
if (_rootCount == 0) {
uint32_t result = 0;
_objRef.incRef(_env);
Expand All @@ -661,7 +657,6 @@ void Object::root() {
}

void Object::unroot() {
JSVM_Status status;
if (_rootCount > 0) {
--_rootCount;
if (_rootCount == 0) {
Expand Down Expand Up @@ -704,37 +699,34 @@ void Object::weakCallback(JSVM_Env env, void* nativeObject, void* finalizeHint /
}
void* rawPtr = reinterpret_cast<Object*>(finalizeHint)->_privateData;
Object* seObj = reinterpret_cast<Object*>(finalizeHint);

auto it = objBaseSet.find(seObj);
if(it != objBaseSet.end()) {
if (seObj->_onCleaingPrivateData) { //called by cleanPrivateData, not release seObj;
return;
}

if (seObj->_onCleaingPrivateData) { //called by cleanPrivateData, not release seObj;
if(!NativePtrToObjectMap::isValid()) {
return;
}
if (seObj->_clearMappingInFinalizer && rawPtr != nullptr) {
auto iter = NativePtrToObjectMap::find(rawPtr);
if (iter != NativePtrToObjectMap::end()) {
if (seObj->_finalizeCb != nullptr) {
seObj->_finalizeCb(env, finalizeHint, finalizeHint);
} else {
assert(seObj->_getClass() != nullptr);
if (seObj->_getClass()->_getFinalizeFunction() != nullptr) {
seObj->_getClass()->_getFinalizeFunction()(env, finalizeHint, finalizeHint);
}
}
seObj->decRef();
NativePtrToObjectMap::erase(iter);
} else {
SE_LOGE("not find ptr in NativePtrToObjectMap");
}
}

if (seObj->_finalizeCb != nullptr) {
seObj->_finalizeCb(env, finalizeHint, finalizeHint);
} else {
assert(seObj->_getClass() != nullptr);
if (seObj->_getClass()->_getFinalizeFunction() != nullptr) {
seObj->_getClass()->_getFinalizeFunction()(env, finalizeHint, finalizeHint);
}
}
seObj->decRef();
}
}

void Object::setup() {
restarting = false;
__objectMap = std::make_unique<std::unordered_map<Object*, void*>>();
}

Expand All @@ -749,11 +741,11 @@ void Object::cleanup() {
obj = e.second;

if (obj->_finalizeCb != nullptr) {
obj->_finalizeCb(ScriptEngine::getEnv(), nativeObj, nullptr);
obj->_finalizeCb(ScriptEngine::getEnv(), obj, nullptr);
} else {
if (obj->_getClass() != nullptr) {
if (obj->_getClass()->_getFinalizeFunction() != nullptr) {
obj->_getClass()->_getFinalizeFunction()(ScriptEngine::getEnv(), nativeObj, nullptr);
obj->_getClass()->_getFinalizeFunction()(ScriptEngine::getEnv(), obj, nullptr);
}
}
}
Expand Down
6 changes: 0 additions & 6 deletions native/cocos/bindings/jswrapper/jsvm/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,6 @@ class Object : public RefCounter {
BIGINT64,
BIGUINT64
};

static std::set<Object*> objBaseSet;
static bool restarting;
static void resetBaseSet() {
objBaseSet.erase(objBaseSet.begin(), objBaseSet.end());
}

using BufferContentsFreeFunc = void (*)(void *contents, size_t byteLength, void *userData);

Expand Down
46 changes: 21 additions & 25 deletions native/cocos/bindings/jswrapper/jsvm/ScriptEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,14 @@ SE_BIND_FUNC(JSB_console_assert)
ScriptEngine *gSriptEngineInstance = nullptr;

ScriptEngine::ScriptEngine() {
static bool initialized = false;
if (initialized) {
return;
}
JSVM_InitOptions initOptions;
memset(&initOptions, 0, sizeof(initOptions));
OH_JSVM_Init(&initOptions);
initialized = true;
OH_JSVM_Init(nullptr);
gSriptEngineInstance = this;
};

ScriptEngine::~ScriptEngine() = default;
ScriptEngine::~ScriptEngine() {
cleanup();
gSriptEngineInstance = nullptr;
};

void ScriptEngine::setFileOperationDelegate(const FileOperationDelegate &delegate) {
_fileOperationDelegate = delegate;
Expand Down Expand Up @@ -303,6 +300,21 @@ bool ScriptEngine::init() {

Object *ScriptEngine::getGlobalObject() const { return _globalObj; }

void ScriptEngine::closeEngine() {
JSVM_Env env = _env;
_env = nullptr;

JSVM_Status status;
NODE_API_CALL(status, env, OH_JSVM_CloseEnvScope(env, _envScope));
NODE_API_CALL(status, env, OH_JSVM_DestroyEnv(env));
NODE_API_CALL(status, env, OH_JSVM_CloseVMScope(_vm, _vmScope));
NODE_API_CALL(status, env, OH_JSVM_DestroyVM(_vm));
_envScope = nullptr;
env = nullptr;
_vmScope = nullptr;
_vm = nullptr;
}

bool ScriptEngine::start() {
bool ok = true;
if (!init()) {
Expand Down Expand Up @@ -337,8 +349,6 @@ void ScriptEngine::cleanup() {
if (!_isValid) {
return;
}
Object::restarting = true;
Object::resetBaseSet();
SE_LOGD("ScriptEngine::cleanup begin ...\n");
_isInCleanup = true;
se::AutoHandleScope hs;
Expand All @@ -361,20 +371,6 @@ void ScriptEngine::cleanup() {
__oldConsoleError.setUndefined();
__oldConsoleAssert.setUndefined();

JSVM_Env env = _env;
_env = nullptr;

JSVM_Status status;
NODE_API_CALL(status, env, OH_JSVM_CloseEnvScope(env, _envScope));

NODE_API_CALL(status, env, OH_JSVM_DestroyEnv(env));
NODE_API_CALL(status, env, OH_JSVM_CloseVMScope(_vm, _vmScope));
NODE_API_CALL(status, env, OH_JSVM_DestroyVM(_vm));
_envScope = nullptr;
env = nullptr;
_vmScope = nullptr;
_vm = nullptr;

_globalObj = nullptr;
_isValid = false;
_gcFunc = nullptr;
Expand Down
5 changes: 5 additions & 0 deletions native/cocos/bindings/jswrapper/jsvm/ScriptEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class ScriptEngine {
};
ScriptEngine();
~ScriptEngine();
/**
* @brief Shut down the JSVM engine, because cleanup doesn't destroy all objects immediately.
*/
void closeEngine();

/**
* @brief Sets the delegate for file operation.
* @param delegate[in] The delegate instance for file operation.
Expand Down
16 changes: 10 additions & 6 deletions native/cocos/engine/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,31 +73,31 @@
namespace {

bool setCanvasCallback(se::Object *global) {
se::AutoHandleScope scope;
const se::AutoHandleScope scope;
se::ScriptEngine *se = se::ScriptEngine::getInstance();
auto *window = CC_GET_MAIN_SYSTEM_WINDOW();
auto handler = window->getWindowHandle();
auto viewSize = window->getViewSize();
auto dpr = cc::BasePlatform::getPlatform()->getInterface<cc::IScreen>()->getDevicePixelRatio();

se::Value jsbVal;
bool ok = global->getProperty("jsb", &jsbVal);
const bool ok = global->getProperty("jsb", &jsbVal);
if (!jsbVal.isObject()) {
se::HandleObject jsbObj(se::Object::createPlainObject());
const se::HandleObject jsbObj(se::Object::createPlainObject());
global->setProperty("jsb", se::Value(jsbObj));
jsbVal.setObject(jsbObj, true);
}

se::Value windowVal;
jsbVal.toObject()->getProperty("window", &windowVal);
if (!windowVal.isObject()) {
se::HandleObject windowObj(se::Object::createPlainObject());
const se::HandleObject windowObj(se::Object::createPlainObject());
jsbVal.toObject()->setProperty("window", se::Value(windowObj));
windowVal.setObject(windowObj, true);
}

int width = static_cast<int>(viewSize.width / dpr);
int height = static_cast<int>(viewSize.height / dpr);
const int width = static_cast<int>(viewSize.width / dpr);
const int height = static_cast<int>(viewSize.height / dpr);
windowVal.toObject()->setProperty("innerWidth", se::Value(width));
windowVal.toObject()->setProperty("innerHeight", se::Value(height));

Expand Down Expand Up @@ -207,6 +207,10 @@ void Engine::destroy() {
if (cc::render::getRenderingModule()) {
cc::render::Factory::destroy(cc::render::getRenderingModule());
}
#if(CC_PLATFORM == CC_PLATFORM_OPENHARMONY && SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSVM)
// When using JSVM, not all objects are destroyed during cleanup, so we need to close JSVM at the end.
_scriptEngine->closeEngine();
#endif

CC_SAFE_DESTROY_AND_DELETE(_gfxDevice);
delete _fs;
Expand Down
Loading