1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef OHOS_FFI_CJ_LAMBDA_H
17#define OHOS_FFI_CJ_LAMBDA_H
18
19#include <cstdint>
20#include <tuple>
21
22#include "ffi_remote_data.h"
23
24#ifndef FFI_EXPORT
25#ifndef WINDOWS_PLATFORM
26#define FFI_EXPORT __attribute__((visibility("default")))
27#else
28#define FFI_EXPORT __declspec(dllexport)
29#endif
30#endif
31
32class FFI_EXPORT CJLambda {
33    template <size_t I>
34    struct TupleRuntimeHelper {
35        template <typename T>
36        static void* GetElementAddr(T& tup, size_t idx)
37        {
38            if (idx == I - 1) {
39                return &(std::get<I - 1>(tup));
40            } else {
41                return TupleRuntimeHelper<I - 1>::GetElementAddr(tup, idx);
42            }
43        }
44    };
45
46    template <>
47    struct TupleRuntimeHelper<0> {
48        template <typename T>
49        static void* GetElementAddr(T& tup, size_t idx)
50        {
51            // Unreachable
52            return nullptr;
53        }
54    };
55
56    template <typename... Types>
57    static inline void* GetElementAddr(std::tuple<Types...> const& tup, size_t idx)
58    {
59        return TupleRuntimeHelper<sizeof...(Types)>::GetElementAddr(tup, idx);
60    }
61
62    template <typename... Types>
63    static inline void* GetElementAddr(std::tuple<Types...>& tup, size_t idx)
64    {
65        return TupleRuntimeHelper<sizeof...(Types)>::GetElementAddr(tup, idx);
66    }
67public:
68    template<class... I>
69    static std::function<void(I...)> Create(void (*callback)(I...))
70    {
71        auto handle = OHOS::FFI::RemoteData::Create<OHOS::FFI::CJLambdaRemoteData>(reinterpret_cast<int64_t>(callback));
72        return [handle](I...args) -> void {
73            constexpr int32_t argc = std::tuple_size_v<std::tuple<I...>>;
74            if (argc == 0) {
75                InvokeLambda(handle->GetID(), argc, nullptr, nullptr);
76                return;
77            }
78            auto argsTuple = std::make_tuple(args...);
79            void* argv[argc];
80            for (size_t i = 0; i < argc; ++i) {
81                argv[i] = GetElementAddr(argsTuple, i);
82            }
83            InvokeLambda(handle->GetID(), argc, argv, nullptr);
84        };
85    }
86
87    template<class... I, class R>
88    static std::function<R(I...)> Create(R (*callback)(I...))
89    {
90        auto handle = OHOS::FFI::RemoteData::Create<OHOS::FFI::CJLambdaRemoteData>(reinterpret_cast<int64_t>(callback));
91        return [handle](I...args) -> R {
92            R res;
93            constexpr int32_t argc = std::tuple_size_v<std::tuple<I...>>;
94            if (argc == 0) {
95                InvokeLambda(handle->GetID(), argc, nullptr, &res);
96                return res;
97            }
98            auto argsTuple = std::make_tuple(args...);
99            void* argv[argc];
100            for (size_t i = 0; i < argc; ++i) {
101                argv[i] = GetElementAddr(argsTuple, i);
102            }
103            InvokeLambda(handle->GetID(), argc, argv, &res);
104            return res;
105        };
106    }
107
108private:
109    static inline void InvokeLambda(int64_t lambdaId, int32_t argc, void** argv, void* result)
110    {
111        auto invoker = CJFFIFnInvoker::GetInstance()->GetCJFuncs().atCOHOSFFICallbackInvoker;
112        invoker(lambdaId, argc, argv, result);
113    }
114};
115
116#endif // OHOS_FFI_CJ_LAMBDA_H
117