ScriptEngine.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. /****************************************************************************
  2. Copyright (c) 2016 Chukong Technologies Inc.
  3. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
  4. http://www.cocos2d-x.org
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11. The above copyright notice and this permission notice shall be included in
  12. all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. THE SOFTWARE.
  20. ****************************************************************************/
  21. #include "ScriptEngine.hpp"
  22. #include "platform/CCPlatformConfig.h"
  23. #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
  24. #include "Object.hpp"
  25. #include "Class.hpp"
  26. #include "Utils.hpp"
  27. #include "../State.hpp"
  28. #include "../MappingUtils.hpp"
  29. #if SE_ENABLE_INSPECTOR
  30. #include "debugger/inspector_agent.h"
  31. #include "debugger/env.h"
  32. #include "debugger/node.h"
  33. #endif
  34. #include <sstream>
  35. #define EXPOSE_GC "__jsb_gc__"
  36. uint32_t __jsbInvocationCount = 0;
  37. uint32_t __jsbStackFrameLimit = 20;
  38. #define RETRUN_VAL_IF_FAIL(cond, val) \
  39. if (!(cond)) return val
  40. namespace se {
  41. Class* __jsb_CCPrivateData_class = nullptr;
  42. namespace {
  43. ScriptEngine* __instance = nullptr;
  44. void __log(const v8::FunctionCallbackInfo<v8::Value>& info)
  45. {
  46. if (info[0]->IsString())
  47. {
  48. v8::String::Utf8Value utf8(v8::Isolate::GetCurrent(), info[0]);
  49. SE_LOGD("JS: %s\n", *utf8);
  50. }
  51. }
  52. void __forceGC(const v8::FunctionCallbackInfo<v8::Value>& info)
  53. {
  54. ScriptEngine::getInstance()->garbageCollect();
  55. }
  56. std::string stackTraceToString(v8::Local<v8::StackTrace> stack)
  57. {
  58. std::string stackStr;
  59. if (stack.IsEmpty())
  60. return stackStr;
  61. char tmp[100] = {0};
  62. for (int i = 0, e = stack->GetFrameCount(); i < e; ++i)
  63. {
  64. v8::Local<v8::StackFrame> frame = stack->GetFrame(v8::Isolate::GetCurrent(), i);
  65. v8::Local<v8::String> script = frame->GetScriptName();
  66. std::string scriptName;
  67. if (!script.IsEmpty())
  68. {
  69. scriptName = *v8::String::Utf8Value(v8::Isolate::GetCurrent(), script);
  70. }
  71. v8::Local<v8::String> func = frame->GetFunctionName();
  72. std::string funcName;
  73. if (!func.IsEmpty())
  74. {
  75. funcName = *v8::String::Utf8Value(v8::Isolate::GetCurrent(), func);
  76. }
  77. stackStr += "[";
  78. snprintf(tmp, sizeof(tmp), "%d", i);
  79. stackStr += tmp;
  80. stackStr += "]";
  81. stackStr += (funcName.empty() ? "anonymous" : funcName.c_str());
  82. stackStr += "@";
  83. stackStr += (scriptName.empty() ? "(no filename)" : scriptName.c_str());
  84. stackStr += ":";
  85. snprintf(tmp, sizeof(tmp), "%d", frame->GetLineNumber());
  86. stackStr += tmp;
  87. if (i < (e-1))
  88. {
  89. stackStr += "\n";
  90. }
  91. }
  92. return stackStr;
  93. }
  94. se::Value __oldConsoleLog;
  95. se::Value __oldConsoleDebug;
  96. se::Value __oldConsoleInfo;
  97. se::Value __oldConsoleWarn;
  98. se::Value __oldConsoleError;
  99. se::Value __oldConsoleAssert;
  100. bool JSB_console_format_log(State& s, const char* prefix, int msgIndex = 0)
  101. {
  102. if (msgIndex < 0)
  103. return false;
  104. const auto& args = s.args();
  105. int argc = (int)args.size();
  106. if ((argc - msgIndex) == 1)
  107. {
  108. std::string msg = args[msgIndex].toStringForce();
  109. SE_LOGD("JS: %s%s\n", prefix, msg.c_str());
  110. }
  111. else if (argc > 1)
  112. {
  113. std::string msg = args[msgIndex].toStringForce();
  114. size_t pos;
  115. for (int i = (msgIndex+1); i < argc; ++i)
  116. {
  117. pos = msg.find("%");
  118. if (pos != std::string::npos && pos != (msg.length()-1) && (msg[pos+1] == 'd' || msg[pos+1] == 's' || msg[pos+1] == 'f'))
  119. {
  120. msg.replace(pos, 2, args[i].toStringForce());
  121. }
  122. else
  123. {
  124. msg += " " + args[i].toStringForce();
  125. }
  126. }
  127. SE_LOGD("JS: %s%s\n", prefix, msg.c_str());
  128. }
  129. return true;
  130. }
  131. bool JSB_console_log(State& s)
  132. {
  133. JSB_console_format_log(s, "");
  134. __oldConsoleLog.toObject()->call(s.args(), s.thisObject());
  135. return true;
  136. }
  137. SE_BIND_FUNC(JSB_console_log)
  138. bool JSB_console_debug(State& s)
  139. {
  140. JSB_console_format_log(s, "[DEBUG]: ");
  141. __oldConsoleDebug.toObject()->call(s.args(), s.thisObject());
  142. return true;
  143. }
  144. SE_BIND_FUNC(JSB_console_debug)
  145. bool JSB_console_info(State& s)
  146. {
  147. JSB_console_format_log(s, "[INFO]: ");
  148. __oldConsoleInfo.toObject()->call(s.args(), s.thisObject());
  149. return true;
  150. }
  151. SE_BIND_FUNC(JSB_console_info)
  152. bool JSB_console_warn(State& s)
  153. {
  154. JSB_console_format_log(s, "[WARN]: ");
  155. __oldConsoleWarn.toObject()->call(s.args(), s.thisObject());
  156. return true;
  157. }
  158. SE_BIND_FUNC(JSB_console_warn)
  159. bool JSB_console_error(State& s)
  160. {
  161. JSB_console_format_log(s, "[ERROR]: ");
  162. __oldConsoleError.toObject()->call(s.args(), s.thisObject());
  163. return true;
  164. }
  165. SE_BIND_FUNC(JSB_console_error)
  166. bool JSB_console_assert(State& s)
  167. {
  168. const auto& args = s.args();
  169. if (!args.empty())
  170. {
  171. if (args[0].isBoolean() && !args[0].toBoolean())
  172. {
  173. JSB_console_format_log(s, "[ASSERT]: ", 1);
  174. __oldConsoleAssert.toObject()->call(s.args(), s.thisObject());
  175. }
  176. }
  177. return true;
  178. }
  179. SE_BIND_FUNC(JSB_console_assert)
  180. } // namespace {
  181. void ScriptEngine::callExceptionCallback(const char* location, const char* message, const char *stack) {
  182. if (_nativeExceptionCallback) {
  183. _nativeExceptionCallback(location, message, stack);
  184. }
  185. if (_jsExceptionCallback) {
  186. _jsExceptionCallback(location, message, stack);
  187. }
  188. }
  189. void ScriptEngine::onFatalErrorCallback(const char* location, const char* message)
  190. {
  191. std::string errorStr = "[FATAL ERROR] location: ";
  192. errorStr += location;
  193. errorStr += ", message: ";
  194. errorStr += message;
  195. SE_LOGE("%s\n", errorStr.c_str());
  196. getInstance()->callExceptionCallback(location, message, "(no stack information)");
  197. }
  198. void ScriptEngine::onOOMErrorCallback(const char* location, bool is_heap_oom)
  199. {
  200. std::string errorStr = "[OOM ERROR] location: ";
  201. errorStr += location;
  202. std::string message;
  203. message = "is heap out of memory: ";
  204. if (is_heap_oom)
  205. message += "true";
  206. else
  207. message += "false";
  208. errorStr += ", " + message;
  209. SE_LOGE("%s\n", errorStr.c_str());
  210. getInstance()->callExceptionCallback(location, message.c_str(), "(no stack information)");
  211. }
  212. void ScriptEngine::onMessageCallback(v8::Local<v8::Message> message, v8::Local<v8::Value> data)
  213. {
  214. ScriptEngine* thiz = getInstance();
  215. v8::Local<v8::String> msg = message->Get();
  216. Value msgVal;
  217. internal::jsToSeValue(v8::Isolate::GetCurrent(), msg, &msgVal);
  218. assert(msgVal.isString());
  219. v8::ScriptOrigin origin = message->GetScriptOrigin();
  220. Value resouceNameVal;
  221. internal::jsToSeValue(v8::Isolate::GetCurrent(), origin.ResourceName(), &resouceNameVal);
  222. Value line;
  223. internal::jsToSeValue(v8::Isolate::GetCurrent(), origin.ResourceLineOffset(), &line);
  224. Value column;
  225. internal::jsToSeValue(v8::Isolate::GetCurrent(), origin.ResourceColumnOffset(), &column);
  226. std::string location = resouceNameVal.toStringForce() + ":" + line.toStringForce() + ":" + column.toStringForce();
  227. std::string errorStr = msgVal.toString() + ", location: " + location;
  228. std::string stackStr = stackTraceToString(message->GetStackTrace());
  229. if (!stackStr.empty())
  230. {
  231. if (line.toInt32() == 0)
  232. {
  233. location = "(see stack)";
  234. }
  235. errorStr += "\nSTACK:\n" + stackStr;
  236. }
  237. SE_LOGE("ERROR: %s\n", errorStr.c_str());
  238. thiz->callExceptionCallback(location.c_str(), msgVal.toString().c_str(), stackStr.c_str());
  239. if (!thiz->_isErrorHandleWorking)
  240. {
  241. thiz->_isErrorHandleWorking = true;
  242. Value errorHandler;
  243. if (thiz->_globalObj && thiz->_globalObj->getProperty("__errorHandler", &errorHandler) && errorHandler.isObject() && errorHandler.toObject()->isFunction())
  244. {
  245. ValueArray args;
  246. args.push_back(resouceNameVal);
  247. args.push_back(line);
  248. args.push_back(msgVal);
  249. args.push_back(Value(stackStr));
  250. errorHandler.toObject()->call(args, thiz->_globalObj);
  251. }
  252. thiz->_isErrorHandleWorking = false;
  253. }
  254. else
  255. {
  256. SE_LOGE("ERROR: __errorHandler has exception\n");
  257. }
  258. }
  259. void ScriptEngine::onPromiseRejectCallback(v8::PromiseRejectMessage msg)
  260. {
  261. v8::Isolate *isolate = getInstance()->_isolate;
  262. v8::HandleScope scope(isolate);
  263. std::stringstream ss;
  264. auto event = msg.GetEvent();
  265. auto value = msg.GetValue();
  266. const char *eventName = "[invalidatePromiseEvent]";
  267. if(event == v8::kPromiseRejectWithNoHandler) {
  268. eventName = "unhandledRejectedPromise";
  269. }else if(event == v8::kPromiseHandlerAddedAfterReject) {
  270. eventName = "handlerAddedAfterPromiseRejected";
  271. }else if(event == v8::kPromiseRejectAfterResolved) {
  272. eventName = "rejectAfterPromiseResolved";
  273. }else if( event == v8::kPromiseResolveAfterResolved) {
  274. eventName = "resolveAfterPromiseResolved";
  275. }
  276. if(!value.IsEmpty()) {
  277. // prepend error object to stack message
  278. v8::Local<v8::String> str = value->ToString(isolate->GetCurrentContext()).ToLocalChecked();
  279. v8::String::Utf8Value valueUtf8(isolate, str);
  280. ss << *valueUtf8 << std::endl;
  281. }
  282. auto stackStr = getInstance()->getCurrentStackTrace();
  283. ss << "stacktrace: " << std::endl;
  284. ss << stackStr << std::endl;
  285. getInstance()->callExceptionCallback("", eventName, ss.str().c_str());
  286. }
  287. void ScriptEngine::privateDataFinalize(void* nativeObj)
  288. {
  289. internal::PrivateData* p = (internal::PrivateData*)nativeObj;
  290. Object::nativeObjectFinalizeHook(p->data);
  291. assert(p->seObj->getRefCount() == 1);
  292. p->seObj->decRef();
  293. free(p);
  294. }
  295. ScriptEngine *ScriptEngine::getInstance()
  296. {
  297. if (__instance == nullptr)
  298. {
  299. __instance = new ScriptEngine();
  300. }
  301. return __instance;
  302. }
  303. void ScriptEngine::destroyInstance()
  304. {
  305. delete __instance;
  306. __instance = nullptr;
  307. }
  308. ScriptEngine::ScriptEngine()
  309. : _platform(nullptr)
  310. , _isolate(nullptr)
  311. , _handleScope(nullptr)
  312. , _globalObj(nullptr)
  313. #if SE_ENABLE_INSPECTOR
  314. , _env(nullptr)
  315. , _isolateData(nullptr)
  316. #endif
  317. , _debuggerServerPort(0)
  318. , _vmId(0)
  319. , _isValid(false)
  320. , _isGarbageCollecting(false)
  321. , _isInCleanup(false)
  322. , _isErrorHandleWorking(false)
  323. {
  324. _platform = v8::platform::NewDefaultPlatform().release();
  325. v8::V8::InitializePlatform(_platform);
  326. std::string flags;
  327. //NOTICE: spaces are required between flags
  328. flags.append(" --expose-gc-as=" EXPOSE_GC);
  329. // flags.append(" --trace-gc"); // v8 trace gc
  330. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
  331. flags.append(" --jitless");
  332. #endif
  333. if(!flags.empty())
  334. {
  335. v8::V8::SetFlagsFromString(flags.c_str(), (int)flags.length());
  336. }
  337. bool ok = v8::V8::Initialize();
  338. assert(ok);
  339. }
  340. ScriptEngine::~ScriptEngine()
  341. {
  342. cleanup();
  343. v8::V8::Dispose();
  344. v8::V8::ShutdownPlatform();
  345. delete _platform;
  346. }
  347. bool ScriptEngine::init()
  348. {
  349. cleanup();
  350. SE_LOGD("Initializing V8, version: %s\n", v8::V8::GetVersion());
  351. ++_vmId;
  352. _engineThreadId = std::this_thread::get_id();
  353. for (const auto& hook : _beforeInitHookArray)
  354. {
  355. hook();
  356. }
  357. _beforeInitHookArray.clear();
  358. v8::Isolate::CreateParams create_params;
  359. create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
  360. _isolate = v8::Isolate::New(create_params);
  361. v8::HandleScope hs(_isolate);
  362. _isolate->Enter();
  363. _isolate->SetCaptureStackTraceForUncaughtExceptions(true, __jsbStackFrameLimit, v8::StackTrace::kOverview);
  364. _isolate->SetFatalErrorHandler(onFatalErrorCallback);
  365. _isolate->SetOOMErrorHandler(onOOMErrorCallback);
  366. _isolate->AddMessageListener(onMessageCallback);
  367. _isolate->SetPromiseRejectCallback(onPromiseRejectCallback);
  368. _context.Reset(_isolate, v8::Context::New(_isolate));
  369. _context.Get(_isolate)->Enter();
  370. NativePtrToObjectMap::init();
  371. NonRefNativePtrCreatedByCtorMap::init();
  372. Object::setup();
  373. Class::setIsolate(_isolate);
  374. Object::setIsolate(_isolate);
  375. _globalObj = Object::_createJSObject(nullptr, _context.Get(_isolate)->Global());
  376. _globalObj->root();
  377. _globalObj->setProperty("window", Value(_globalObj));
  378. se::Value consoleVal;
  379. if (_globalObj->getProperty("console", &consoleVal) && consoleVal.isObject())
  380. {
  381. consoleVal.toObject()->getProperty("log", &__oldConsoleLog);
  382. consoleVal.toObject()->defineFunction("log", _SE(JSB_console_log));
  383. consoleVal.toObject()->getProperty("debug", &__oldConsoleDebug);
  384. consoleVal.toObject()->defineFunction("debug", _SE(JSB_console_debug));
  385. consoleVal.toObject()->getProperty("info", &__oldConsoleInfo);
  386. consoleVal.toObject()->defineFunction("info", _SE(JSB_console_info));
  387. consoleVal.toObject()->getProperty("warn", &__oldConsoleWarn);
  388. consoleVal.toObject()->defineFunction("warn", _SE(JSB_console_warn));
  389. consoleVal.toObject()->getProperty("error", &__oldConsoleError);
  390. consoleVal.toObject()->defineFunction("error", _SE(JSB_console_error));
  391. consoleVal.toObject()->getProperty("assert", &__oldConsoleAssert);
  392. consoleVal.toObject()->defineFunction("assert", _SE(JSB_console_assert));
  393. }
  394. _globalObj->setProperty("scriptEngineType", se::Value("V8"));
  395. _globalObj->defineFunction("log", __log);
  396. _globalObj->defineFunction("forceGC", __forceGC);
  397. _globalObj->getProperty(EXPOSE_GC, &_gcFuncValue);
  398. if(_gcFuncValue.isObject() && _gcFuncValue.toObject()->isFunction()) {
  399. _gcFunc = _gcFuncValue.toObject();
  400. } else {
  401. _gcFunc = nullptr;
  402. }
  403. __jsb_CCPrivateData_class = Class::create("__PrivateData", _globalObj, nullptr, nullptr);
  404. __jsb_CCPrivateData_class->defineFinalizeFunction(privateDataFinalize);
  405. __jsb_CCPrivateData_class->setCreateProto(false);
  406. __jsb_CCPrivateData_class->install();
  407. _isValid = true;
  408. for (const auto& hook : _afterInitHookArray)
  409. {
  410. hook();
  411. }
  412. _afterInitHookArray.clear();
  413. return _isValid;
  414. }
  415. void ScriptEngine::cleanup()
  416. {
  417. if (!_isValid)
  418. return;
  419. SE_LOGD("ScriptEngine::cleanup begin ...\n");
  420. _isInCleanup = true;
  421. {
  422. AutoHandleScope hs;
  423. for (const auto& hook : _beforeCleanupHookArray)
  424. {
  425. hook();
  426. }
  427. _beforeCleanupHookArray.clear();
  428. SAFE_DEC_REF(_globalObj);
  429. Object::cleanup();
  430. Class::cleanup();
  431. garbageCollect();
  432. __oldConsoleLog.setUndefined();
  433. __oldConsoleDebug.setUndefined();
  434. __oldConsoleInfo.setUndefined();
  435. __oldConsoleWarn.setUndefined();
  436. __oldConsoleError.setUndefined();
  437. __oldConsoleAssert.setUndefined();
  438. #if SE_ENABLE_INSPECTOR
  439. if (_isolateData != nullptr)
  440. {
  441. node::FreeIsolateData(_isolateData);
  442. _isolateData = nullptr;
  443. }
  444. if (_env != nullptr)
  445. {
  446. _env->inspector_agent()->Stop();
  447. _env->CleanupHandles();
  448. node::FreeEnvironment(_env);
  449. _env = nullptr;
  450. }
  451. #endif
  452. _context.Get(_isolate)->Exit();
  453. _context.Reset();
  454. _isolate->Exit();
  455. }
  456. _isolate->Dispose();
  457. _isolate = nullptr;
  458. _globalObj = nullptr;
  459. _isValid = false;
  460. _registerCallbackArray.clear();
  461. for (const auto& hook : _afterCleanupHookArray)
  462. {
  463. hook();
  464. }
  465. _afterCleanupHookArray.clear();
  466. _isInCleanup = false;
  467. NativePtrToObjectMap::destroy();
  468. NonRefNativePtrCreatedByCtorMap::destroy();
  469. _gcFunc = nullptr;
  470. SE_LOGD("ScriptEngine::cleanup end ...\n");
  471. }
  472. Object* ScriptEngine::getGlobalObject() const
  473. {
  474. return _globalObj;
  475. }
  476. void ScriptEngine::addBeforeInitHook(const std::function<void()>& hook)
  477. {
  478. _beforeInitHookArray.push_back(hook);
  479. }
  480. void ScriptEngine::addAfterInitHook(const std::function<void()>& hook)
  481. {
  482. _afterInitHookArray.push_back(hook);
  483. }
  484. void ScriptEngine::addBeforeCleanupHook(const std::function<void()>& hook)
  485. {
  486. _beforeCleanupHookArray.push_back(hook);
  487. }
  488. void ScriptEngine::addAfterCleanupHook(const std::function<void()>& hook)
  489. {
  490. _afterCleanupHookArray.push_back(hook);
  491. }
  492. void ScriptEngine::addRegisterCallback(RegisterCallback cb)
  493. {
  494. assert(std::find(_registerCallbackArray.begin(), _registerCallbackArray.end(), cb) == _registerCallbackArray.end());
  495. _registerCallbackArray.push_back(cb);
  496. }
  497. bool ScriptEngine::start()
  498. {
  499. if (!init())
  500. return false;
  501. se::AutoHandleScope hs;
  502. // debugger
  503. if (isDebuggerEnabled())
  504. {
  505. #if SE_ENABLE_INSPECTOR
  506. // V8 inspector stuff, most code are taken from NodeJS.
  507. _isolateData = node::CreateIsolateData(_isolate, uv_default_loop());
  508. _env = node::CreateEnvironment(_isolateData, _context.Get(_isolate), 0, nullptr, 0, nullptr);
  509. node::DebugOptions options;
  510. options.set_wait_for_connect(_isWaitForConnect);// the program will be hung up until debug attach if _isWaitForConnect = true
  511. options.set_inspector_enabled(true);
  512. options.set_port((int)_debuggerServerPort);
  513. options.set_host_name(_debuggerServerAddr.c_str());
  514. bool ok = _env->inspector_agent()->Start(_platform, "", options);
  515. assert(ok);
  516. #endif
  517. }
  518. //
  519. bool ok = false;
  520. _startTime = std::chrono::steady_clock::now();
  521. for (auto cb : _registerCallbackArray)
  522. {
  523. ok = cb(_globalObj);
  524. assert(ok);
  525. if (!ok)
  526. break;
  527. }
  528. // After ScriptEngine is started, _registerCallbackArray isn't needed. Therefore, clear it here.
  529. _registerCallbackArray.clear();
  530. return ok;
  531. }
  532. void ScriptEngine::garbageCollect()
  533. {
  534. int objSize = __objectMap ? (int)__objectMap->size() : -1;
  535. SE_LOGD("GC begin ..., (js->native map) size: %d, all objects: %d\n", (int)NativePtrToObjectMap::size(), objSize);
  536. if(_gcFunc == nullptr)
  537. {
  538. const double kLongIdlePauseInSeconds = 1.0;
  539. _isolate->ContextDisposedNotification();
  540. _isolate->IdleNotificationDeadline(_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
  541. // By sending a low memory notifications, we will try hard to collect all
  542. // garbage and will therefore also invoke all weak callbacks of actually
  543. // unreachable persistent handles.
  544. _isolate->LowMemoryNotification();
  545. }
  546. else
  547. {
  548. _gcFunc->call({}, nullptr);
  549. }
  550. objSize = __objectMap ? (int)__objectMap->size() : -1;
  551. SE_LOGD("GC end ..., (js->native map) size: %d, all objects: %d\n", (int)NativePtrToObjectMap::size(), objSize);
  552. }
  553. bool ScriptEngine::isGarbageCollecting()
  554. {
  555. return _isGarbageCollecting;
  556. }
  557. void ScriptEngine::_setGarbageCollecting(bool isGarbageCollecting)
  558. {
  559. _isGarbageCollecting = isGarbageCollecting;
  560. }
  561. bool ScriptEngine::isValid() const
  562. {
  563. return _isValid;
  564. }
  565. bool ScriptEngine::evalString(const char* script, ssize_t length/* = -1 */, Value* ret/* = nullptr */, const char* fileName/* = nullptr */)
  566. {
  567. if(_engineThreadId != std::this_thread::get_id())
  568. {
  569. // `evalString` should run in main thread
  570. assert(false);
  571. return false;
  572. }
  573. assert(script != nullptr);
  574. if (length < 0)
  575. length = strlen(script);
  576. if (fileName == nullptr)
  577. fileName = "(no filename)";
  578. // Fix the source url is too long displayed in Chrome debugger.
  579. std::string sourceUrl = fileName;
  580. static const std::string prefixKey = "/temp/quick-scripts/";
  581. size_t prefixPos = sourceUrl.find(prefixKey);
  582. if (prefixPos != std::string::npos)
  583. {
  584. sourceUrl = sourceUrl.substr(prefixPos + prefixKey.length());
  585. }
  586. // It is needed, or will crash if invoked from non C++ context, such as invoked from objective-c context(for example, handler of UIKit).
  587. v8::HandleScope handle_scope(_isolate);
  588. std::string scriptStr(script, length);
  589. v8::MaybeLocal<v8::String> source = v8::String::NewFromUtf8(_isolate, scriptStr.c_str(), v8::NewStringType::kNormal);
  590. if (source.IsEmpty())
  591. return false;
  592. v8::MaybeLocal<v8::String> originStr = v8::String::NewFromUtf8(_isolate, sourceUrl.c_str(), v8::NewStringType::kNormal);
  593. if (originStr.IsEmpty())
  594. return false;
  595. v8::ScriptOrigin origin(originStr.ToLocalChecked());
  596. v8::MaybeLocal<v8::Script> maybeScript = v8::Script::Compile(_context.Get(_isolate), source.ToLocalChecked(), &origin);
  597. bool success = false;
  598. if (!maybeScript.IsEmpty())
  599. {
  600. v8::TryCatch block(_isolate);
  601. v8::Local<v8::Script> v8Script = maybeScript.ToLocalChecked();
  602. v8::MaybeLocal<v8::Value> maybeResult = v8Script->Run(_context.Get(_isolate));
  603. if (!maybeResult.IsEmpty())
  604. {
  605. v8::Local<v8::Value> result = maybeResult.ToLocalChecked();
  606. if (!result->IsUndefined() && ret != nullptr)
  607. {
  608. internal::jsToSeValue(_isolate, result, ret);
  609. }
  610. success = true;
  611. }
  612. if (block.HasCaught()) {
  613. v8::Local<v8::Message> message = block.Message();
  614. SE_LOGE("ScriptEngine::evalString catch exception:\n");
  615. onMessageCallback(message, v8::Undefined(_isolate));
  616. }
  617. }
  618. if (!success)
  619. {
  620. SE_LOGE("ScriptEngine::evalString script %s, failed!\n", fileName);
  621. }
  622. return success;
  623. }
  624. std::string ScriptEngine::getCurrentStackTrace()
  625. {
  626. if (!_isValid)
  627. return std::string();
  628. v8::HandleScope hs(_isolate);
  629. v8::Local<v8::StackTrace> stack = v8::StackTrace::CurrentStackTrace(_isolate, __jsbStackFrameLimit, v8::StackTrace::kOverview);
  630. return stackTraceToString(stack);
  631. }
  632. void ScriptEngine::setFileOperationDelegate(const FileOperationDelegate& delegate)
  633. {
  634. _fileOperationDelegate = delegate;
  635. }
  636. const ScriptEngine::FileOperationDelegate& ScriptEngine::getFileOperationDelegate() const
  637. {
  638. return _fileOperationDelegate;
  639. }
  640. bool ScriptEngine::runScript(const std::string& path, Value* ret/* = nullptr */)
  641. {
  642. assert(!path.empty());
  643. assert(_fileOperationDelegate.isValid());
  644. std::string scriptBuffer = _fileOperationDelegate.onGetStringFromFile(path);
  645. if (!scriptBuffer.empty())
  646. {
  647. return evalString(scriptBuffer.c_str(), scriptBuffer.length(), ret, path.c_str());
  648. }
  649. SE_LOGE("ScriptEngine::runScript script %s, buffer is empty!\n", path.c_str());
  650. return false;
  651. }
  652. void ScriptEngine::clearException()
  653. {
  654. //IDEA:
  655. }
  656. void ScriptEngine::setExceptionCallback(const ExceptionCallback& cb)
  657. {
  658. _nativeExceptionCallback = cb;
  659. }
  660. void ScriptEngine::setJSExceptionCallback(const ExceptionCallback& cb)
  661. {
  662. _jsExceptionCallback = cb;
  663. }
  664. v8::Local<v8::Context> ScriptEngine::_getContext() const
  665. {
  666. return _context.Get(_isolate);
  667. }
  668. void ScriptEngine::enableDebugger(const std::string& serverAddr, uint32_t port, bool isWait)
  669. {
  670. _debuggerServerAddr = serverAddr;
  671. _debuggerServerPort = port;
  672. _isWaitForConnect = isWait;
  673. }
  674. bool ScriptEngine::isDebuggerEnabled() const
  675. {
  676. return !_debuggerServerAddr.empty() && _debuggerServerPort > 0;
  677. }
  678. void ScriptEngine::mainLoopUpdate()
  679. {
  680. // empty implementation
  681. }
  682. } // namespace se {
  683. #endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8