Skip to content
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
2 changes: 2 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:

jobs:
coverage-mac-clang:
timeout-minutes: 30
runs-on: macos-latest
strategy:
fail-fast: false
Expand Down Expand Up @@ -60,6 +61,7 @@ jobs:
parallel: true

coverage-finish:
timeout-minutes: 30
needs: coverage-mac-clang
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/sync_to_tencent_code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:

jobs:
sync:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:

jobs:
mac-clang-run:
timeout-minutes: 30
runs-on: macos-latest
strategy:
fail-fast: false
Expand Down Expand Up @@ -49,6 +50,7 @@ jobs:
./UnitTests

windows-msvc-run:
timeout-minutes: 30
runs-on: windows-latest
strategy:
fail-fast: false
Expand Down Expand Up @@ -98,6 +100,7 @@ jobs:
${{ matrix.build_type }}/UnitTests

ubuntu-gcc-build:
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -190,6 +195,7 @@ jobs:
npm run test

webassembly-emscripten-run:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand Down
16 changes: 9 additions & 7 deletions src/Native.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,25 +513,27 @@ template <typename RetType, typename... Args>
std::enable_if_t<::script::converter::isConvertible<RetType> &&
isArgsConvertible<std::tuple<Args...>>,
std::function<RetType(Args...)>>
createFunctionWrapperInner(const Local<Function>& function, const std::tuple<Args...>*) {
createFunctionWrapperInner(const Local<Function>& function, const Local<Value>& thiz,
const std::tuple<Args...>*) {
using EngineImpl = typename ImplType<ScriptEngine>::type;
return std::function(
[f = Global<Function>(function),
[func = Global<Function>(function), receiver = Global<Value>(thiz),
engine = EngineScope::currentEngineAs<EngineImpl>()](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<RetType>) {
return ::script::converter::Converter<RetType>::toCpp(ret);
}
});
}

template <typename FuncType>
inline std::function<FuncType> createFunctionWrapper(const Local<Function>& function) {
inline std::function<FuncType> createFunctionWrapper(const Local<Function>& function,
const Local<Value>& thiz) {
using FC = traits::FunctionTrait<FuncType>;
return createFunctionWrapperInner<typename FC::ReturnType>(
function, static_cast<typename FC::Arguments*>(nullptr));
function, thiz, static_cast<typename FC::Arguments*>(nullptr));
}

} // namespace script::internal
Expand Down Expand Up @@ -585,8 +587,8 @@ Local<Function>::call(const Local<Value>& thiz, T&&... args) const {
}

template <typename FuncType>
std::function<FuncType> Local<Function>::wrapper() const {
return internal::createFunctionWrapper<FuncType>(*this);
std::function<FuncType> Local<Function>::wrapper(const Local<Value>& thiz) const {
return internal::createFunctionWrapper<FuncType>(*this, thiz);
}

template <typename... T>
Expand Down
14 changes: 10 additions & 4 deletions src/Reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ class Local<Function> {
template <typename... T>
Local<Value> call(const Local<Value>& thiz, T&&... args) const;

/**
* helper function to call with null thiz(receiver) and no arguments.
*/
Local<Value> call() const { return call({}); }

/**
Expand All @@ -490,12 +493,15 @@ class Local<Function> {
* \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<Function> 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<Function> 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 <typename FuncType>
std::function<FuncType> wrapper() const; // implemented in Native.hpp
std::function<FuncType> wrapper(const Local<Value>& thiz = {}) const;

SPECIALIZE_NON_VALUE(Function)

Expand Down
26 changes: 26 additions & 0 deletions test/src/NativeTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<int()>(receiver);
EXPECT_EQ(withReceiver(), 42);

auto noReceiver = func.wrapper<int()>();
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);
Expand Down