diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index dc5ef186..1c4686d8 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -12,6 +12,7 @@ on: jobs: coverage-mac-clang: + timeout-minutes: 30 runs-on: macos-latest strategy: fail-fast: false @@ -60,6 +61,7 @@ jobs: parallel: true coverage-finish: + timeout-minutes: 30 needs: coverage-mac-clang runs-on: ubuntu-latest steps: diff --git a/.github/workflows/sync_to_tencent_code.yml b/.github/workflows/sync_to_tencent_code.yml index 45a60ff9..254bdd93 100644 --- a/.github/workflows/sync_to_tencent_code.yml +++ b/.github/workflows/sync_to_tencent_code.yml @@ -6,6 +6,7 @@ on: jobs: sync: + timeout-minutes: 30 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index d29afd40..ee1f70fc 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -12,6 +12,7 @@ on: jobs: mac-clang-run: + timeout-minutes: 30 runs-on: macos-latest strategy: fail-fast: false @@ -49,6 +50,7 @@ jobs: ./UnitTests windows-msvc-run: + timeout-minutes: 30 runs-on: windows-latest strategy: fail-fast: false @@ -98,6 +100,7 @@ jobs: ${{ matrix.build_type }}/UnitTests ubuntu-gcc-build: + timeout-minutes: 30 runs-on: ubuntu-latest strategy: fail-fast: false @@ -133,6 +136,7 @@ jobs: # 1. we don't have android libraries # 2. -undefined dynamic_lookup not work with NDK if: ${{ false }} + timeout-minutes: 30 runs-on: ubuntu-latest strategy: fail-fast: false @@ -167,6 +171,7 @@ jobs: cmake --build . -j $(nproc) --target UnitTests node-gcc-run: + timeout-minutes: 30 runs-on: ubuntu-latest strategy: fail-fast: false @@ -190,6 +195,7 @@ jobs: npm run test webassembly-emscripten-run: + timeout-minutes: 30 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/src/Native.hpp b/src/Native.hpp index 626395a6..48f5b4b2 100644 --- a/src/Native.hpp +++ b/src/Native.hpp @@ -513,14 +513,15 @@ template std::enable_if_t<::script::converter::isConvertible && isArgsConvertible>, std::function> -createFunctionWrapperInner(const Local& function, const std::tuple*) { +createFunctionWrapperInner(const Local& function, const Local& thiz, + const std::tuple*) { using EngineImpl = typename ImplType::type; return std::function( - [f = Global(function), + [func = Global(function), receiver = Global(thiz), engine = EngineScope::currentEngineAs()](Args... args) -> RetType { // use EngineImpl to avoid possible dynamic_cast EngineScope scope(engine); - auto ret = f.get().call({}, args...); + auto ret = func.get().call(receiver.getValue(), args...); if constexpr (!std::is_void_v) { return ::script::converter::Converter::toCpp(ret); } @@ -528,10 +529,11 @@ createFunctionWrapperInner(const Local& function, const std::tuple -inline std::function createFunctionWrapper(const Local& function) { +inline std::function createFunctionWrapper(const Local& function, + const Local& thiz) { using FC = traits::FunctionTrait; return createFunctionWrapperInner( - function, static_cast(nullptr)); + function, thiz, static_cast(nullptr)); } } // namespace script::internal @@ -585,8 +587,8 @@ Local::call(const Local& thiz, T&&... args) const { } template -std::function Local::wrapper() const { - return internal::createFunctionWrapper(*this); +std::function Local::wrapper(const Local& thiz) const { + return internal::createFunctionWrapper(*this, thiz); } template diff --git a/src/Reference.h b/src/Reference.h index f9823a89..a4303bc2 100644 --- a/src/Reference.h +++ b/src/Reference.h @@ -477,6 +477,9 @@ class Local { template Local call(const Local& thiz, T&&... args) const; + /** + * helper function to call with null thiz(receiver) and no arguments. + */ Local call() const { return call({}); } /** @@ -490,12 +493,15 @@ class Local { * \endcode * * @tparam FuncType function signature, like "int(int, int)" or "std::string(const char*, int)" - * @param function a std::function - * @note the returned std::function holds a Global to this reference, and will be auto - * released when destroy engine, after that, the returned std::function is not valid anymore. + * @return a std::function + * @param thiz the receiver of the function, default to null + * @note the returned std::function holds a Global to the function and receiver + * reference, and will be auto released when destroy engine, after that, the returned + * std::function is not valid anymore. */ + // implemented in Native.hpp template - std::function wrapper() const; // implemented in Native.hpp + std::function wrapper(const Local& thiz = {}) const; SPECIALIZE_NON_VALUE(Function) diff --git a/test/src/NativeTest.cc b/test/src/NativeTest.cc index e5d1e2f6..709382f3 100644 --- a/test/src/NativeTest.cc +++ b/test/src/NativeTest.cc @@ -1143,6 +1143,32 @@ TEST_F(NativeTest, FunctionWrapper) { EXPECT_EQ(add(1, 1), 2) << "Out of EngineScope test"; } +TEST_F(NativeTest, FunctionWrapperReceiver) { + EngineScope scope(engine); + try { + auto func = + engine + ->eval( + TS().js("(function () { if (this && this.num) return this.num; else return -1 ;})") + .lua("return function (self) if self ~= nil then return self.num else return " + "-1 end end") + .select()) + .asFunction(); + + auto receiver = + engine->eval(TS().js("({ num: 42})").lua("num = {}; num.num = 42; return num;").select()) + .asObject(); + + auto withReceiver = func.wrapper(receiver); + EXPECT_EQ(withReceiver(), 42); + + auto noReceiver = func.wrapper(); + EXPECT_EQ(noReceiver(), -1); + } catch (const Exception& e) { + FAIL() << e; + } +} + TEST_F(NativeTest, ValidateClassDefine) { // static & instance are empty EXPECT_THROW({ defineClass("hello").build(); }, std::runtime_error);