Files
LayaNative2.0/Conch/source/conch/JCScriptRuntime.cpp
T
lvfulong 89637246a7 同步
2026-02-25 19:05:22 +08:00

1118 lines
38 KiB
C++

/**
@file JCScriptRuntime.cpp
@brief
@author James
@version 1.0
@date 2016_5_13
*/
#include "JCScriptRuntime.h"
#include <algorithm>
#include <cstring>
#include <util/Log.h>
#include "JSWrapper/JSInterface/JSInterface.h"
#include "JSWrapper/LayaWrap/JSFileReader.h"
#include "JSWrapper/LayaWrap/JSGlobalExportCFun.h"
#include "JSWrapper/LayaWrap/JSInput.h"
#include <downloadCache/JCFileSource.h>
#include <resource/JCFileResManager.h>
#include "Audio/JCAudioManager.h"
#include "JCSystemConfig.h"
#include "JCConch.h"
#include <Performance/JCPerfHUD.h>
#include <downloadMgr/JCDownloadMgr.h>
#include <inttypes.h>
#include "JSWrapper/LayaWrap/JSCallbackFuncObj.h"
#include "JSWrapper/LayaWrap/JSLayaGL.h"
#include <LayaGL/JCLayaGLDispatch.h>
#include <webglplus/JCWebGLPlus.h>
#include "JSWrapper/LayaWrap/JSPromiseRejectionEvent.h"
#ifdef JS_V8
#include "JSWrapper/v8debug/debug-agent.h"
#elif JS_JSVM
#include "JSWrapper/v8debug/debug-agent.h"
#include "ark_runtime/jsvm.h"
#endif
//#include "btBulletDynamicsCommon.h"
extern int g_nInnerWidth;
extern int g_nInnerHeight;
extern bool g_bGLCanvasSizeChanged;
#ifdef ANDROID
#include <android/configuration.h>
#include <dlfcn.h>
// Declaration for native chreographer API.
struct AChoreographer;
typedef void(*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
typedef AChoreographer* (*func_AChoreographer_getInstance)();
typedef void(*func_AChoreographer_postFrameCallback)(
AChoreographer* choreographer, AChoreographer_frameCallback callback,
void* data);
// Function pointers for native Choreographer API.
func_AChoreographer_getInstance AChoreographer_getInstance_;
func_AChoreographer_postFrameCallback AChoreographer_postFrameCallback_;
void choreographer_callback(long frameTimeNanos, void* data);
double lastVSYNC = 0.0;
void StartChoreographer()
{
auto choreographer = AChoreographer_getInstance_();
AChoreographer_postFrameCallback_(choreographer, choreographer_callback, nullptr);
}
void choreographer_callback(long frameTimeNanos, void* data)
{
//LOGE("---TM:"PRIu64",d=%f", frameTimeNanos,(frameTimeNanos- lastVSYNC)/(1e6f));
double vsynctm = ((unsigned long)frameTimeNanos) / 1e6;
auto ctm = laya::tmGetCurms();
//LOGE("---TM:%f,d=%f,cur=%f,d=%f", (float)vsynctm, (float)(vsynctm - lastVSYNC) , ctm,(ctm-vsynctm));
laya::JCPerfHUD::m_tmVSYNC = vsynctm;
if (laya::JCScriptRuntime::s_JSRT) {
laya::JCScriptRuntime::s_JSRT->onVSyncEvent(vsynctm);
}
lastVSYNC = vsynctm;
StartChoreographer();
}
void initChoreographer()
{
return;//不用了,全部用java更方便一些。
//>=24
void* lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL);
if (lib != nullptr)
{
LOGE("Run with Choreographer Native API.");
//api_mode_ = kAPINativeChoreographer;
// Retrieve function pointers from shared object.
AChoreographer_getInstance_ = reinterpret_cast<func_AChoreographer_getInstance>(dlsym(lib, "AChoreographer_getInstance"));
AChoreographer_postFrameCallback_ = reinterpret_cast<func_AChoreographer_postFrameCallback>(dlsym(lib, "AChoreographer_postFrameCallback"));
//assert(AChoreographer_getInstance_);
//assert(AChoreographer_postFrameCallback_);
//开始
if (AChoreographer_getInstance_)
StartChoreographer();
}
}
#endif
namespace laya
{
JCScriptRuntime* JCScriptRuntime::s_JSRT = NULL;
JCScriptRuntime::JCScriptRuntime()
{
#ifdef ANDROID
initChoreographer();
#endif
s_JSRT = this;
m_pPoster = NULL;
m_bHasJSThread = false;
m_pFileResMgr = NULL;
m_pAssetsRes = NULL;
m_bIsExit = false;
m_pUrl = new JCUrl();
m_nThreadState = 0;
m_bJSOnBackPressedFunctionSet = false;
m_bHasPostVsync = false;
m_pArrayBufferManager = JCWebGLPlus::getInstance()->m_pJSArrayBufferManager;
m_pABManagerSyncToRender = JCWebGLPlus::getInstance()->m_pJSABManagerSyncToRender;
m_pCallbackFuncManager = new JCOrderResManager<JSCallbackFuncObj>(false);
m_pRegister = new JCRegister();
JCAudioManager::GetInstance();
if (g_kSystemConfig.m_nThreadMODE == THREAD_MODE_SINGLE)
{
m_pScriptThread = new JSSingleThread();
}
else
{
m_pScriptThread = new JSMulThread();
}
if (g_kSystemConfig.m_nThreadMODE == THREAD_MODE_DOUBLE)
{
m_pRenderCmd = new JCCommandEncoderBuffer(102400, 1280);
}
else
{
m_pRenderCmd = new JCCommandEncoderBuffer(0,0);
}
m_pGCCmd = new JCCommandEncoderBuffer(2048, 2048);
//事件现在有问题,只能添加不能删除。所以不要调用多次。
m_pScriptThread->on(JCWorkerThread::Event_threadStart, std::bind(&JCScriptRuntime::onThreadInit, this, std::placeholders::_1));
m_pScriptThread->on(JCWorkerThread::Event_threadStop, std::bind(&JCScriptRuntime::onThreadExit, this, std::placeholders::_1));
m_nFPS = 60;
m_nDelayTime = 16;
m_nUpdateCount = 0;
m_bRunDraw = true;
m_dbLastUsedVsync = 0;
m_dbCurVsync = 0;
#ifdef JS_V8
m_pDbgAgent = nullptr;
#elif JS_JSVM
m_pDbgAgent = nullptr;
#endif
#ifndef WIN32
m_pCurEditBox = NULL;
#endif
m_bReload = false;
}
JCScriptRuntime::~JCScriptRuntime()
{
//在这时候js可能还在执行。需要先停止才能释放其运行环境。
#ifdef __APPLE__
m_pScriptThread->stop();
#else
if (g_kSystemConfig.m_nThreadMODE == THREAD_MODE_DOUBLE)
{
m_pScriptThread->stop();
}
#endif
if (m_pScriptThread)
{
delete m_pScriptThread;
m_pScriptThread = NULL;
}
s_JSRT = NULL;
m_pFileResMgr = NULL;
m_pAssetsRes = NULL;
if (m_pUrl)
{
delete m_pUrl;
m_pUrl = NULL;
}
if (m_pCallbackFuncManager)
{
delete m_pCallbackFuncManager;
m_pCallbackFuncManager = NULL;
}
if (m_pRegister)
{
delete m_pRegister;
m_pRegister = NULL;
}
if (m_pRenderCmd)
{
delete m_pRenderCmd;
m_pRenderCmd = NULL;
}
if (m_pGCCmd)
{
delete m_pGCCmd;
m_pGCCmd = NULL;
}
JCWebGLPlus::releaseInstance();
}
void JCScriptRuntime::init(JCFileResManager* pFileMgr, JCFileSource* pAssetRes, IConchThreadCmdMgr* pThreadCmdSender)
{
m_pFileResMgr = pFileMgr;
m_pAssetsRes = pAssetRes;
m_pPoster = pThreadCmdSender;
}
#if defined(JS_V8) || defined(JS_JSVM)
// Promise rejection pipeline - engine-specific helpers
void JCScriptRuntime::ReleaseRecord(PromiseRejectionRecord& record)
{
#ifdef JS_V8
record.promise.Reset();
record.reason.Reset();
#elif JS_JSVM
auto env = Javascript::getEnv();
JSVM_Status status;
if (record.promise) { JSVM_API_CALL(status, env, OH_JSVM_DeleteReference(env, record.promise)); }
if (record.reason) { JSVM_API_CALL(status, env, OH_JSVM_DeleteReference(env, record.reason)); }
#endif
}
JCScriptRuntime::PromiseRejectionList::iterator JCScriptRuntime::FindPromiseRecord(
PromiseRejectionList& list, JSValueAsParam promise)
{
#ifdef JS_V8
v8::Isolate* isolate = v8::Isolate::GetCurrent();
for (auto it = list.begin(); it != list.end(); ++it)
{
v8::Local<v8::Value> p = v8::Local<v8::Value>::New(isolate, it->promise);
if (p->StrictEquals(promise))
{
return it;
}
}
#elif JS_JSVM
auto env = Javascript::getEnv();
JSVM_Status status;
for (auto it = list.begin(); it != list.end(); ++it)
{
JSVM_Value p;
JSVM_API_CALL(status, env, OH_JSVM_GetReferenceValue(env, it->promise, &p));
bool equals = false;
JSVM_API_CALL(status, env, OH_JSVM_StrictEquals(env, p, promise, &equals));
if (equals)
{
return it;
}
}
#endif
return list.end();
}
bool JCScriptRuntime::RemovePromiseRecord(PromiseRejectionList& list, JSValueAsParam promise)
{
auto it = FindPromiseRecord(list, promise);
if (it != list.end())
{
ReleaseRecord(*it);
list.erase(it);
return true;
}
return false;
}
void JCScriptRuntime::EmitPromiseRejectionEvent(const char* type, const PromiseRejectionRecord& record)
{
if (m_vJSOnUnhandledRejectionFunctions.empty())
return;
#ifdef JS_V8
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> eventObj = v8::Object::New(isolate);
eventObj->Set(context,
v8::String::NewFromUtf8(isolate, "promise").ToLocalChecked(),
v8::Local<v8::Value>::New(isolate, record.promise)).Check();
if (record.reasonType == PromiseRejectionRecord::ReasonType::ReasonTypeString)
{
eventObj->Set(context,
v8::String::NewFromUtf8(isolate, "reason").ToLocalChecked(),
v8::String::NewFromUtf8(isolate, record.reasonString.c_str()).ToLocalChecked()).Check();
}
else if (record.reasonType == PromiseRejectionRecord::ReasonType::ReasonTypeObject)
{
eventObj->Set(context,
v8::String::NewFromUtf8(isolate, "reason").ToLocalChecked(),
v8::Local<v8::Value>::New(isolate, record.reason)).Check();
}
eventObj->Set(context,
v8::String::NewFromUtf8(isolate, "type").ToLocalChecked(),
v8::String::NewFromUtf8(isolate, type).ToLocalChecked()).Check();
for (auto& funcGlobal : m_vJSOnUnhandledRejectionFunctions)
{
v8::Local<v8::Function> func = v8::Local<v8::Function>::New(isolate, funcGlobal);
v8::Local<v8::Value> argv[] = { eventObj };
func->Call(context, context->Global(), 1, argv).FromMaybe(v8::Local<v8::Value>());
}
#elif JS_JSVM
auto env = Javascript::getEnv();
JSVM_Status status;
AutoHandleScope scope;
JSVM_Value eventObj;
JSVM_API_CALL(status, env, OH_JSVM_CreateObject(env, &eventObj));
JSVM_Value promiseVal;
JSVM_API_CALL(status, env, OH_JSVM_GetReferenceValue(env, record.promise, &promiseVal));
JSVM_API_CALL(status, env, OH_JSVM_SetNamedProperty(env, eventObj, "promise", promiseVal));
if (record.reasonType == PromiseRejectionRecord::ReasonType::ReasonTypeString)
{
JSVM_Value reasonVal;
JSVM_API_CALL(status, env, OH_JSVM_CreateStringUtf8(env, record.reasonString.c_str(), record.reasonString.size(), &reasonVal));
JSVM_API_CALL(status, env, OH_JSVM_SetNamedProperty(env, eventObj, "reason", reasonVal));
}
else if (record.reasonType == PromiseRejectionRecord::ReasonType::ReasonTypeObject)
{
JSVM_Value reasonVal;
JSVM_API_CALL(status, env, OH_JSVM_GetReferenceValue(env, record.reason, &reasonVal));
JSVM_API_CALL(status, env, OH_JSVM_SetNamedProperty(env, eventObj, "reason", reasonVal));
}
JSVM_Value typeVal;
JSVM_API_CALL(status, env, OH_JSVM_CreateStringUtf8(env, type, strlen(type), &typeVal));
JSVM_API_CALL(status, env, OH_JSVM_SetNamedProperty(env, eventObj, "type", typeVal));
JSVM_Value global;
JSVM_API_CALL(status, env, OH_JSVM_GetGlobal(env, &global));
for (auto& funcRef : m_vJSOnUnhandledRejectionFunctions)
{
JSVM_Value func;
JSVM_API_CALL(status, env, OH_JSVM_GetReferenceValue(env, funcRef, &func));
JSVM_Value argv[] = { eventObj };
JSVM_Value result;
JSVM_API_CALL(status, env, OH_JSVM_CallFunction(env, global, func, 1, argv, &result));
}
#endif
}
void JCScriptRuntime::EnqueueUnhandledRejection(JSValueAsParam promise, JSValueAsParam reason)
{
PromiseRejectionRecord record;
record.uid = ++m_lastPromiseRejectionId;
#ifdef JS_V8
v8::Isolate* isolate = v8::Isolate::GetCurrent();
record.promise.Reset(isolate, promise);
if (!reason.IsEmpty() && reason->IsString())
{
record.reasonType = PromiseRejectionRecord::ReasonType::ReasonTypeString;
v8::String::Utf8Value str(isolate, reason);
record.reasonString = *str ? *str : "";
}
else if (!reason.IsEmpty() && reason->IsObject())
{
record.reasonType = PromiseRejectionRecord::ReasonType::ReasonTypeObject;
record.reasonString = "";
record.reason.Reset(isolate, reason);
}
else
{
record.reasonType = PromiseRejectionRecord::ReasonType::ReasonTypeString;
record.reasonString = "";
}
#elif JS_JSVM
auto env = Javascript::getEnv();
JSVM_Status status;
JSVM_API_CALL(status, env, OH_JSVM_CreateReference(env, promise, 1, &record.promise));
bool isString = false;
bool isObject = false;
if (reason != nullptr)
{
JSVM_API_CALL(status, env, OH_JSVM_IsString(env, reason, &isString));
if (!isString)
{
JSVM_API_CALL(status, env, OH_JSVM_IsObject(env, reason, &isObject));
}
}
if (isString)
{
record.reasonType = PromiseRejectionRecord::ReasonType::ReasonTypeString;
record.reasonString = __TransferToCpp<std::string>::ToCpp(reason);
}
else if (isObject)
{
record.reasonType = PromiseRejectionRecord::ReasonType::ReasonTypeObject;
record.reasonString = "";
JSVM_API_CALL(status, env, OH_JSVM_CreateReference(env, reason, 1, &record.reason));
}
else
{
record.reasonType = PromiseRejectionRecord::ReasonType::ReasonTypeString;
record.reasonString = "";
}
#endif
m_pendingUnhandledRejections.emplace_back(std::move(record));
SchedulePromiseRejectionProcessing();
}
// Promise rejection pipeline - unified methods (no engine-specific code)
void JCScriptRuntime::SchedulePromiseRejectionProcessing()
{
if (m_isProcessingScheduled)
return;
m_isProcessingScheduled = true;
m_pScriptThread->post([this]() {
this->ProcessPromiseRejectionEvents();
});
}
void JCScriptRuntime::ProcessPromiseRejectionEvents()
{
m_isProcessingScheduled = false;
PromiseRejectionList pending;
pending.swap(m_pendingUnhandledRejections);
for (auto& record : pending)
{
record.warned = true;
EmitPromiseRejectionEvent("unhandledrejection", record);
m_maybeUnhandledRejections.emplace_back(std::move(record));
}
if (!m_asyncHandledRejections.empty())
{
std::vector<uint64_t> handled;
handled.swap(m_asyncHandledRejections);
for (const auto uid : handled)
{
auto it = std::find_if(
m_maybeUnhandledRejections.begin(),
m_maybeUnhandledRejections.end(),
[uid](const PromiseRejectionRecord& entry) { return entry.uid == uid; });
if (it != m_maybeUnhandledRejections.end())
{
EmitPromiseRejectionEvent("rejectionhandled", *it);
ReleaseRecord(*it);
m_maybeUnhandledRejections.erase(it);
}
}
}
}
void JCScriptRuntime::EnqueueRejectionHandled(JSValueAsParam promise)
{
if (RemovePromiseRecord(m_pendingUnhandledRejections, promise))
return;
auto it = FindPromiseRecord(m_maybeUnhandledRejections, promise);
if (it != m_maybeUnhandledRejections.end())
{
if (std::find(m_asyncHandledRejections.begin(),
m_asyncHandledRejections.end(),
it->uid) == m_asyncHandledRejections.end())
{
m_asyncHandledRejections.push_back(it->uid);
SchedulePromiseRejectionProcessing();
}
}
}
void JCScriptRuntime::ClearPromiseRejectionTracking()
{
for (auto& record : m_pendingUnhandledRejections)
ReleaseRecord(record);
m_pendingUnhandledRejections.clear();
for (auto& record : m_maybeUnhandledRejections)
ReleaseRecord(record);
m_maybeUnhandledRejections.clear();
m_asyncHandledRejections.clear();
m_lastPromiseRejectionId = 0;
m_isProcessingScheduled = false;
}
void JCScriptRuntime::HandlePromiseRejectionEvent(JSValueAsParam promise, JSValueAsParam reason, const char* type)
{
if (type == nullptr)
return;
if (std::strcmp(type, "unhandledrejection") == 0)
{
EnqueueUnhandledRejection(promise, reason);
}
else if (std::strcmp(type, "rejectionhandled") == 0)
{
EnqueueRejectionHandled(promise);
}
}
#endif
void JCScriptRuntime::start(const char* pStartJS)
{
LOGI("Start js %s", pStartJS);
if (pStartJS)m_strStartJS = pStartJS;
#if defined(JS_V8) || defined(JS_JSVM)
m_pScriptThread->initialize(JCConch::s_pConch->m_nJSDebugPort,
[](JSValueAsParam promise, JSValueAsParam reason, const char* type) {
if (JCScriptRuntime::s_JSRT) {
JCScriptRuntime::s_JSRT->HandlePromiseRejectionEvent(promise, reason, type);
}
});
#else
m_pScriptThread->initialize(JCConch::s_pConch->m_nJSDebugPort);
#endif
#ifdef __APPLE__
m_pScriptThread->setLoopFunc(std::bind(&JCScriptRuntime::onUpdate, this));
#endif
m_nThreadState = 1;
m_pScriptThread->start();
}
void JCScriptRuntime::stop()
{
LOGI("Stop js start...");
while (m_nThreadState==1)
{
LOGI("stop: wait for thread to start...");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
#ifdef __APPLE__
if (JCConch::s_pConchRender)
{
while (JCConch::s_pConchRender->m_kRenderSem.getDataNum() == 1)
{
JCConch::s_pConchRender->renderFrame(0, false);
}
}
#endif
m_pScriptThread->stop();
m_pScriptThread->uninitialize();
LOGI("Stop js end.");
}
void JCScriptRuntime::reload()
{
m_bReload = false;
#ifdef __APPLE__
if(JCConch::s_pConchRender)
{
JCConch::s_pConchRender->m_bStopRender = true;
}
#else
if (g_kSystemConfig.m_nThreadMODE == THREAD_MODE_SINGLE)
{
JCConch::s_pConchRender->m_bStopRender = true;
}
#endif
stop();
//为了避免上个js的下载的影响,去掉下载任务
//这个必须在stop之后做,且保证stop确实会停止线程,包括还没有启动的线程。
//如果线程还没有启动,stop会等待线程先启动。这样就可能会执行脚本,所以,清理必须放在stop之后。
JCDownloadMgr* pNetLoader = JCDownloadMgr::getInstance();
pNetLoader->stopCurTask();
pNetLoader->clearAllAsyncTask();
pNetLoader->resetDownloadTail(); //防止第二次进入的时候,下载错误的dcc等
pNetLoader->resetFinalReplacePath();
pNetLoader->resetDownloadReplaceExt();
//文件资源不能跨js环境使用,所以必须clear
// 例如一个资源正在下载,则可能的问题:1.可能会上个线程取消了,不会再回调, 2. 自己希望回调的是上个js环境,也无法传给新的js环境。
// 所以需要clear。
m_pFileResMgr->clear();
start(m_strStartJS.c_str());
#ifndef __APPLE_
if (g_kSystemConfig.m_nThreadMODE == THREAD_MODE_SINGLE)
{
if (JCConch::s_pConch)
{
JCConch::s_pConch->postCmdToMainThread(JCConch::CMD_MgrStartThread, 0, 0);
}
}
#endif
}
void JCScriptRuntime::onThreadInit(JCEventEmitter::evtPtr evt)
{
LOGI("js thread started.");
m_nThreadState = 2;
JCPerfHUD::resetFrame();
#ifdef JS_V8
JSObjNode::s_pListJSObj = new JCSimpList();
#ifdef JS_V8_DEBUGGER
if (m_pDbgAgent)
{
m_pDbgAgent->onJSStart(m_pScriptThread,(JCConch::s_pConch->m_nJSDebugMode == JS_DEBUG_MODE_WAIT) ? true : false);
LOGI("js debug open mode: %d port %d", JCConch::s_pConch->m_nJSDebugMode, JCConch::s_pConch->m_nJSDebugPort);
}
else
{
LOGI("js debug closed");
}
#endif
#elif JS_JSVM
JSObjNode::s_pListJSObj = new JCSimpList();
//#ifdef JS_V8_DEBUGGER
auto env = ENV;
if (JCConch::s_pConch->m_nJSDebugMode != JS_DEBUG_MODE_OFF)
{
OH_JSVM_OpenInspector(env, "localhost", JCConch::s_pConch->m_nJSDebugPort);
if (JCConch::s_pConch->m_nJSDebugMode == JS_DEBUG_MODE_WAIT) {
OH_JSVM_WaitForDebugger(env, true);
}
}
//#endif
#endif
JCConch::s_pConchRender->m_pImageManager->resetJSThread();
//JS线程的数据清空一下
m_pCallbackFuncManager->clearAllRes();
m_pCallbackFuncManager->resetGlobalID();
m_pArrayBufferManager->clearAll();
m_pABManagerSyncToRender->clearAll();
//给渲染线程发开始指令
startScriptOnRenderThread();
JsFile::RegisterToJS();
JsFileReader::RegisterToJS();
JSGlobalExportC();
//设置js的一些环境。必须在所有导出之后,执行其他脚本之前。
//#ifndef WIN32
//JSP_RUN_SCRIPT((const char*)"function getExePath(){return null;}");
//#endif
{
char* sJSRuntime = NULL;
int nSize = 0;
if (m_pAssetsRes->loadFileContent("scripts/runtimeInit.js", sJSRuntime, nSize))
{
//#ifdef JS_V8
// JSP_RUN_SCRIPT(sJSRuntime);
//#elif JS_JSVM
JSP_RUN_SCRIPT(sJSRuntime, "scripts/runtimeInit.js");
//#endif
delete[] sJSRuntime;
}
}
char* sJCBuffer = NULL;
int nJSSize = 0;
if (m_pAssetsRes->loadFileContent(m_strStartJS.c_str(), sJCBuffer, nJSSize))
{
std::string kBuf = "(function(window){\n'use strict'\n";
kBuf += sJCBuffer;
kBuf += "\n})(window);\n//@ sourceURL=apploader.js";
#ifdef JS_V8
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate);
v8::TryCatch try_catch(isolate);
JSP_RUN_SCRIPT(kBuf.c_str(), NULL);
if (try_catch.HasCaught())
{
__JSRun::ReportException(isolate, &try_catch);
}
#else
//#ifdef JS_V8
// JSP_RUN_SCRIPT(kBuf.c_str());
//#elif JS_JSVM
JSP_RUN_SCRIPT(kBuf.c_str(),nullptr);
//#endif
#endif
delete[] sJCBuffer;
sJCBuffer = NULL;
}
#ifndef __APPLE__
if (g_kSystemConfig.m_nThreadMODE == THREAD_MODE_DOUBLE)
{
m_pScriptThread->post(std::bind(&JCScriptRuntime::onUpdate, this));
}
#endif
//#ifdef JS_V8
// JSP_RUN_SCRIPT("gc();gc();gc();");
//#elif JS_JSVM
JSP_RUN_SCRIPT("gc();gc();gc();",nullptr);
//#endif
}
void JCScriptRuntime::onThreadExit(JCEventEmitter::evtPtr evt)
{
if (m_nThreadState == 0)
{
return;
}
LOGI("js thread exiting...");
m_nThreadState = 0;
m_pJSOnFrameFunction.Reset();
m_pJSOnResizeFunction.Reset();
m_pJSOnFocusFunction.Reset();
m_pJSOnBlurFunction.Reset();
m_pJSMouseEvtFunction.Reset();
m_pJSKeyEvtFunction.Reset();
m_pJSTouchEvtFunction.Reset();
m_pJSDeviceMotionEvtFunction.Reset();
m_pJSNetworkEvtFunction.Reset();
m_pJSOnBackPressedFunction.Reset();
m_pJSOnceOtherEvtFuction.Reset();
m_pJSOnDrawFunction.Reset();
m_bJSBulletGetWorldTransformHandle.Reset();
m_bJSBulletSetWorldTransformHandle.Reset();
#if defined(JS_V8) || defined(JS_JSVM)
ClearPromiseRejectionTracking();
#ifdef JS_JSVM
{
auto env = Javascript::getEnv();
JSVM_Status status;
for (auto& ref : m_vJSOnUnhandledRejectionFunctions)
{
if (ref) { JSVM_API_CALL(status, env, OH_JSVM_DeleteReference(env, ref)); }
}
}
#endif
m_vJSOnUnhandledRejectionFunctions.clear();
#endif
#ifdef OHOS
m_pGameJsOnMessage.Reset();
#endif
#ifndef WIN32
m_pCurEditBox = NULL;
#endif
JSClassMgr::GetInstance()->resetAllRegClass();
#ifdef JS_V8
JCSimpList* pNodeLists = JSObjNode::s_pListJSObj;
if (pNodeLists != NULL)
{
JCListNode* pCur = pNodeLists->begin();
JCListNode* pEnd = pNodeLists->end();
while (pCur != pEnd)
{
JSObjNode* pJsCur = (JSObjNode*)pCur;
pCur = pNodeLists->delNode(pCur);
delete pJsCur;
}
delete JSObjNode::s_pListJSObj;
JSObjNode::s_pListJSObj = nullptr;
}
#ifdef JS_V8_DEBUGGER
if (m_pDbgAgent)
{
m_pDbgAgent->onJSExit();
}
#endif
#elif JS_JSVM
JSObjBaseJSVM::restarting = true;
JSObjBaseJSVM::resetBaseSet();
JCSimpList* pNodeLists = JSObjNode::s_pListJSObj;
if (pNodeLists != NULL)
{
JCListNode* pCur = pNodeLists->begin();
JCListNode* pEnd = pNodeLists->end();
while (pCur != pEnd)
{
JSObjNode* pJsCur = (JSObjNode*)pCur;
pCur = pNodeLists->delNode(pCur);
delete pJsCur;
}
delete JSObjNode::s_pListJSObj;
JSObjNode::s_pListJSObj = nullptr;
}
//#ifdef JS_V8_DEBUGGER
auto env = ENV;
if (JCConch::s_pConch->m_nJSDebugMode != JS_DEBUG_MODE_OFF)
{
OH_JSVM_CloseInspector(env);
}
//#endif
#elif JS_JSC
JSP_RESET_GLOBAL_FUNCTION;
#endif
JCAudioManager::ClearAllWork();
JCAudioManager::GetInstance()->stopMp3();
JCAudioManager::GetInstance()->pauseMp3();
m_pCallbackFuncManager->clearAllRes();
m_pCallbackFuncManager->resetGlobalID();
JCWebGLPlus::getInstance()->clearAll();
}
void JCScriptRuntime::onUpdate()
{
PERF_INITVAR(nBenginTime);
#ifdef JS_V8
m_pScriptThread->runDbgFuncs();
#elif JS_JSVM
m_pScriptThread->runDbgFuncs();
#endif
m_nUpdateCount++;
bool bRunOnDraw = false;
double nTime = tmGetCurms();
#ifdef ANDROID1
if (m_bRunDraw)
{
m_bRunDraw = false;
bRunOnDraw = true;
m_dbLastUsedVsync = m_dbCurVsync;
onUpdateDraw(m_dbCurVsync);
}
#else
if (!g_kSystemConfig.m_bUseChoreographer)
{
JCPerfHUD::m_tmVSYNC = nTime;
}
#endif
onUpdateDraw(JCPerfHUD::m_tmVSYNC);
JSInput* pInput = JSInput::getInstance();
if ( pInput->m_bTouchMode )
{
pInput->swapCurrentTouchEvent();
if( pInput->m_vInputEventsJS.size() > 0 )
{
pInput->m_nTouchFrame = 120;
for (int i = 0, nSize = (int)pInput->m_vInputEventsJS.size(); i < nSize; i++ )
{
TouchEventInfo* touchEvent = &pInput->m_vInputEventsJS[i];
m_pJSTouchEvtFunction.Call(touchEvent->nType, touchEvent->nID,"type",touchEvent->x, touchEvent->y);
}
}
if( pInput->m_nTouchFrame > 0 )
{
pInput->m_nTouchFrame--;
}
}
if (g_bGLCanvasSizeChanged)
{
m_pJSOnResizeFunction.Call(g_nInnerWidth, g_nInnerHeight);
//m_pRootCanvas->size( g_nInnerWidth,g_nInnerHeight );
g_bGLCanvasSizeChanged = false;
}
int nUpdateNum = m_nUpdateCount % 3;
switch (nUpdateNum)
{
case 0:
JCAudioManager::GetInstance()->update();
break;
case 1:
//如果有需要清理的或者update可以放到这
JCAudioManager::GetInstance()->m_pWavPlayer->autoGarbageCollection();
break;
case 2:
//如果有需要清理的或者update可以放到这
break;
}
JS_TRY;
m_pJSOnFrameFunction.Call();
JS_CATCH;
float dt = tmGetCurms() - nBenginTime;
PERF_UPDATE_DATA(JCPerfHUD::PHUD_JS_DELAY, (float)dt);
return;
}
void JCScriptRuntime::onUpdateDraw(double vsyncTime)
{
m_bHasPostVsync = false;
if (!m_pJSOnDrawFunction.Empty())
{
JS_TRY;
m_pJSOnDrawFunction.Call(vsyncTime);
JS_CATCH;
runLayaGL();
}
}
void JCScriptRuntime::runLayaGL()
{
JSLayaGL* pLayaGL = JSLayaGL::getInstance();
if (pLayaGL->m_nFrameAndSyncCountABListID != -1)
{
//恢复frameCount和syncCount
JCArrayBufferManager::ArrayBufferContent* pBuffer = m_pArrayBufferManager->getArrayBuffer(pLayaGL->m_nFrameAndSyncCountABListID);
if (pBuffer)
{
int* pData = (int*)pBuffer->m_pBuffer;
pLayaGL->m_nSyncArrayBufferCount=pData[1];
pData[0]++;
pData[1]=0;
pLayaGL->m_nFrameCount = pData[0];
}
}
if (g_kSystemConfig.m_nThreadMODE == THREAD_MODE_DOUBLE)
{
flushSharedCmdBuffer();
if (m_pGCCmd->getDataSize() > 0)
{
m_pRenderCmd->append(m_pGCCmd->getBuffer(), m_pGCCmd->getDataSize());
m_pGCCmd->clearData();
}
if (pLayaGL->m_nSyncToRenderABListID != -1)
{
JCArrayBufferManager::ArrayBufferContent* pSyncBufferList = m_pArrayBufferManager->getArrayBuffer(pLayaGL->m_nSyncToRenderABListID);
JCConch::s_pConchRender->setRenderData(m_pABManagerSyncToRender, pSyncBufferList, pLayaGL->m_nSyncArrayBufferCount, m_pRenderCmd, m_nDelayTime, m_nFPS);
}
else
{
JCConch::s_pConchRender->setRenderData(NULL, NULL, 0, m_pRenderCmd, m_nDelayTime, m_nFPS);
}
}
else
{
dispatchLayaGLBuffer(true);
}
}
void JCScriptRuntime::dispatchLayaGLBuffer(bool bDispatchGC)
{
JCArrayBufferManager::ArrayBufferContent* pRootCommandEncoder = JSLayaGL::getInstance()->m_pRootCommandEncoder;
if (!pRootCommandEncoder)return;
char* pBuffer = pRootCommandEncoder->m_pBuffer;
int nLen = (*(int*)pBuffer - 1) * 4;
m_pRenderCmd->setShareBuffer(pBuffer + 4, nLen);
((int*)pBuffer)[0] = 1;
JCLayaGLDispatch::dispatchAllCmds(m_pRenderCmd);
m_pRenderCmd->clearData();
if (bDispatchGC && m_pGCCmd->getDataSize() > 0)
{
JCLayaGLDispatch::dispatchAllCmds(m_pGCCmd);
m_pGCCmd->clearData();
}
}
void JCScriptRuntime::flushSharedCmdBuffer()
{
JCArrayBufferManager::ArrayBufferContent* pRootCommandEncoder = JSLayaGL::getInstance()->m_pRootCommandEncoder;
if (!pRootCommandEncoder)return;
char* pBuffer = pRootCommandEncoder->m_pBuffer;
int nLen = (*(int*)pBuffer - 1)*4;
if (nLen > 0)
{
//TODO想办法不拷贝,改成share
m_pRenderCmd->append(pBuffer + 4, nLen);
((int*)pBuffer)[0] = 1;
}
}
void JCScriptRuntime::onVSyncEvent(double vsyncTime)
{
JCPerfHUD::m_tmVSYNC = vsyncTime;
m_dbCurVsync = vsyncTime;
//m_bRunDraw = true;
//这个事件的速度始终固定,但是js可能会非常卡,导致大量积累,所以要保护 一下。
if (!m_bHasPostVsync)
{
m_bHasPostVsync = true;
m_pScriptThread->post(std::bind(&JCScriptRuntime::onUpdate, this));
}
}
void JCScriptRuntime::dispatchInputEvent(inputEvent e)
{
JSInput::getInstance()->activeCall(e);
}
void JCScriptRuntime::dispatchInputEvent(DeviceOrientationEvent e)
{
JSInput::getInstance()->activeCall(e);
}
void JCScriptRuntime::dispatchInputEvent(DeviceMotionEvent e)
{
JSInput::getInstance()->activeCall(e);
}
void JCScriptRuntime::onNetworkChanged(int nType)
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::onNetworkChangedCallJSFunction, this, nType);
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::onNetworkChangedCallJSFunction(int nType)
{
m_pJSNetworkEvtFunction.Call(nType);
}
void JCScriptRuntime::jsGC()
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::jsGCCallJSFunction, this);
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::jsGCCallJSFunction()
{
//#ifdef JS_V8
// JSP_RUN_SCRIPT("gc()");
//#elif JS_JSVM
JSP_RUN_SCRIPT("gc()",nullptr);
//#endif
}
void JCScriptRuntime::callJC(std::string sFunctionName, std::string sJsonParam, std::string sCallbackFunction)
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::callJSFuncton, this, sFunctionName, sJsonParam, sCallbackFunction );
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::callJSString( std::string sBuffer )
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::callJSStringFunction, this,sBuffer);
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::callJSStringFunction( std::string sBuffer )
{
//#ifdef JS_V8
// JSP_RUN_SCRIPT(sBuffer.c_str());
//#elif JS_JSVM
JSP_RUN_SCRIPT(sBuffer.c_str(),nullptr);
//#endif
}
void JCScriptRuntime::callJSFuncton(std::string sFunctionName, std::string sJsonParam, std::string sCallbackFunction)
{
std::string sBuffer = sFunctionName;
sBuffer += "(\"";
sBuffer += sJsonParam;
sBuffer += "\",\"";
sBuffer += sCallbackFunction;
sBuffer += "\");";
LOGI("JCScriptRuntime::callJSFuncton buffer=%s",sBuffer.c_str() );
//#ifdef JS_V8
// JSP_RUN_SCRIPT( sBuffer.c_str() );
//#elif JS_JSVM
JSP_RUN_SCRIPT( sBuffer.c_str(),nullptr);
//#endif
}
void JCScriptRuntime::restoreAudio()
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::jsRestoreAudioFunction, this);
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::jsRestoreAudioFunction()
{
if(JCAudioManager::GetInstance()->getMp3Mute() == false && JCAudioManager::GetInstance()->getMp3Stopped() == false)
{
JCAudioManager::GetInstance()->resumeMp3();
}
}
void JCScriptRuntime::jsReloadUrl()
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::jsReloadUrlJSFunction, this);
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::jsReloadUrlJSFunction()
{
//#ifdef JS_V8
// JSP_RUN_SCRIPT("reloadJS(true)");
//#elif JS_JSVM
JSP_RUN_SCRIPT("reloadJS(true)",nullptr);
//#endif
}
void JCScriptRuntime::jsUrlback()
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::jsUrlbackJSFunction, this);
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::jsUrlbackJSFunction()
{
//#ifdef JS_V8
// JSP_RUN_SCRIPT("history.back()");
//#elif JS_JSVM
JSP_RUN_SCRIPT("history.back()",nullptr);
//#endif
}
void JCScriptRuntime::startScriptOnRenderThread()
{
m_pRenderCmd->clearData();
m_pGCCmd->clearData();
if (JCConch::s_pConch)
{
#ifdef __APPLE__
JCConch::s_pConch->postCmdToMainThread(JCConch::CMD_ClearRender, 0, 0);
#else
if (JCConch::s_pConchRender){
JCConch::s_pConchRender->clearAllData();
}
#endif
}
}
bool JCScriptRuntime::onBackPressed()
{
std::lock_guard<std::mutex> lock(m_OnBackPressedMutex);
if (!m_bJSOnBackPressedFunctionSet)
{
return false;
}
if (JCScriptRuntime::s_JSRT->m_pPoster)
{
JCScriptRuntime::s_JSRT->m_pPoster->postToJS([this]() {
this->m_pJSOnBackPressedFunction.Call();
});
}
return true;
}
void JCScriptRuntime::postToJS(const std::function<void(void)>& func)
{
m_pScriptThread->post(func);
}
void JCScriptRuntime::postToDownload(const std::function<void(void)>& funcf)
{
}
void JCScriptRuntime::postToDecoder(const std::function<void(void)>& func)
{
}
#if OHOS
void JCScriptRuntime::onJsObjHandle(std::string key, std::string value)
{
std::function<void(void)> pFunction = std::bind(&JCScriptRuntime::onJsObjHandleCallJSFunction, this, key, value);
m_pScriptThread->post(pFunction);
}
void JCScriptRuntime::onJsObjHandleCallJSFunction(std::string key, std::string value)
{
m_pGameJsOnMessage.Call(key,value);
}
#endif
}
//------------------------------------------------------------------------------
//-----------------------------END FILE--------------------------------