diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 8174410d..ed38235f 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -212,10 +212,10 @@ jobs: build/ScriptXTestLibs build/googletest-src - name: Setup Emscripten - uses: mymindstorm/setup-emsdk@v8 + uses: mymindstorm/setup-emsdk@v11 with: - version: '2.0.5' - actions-cache-folder: 'emsdk-cache' + version: '3.0.0' + actions-cache-folder: 'emsdk-cache-3.0.0' - name: Setup Node.js uses: actions/setup-node@v1 with: @@ -236,4 +236,4 @@ jobs: run: | cd build # exclude failed tests - node UnitTests.js '--gtest_filter=-ThreadPool.*:EngineScopeTest.ExitEngine:EngineScopeTest.TwoThreads:EngineScopeTest.ThreadLocal:MessageQueue.Interrupt:MessageQueue.Shutdown:MessageQueue.ShutdownNow:MessageQueue.FullAndPostInsideLoopQueue:ReferenceTest.WeakGc:ReferenceTest.WeakGc:ReferenceTest.GlobalNotClear:ReferenceTest.GlobalOnEngineDestroy:ReferenceTest.WeakOnEngineDestroy:ReferenceTest.WeakNotClrear:ManagedObjectTest.EngineDispose:ManagedObjectTest.FunctionCallback:PressureTest.All:EngineTest.JsPromiseTest' + node UnitTests.js '--gtest_filter=-ThreadPool.*:EngineScopeTest.ExitEngine:EngineScopeTest.TwoThreads:EngineScopeTest.ThreadLocal:MessageQueue.Interrupt:MessageQueue.Shutdown:MessageQueue.ShutdownNow:MessageQueue.FullAndPostInsideLoopQueue:ReferenceTest.WeakGc:ReferenceTest.WeakGc:ReferenceTest.GlobalNotClear:ReferenceTest.GlobalOnEngineDestroy:ReferenceTest.WeakOnEngineDestroy:ReferenceTest.WeakNotClrear:ManagedObjectTest.EngineDispose:ManagedObjectTest.FunctionCallback:PressureTest.All:EngineTest.JsPromiseTest:ShowCaseTest.SetTimeout' diff --git a/CHANGELOG.md b/CHANGELOG.md index 579cf043..551df6c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ --- +Version 3.2.0 (2021-12): +1. fix QuickJs memory leak, engine instance not deleted on destroy +2. fix QuickJs missing EngineScope when run micro task (Promise) Version 3.1.0 (2021-04): 1. add QuickJs backend diff --git a/VERSION b/VERSION index a0cd9f0c..a4f52a5d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.1.0 \ No newline at end of file +3.2.0 \ No newline at end of file diff --git a/backend/WebAssembly/WasmEngine.cc b/backend/WebAssembly/WasmEngine.cc index c88ffd9a..c52d446b 100644 --- a/backend/WebAssembly/WasmEngine.cc +++ b/backend/WebAssembly/WasmEngine.cc @@ -26,8 +26,6 @@ namespace script::wasm_backend { -std::thread::id WasmEngine::engineThreadId_{}; - WasmEngine::WasmEngine() { engineThreadId_ = std::this_thread::get_id(); } WasmEngine* WasmEngine::instance() { @@ -49,7 +47,7 @@ void WasmEngine::destroy() { bool WasmEngine::isDestroying() const { return false; } -void WasmEngine::unitTestResetRetistry() { +void WasmEngine::unitTestResetRegistry() { classDefineRegistry_.clear(); ScriptEngine::classDefineRegistry_.clear(); } diff --git a/backend/WebAssembly/WasmEngine.h b/backend/WebAssembly/WasmEngine.h index 0d0ec207..b8d629f2 100644 --- a/backend/WebAssembly/WasmEngine.h +++ b/backend/WebAssembly/WasmEngine.h @@ -39,12 +39,12 @@ class WasmEngine : public ScriptEngine { std::shared_ptr messageQueue_ = std::make_shared(); bool ignoreDestroyCall_ = false; - static std::thread::id engineThreadId_; + std::thread::id engineThreadId_ = {}; WasmEngine(); // for unit-test only - void unitTestResetRetistry(); + void unitTestResetRegistry(); public: static WasmEngine* instance(); diff --git a/docs/en/WebAssembly.md b/docs/en/WebAssembly.md index 13de1080..d020afb8 100644 --- a/docs/en/WebAssembly.md +++ b/docs/en/WebAssembly.md @@ -71,7 +71,7 @@ The memory can only be managed manually. There are two main aspects related to S In V8 and JSCore, depending on the finalize callback provided by the engine, the automatic release of the bound class is realized, but in WASM it is not possible, and the user can only release it by himself. ScriptX provides auxiliary methods in the JS global scope. -Give a chestnut: +For example: ```C++ static ClassDefine test = diff --git a/docs/zh/WebAssembly.md b/docs/zh/WebAssembly.md index b41debbe..7bc896f8 100644 --- a/docs/zh/WebAssembly.md +++ b/docs/zh/WebAssembly.md @@ -68,7 +68,7 @@ Wasm没有GC,JS也没有finalize回调。。。所以就很坑爹 2. 绑定类的内存释放 ### 绑定类的内存释放 -在V8和JSCore中,依赖引擎提供的finalize回调,实现了绑定类的自动释放,但是在WASM中就搞不定了,使用者只能自己释放只。ScriptX在JS全局提供了辅助方法。 +在V8和JSCore中,依赖引擎提供的finalize回调,实现了绑定类的自动释放,但是在WASM中就搞不定了,使用者只能自己释放之。ScriptX在JS全局提供了辅助方法。 举个栗子: diff --git a/src/Utils.cc b/src/Utils.cc index aeb74055..d075f7e9 100644 --- a/src/Utils.cc +++ b/src/Utils.cc @@ -25,7 +25,7 @@ Tracer::Delegate* Tracer::delegate_; void Tracer::setDelegate(Tracer::Delegate* d) { delegate_ = d; // use atomic to insert a memory barrier here. - std::atomic_int().store(0, std::memory_order_release); + std::atomic_thread_fence(std::memory_order_release); } Tracer::Tracer(ScriptEngine* engine, const char* traceName) noexcept : engine_(engine) { diff --git a/src/utils/MessageQueue.h b/src/utils/MessageQueue.h index 7f6adc69..73e823f9 100644 --- a/src/utils/MessageQueue.h +++ b/src/utils/MessageQueue.h @@ -50,6 +50,8 @@ struct alignas(std::max_align_t) ArbitraryData { ArbitraryData& operator=(const ArbitraryData&); + SCRIPTX_DISALLOW_MOVE(ArbitraryData); + ~ArbitraryData() = default; }; diff --git a/src/version.h b/src/version.h index f4b6553c..ed455cb9 100644 --- a/src/version.h +++ b/src/version.h @@ -19,9 +19,9 @@ // ScriptX version config files // auto generated from the file VERSION -#define SCRIPTX_VERSION_STRING "3.1.0" +#define SCRIPTX_VERSION_STRING "3.2.0" #define SCRIPTX_VERSION_MAJOR 3 -#define SCRIPTX_VERSION_MINOR 1 +#define SCRIPTX_VERSION_MINOR 2 #define SCRIPTX_VERSION_PATCH 0 namespace script { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 694ce856..e277e827 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,8 +13,6 @@ add_executable(UnitTests) include(cmake/TestEnv.cmake) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - string(APPEND CMAKE_CXX_FLAGS_RELEASE " -O3 -flto") - if (TEST_FLAG_ENABLE_ASAN) # macOS doesn't support -fsanitize=memory string(APPEND UNIT_TEST_ASAN_FLAGS @@ -39,10 +37,10 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU # devops support gcov only if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # for clang - string(APPEND CMAKE_CXX_FLAGS " --coverage -g3") + string(APPEND CMAKE_CXX_FLAGS " --coverage") else () # for gcc - string(APPEND CMAKE_CXX_FLAGS " -fprofile-arcs -ftest-coverage -g3") + string(APPEND CMAKE_CXX_FLAGS " -fprofile-arcs -ftest-coverage") endif() endif () endif () @@ -73,7 +71,9 @@ target_sources(UnitTests PRIVATE src/InteroperateTest.cc src/ExceptionTest.cc src/PressureTest.cc - src/EngineTest.cc) + src/EngineTest.cc + src/ShowCaseTest.cc + ) ######## ScriptX config ########## diff --git a/test/src/EngineTest.cc b/test/src/EngineTest.cc index 8d409361..ed73711d 100644 --- a/test/src/EngineTest.cc +++ b/test/src/EngineTest.cc @@ -143,12 +143,11 @@ TEST_F(EngineTest, JsPromiseTest) { engine->eval( u8R"( const promise = new Promise((resolve, reject) => { - resolve('Ok'); + resolve(1); }); - promise.then(x => { - console.log(x); - setValue(1); + promise.then(num => { + setValue(num); }); )"); diff --git a/test/src/ShowCaseTest.cc b/test/src/ShowCaseTest.cc new file mode 100644 index 00000000..b122fb83 --- /dev/null +++ b/test/src/ShowCaseTest.cc @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test.h" + +namespace script::test { + +DEFINE_ENGINE_TEST(ShowCaseTest); + +/** + * show how to implement a `setTimeout(function, delayMs)` + */ +namespace { + +void defineSetTimeout(ScriptEngine* engine) { + EngineScope scope(engine); + auto setTimeOut = Function::newFunction([](const Arguments& args) -> Local { + // make a global ref, we are going to pass it beyond stack-frame lifecycle + auto callback = Global(args[0].asFunction()); + auto delayMs = args[1].asNumber().toInt64(); + auto engine = args.engine(); + + auto runner = [fun = std::move(callback), engine]() { + // make a scope + EngineScope scope(engine); + try { + // run the callback function + fun.get().call(); + } catch (const Exception& e) { + Logger() << "error in setTimeout:" << e; + } + }; + using RunnerType = decltype(runner); + + auto&& queue = args.engine()->messageQueue(); + auto msg = queue->obtainInplaceMessage( + [](utils::InplaceMessage& msg) { msg.getObject()(); }); + msg->inplaceObject(std::move(runner)); + queue->postMessage(msg, std::chrono::milliseconds(delayMs)); + + return {}; + }); + + engine->set("test_setTimeout", setTimeOut); +} + +} // namespace + +TEST_F(ShowCaseTest, SetTimeout) { + defineSetTimeout(engine); + { + EngineScope scope(engine); + int mark = 0; + + engine->set("setMark", Function::newFunction([&mark](int v) { mark = v; })); + + engine->eval(TS().js(u8R"( + test_setTimeout(() => { + setMark(1); + test_setTimeout(() => setMark(2), 0); + }, 0); + )") + .lua(u8R"( + test_setTimeout( + function() + setMark(1); + test_setTimeout(function() setMark(2) end, 0); + end, + 0); + )") + .select()); + auto&& queue = engine->messageQueue(); + ASSERT_EQ(mark, 0); + queue->loopQueue(utils::MessageQueue::LoopType::kLoopOnce); + ASSERT_EQ(mark, 1); + queue->loopQueue(utils::MessageQueue::LoopType::kLoopOnce); + ASSERT_EQ(mark, 2); + } +} + +} // namespace script::test diff --git a/test/src/gtest_main.cc b/test/src/gtest_main.cc index ec87ca64..e9a0258b 100644 --- a/test/src/gtest_main.cc +++ b/test/src/gtest_main.cc @@ -53,7 +53,7 @@ void ScriptXTestFixture::SetUp() { #ifdef SCRIPTX_BACKEND_WEBASSEMBLY auto eng = script::ScriptEngineImpl::instance(); script::ScriptEngineImpl::ignoreDestroyCall(); - eng->unitTestResetRetistry(); + eng->unitTestResetRegistry(); engine = eng; #else engine = new script::ScriptEngineImpl();