1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef BERBERIS_GUEST_ABI_GUEST_PARAMS_H_
18 #define BERBERIS_GUEST_ABI_GUEST_PARAMS_H_
19 
20 #include "berberis/guest_abi/guest_abi.h"
21 #include "berberis/guest_abi/guest_params_arch.h"
22 
23 namespace berberis {
24 
25 // Structured binding rules are designed for access to the insides of a structured type.
26 // Specifically:
27 //   auto [x,y] = structured_var;   // Makes copy of structured_var.
28 //   auto& [x,y] = structured_var;  // Doesn't make copy of a structures var.
29 // Note that x and y themselves are always references -- either to the copy or the original.
30 // Access modifiers are applied to the declaration of hidden, invisible variable.
31 //
32 // This works well when structured_var has some "insides" which may be copied.
33 //
34 // But in our case all these types are "lightweight adapters" used to parse ThreadState.
35 // There are no difference which copy you use -- all of the calls to Params() or Return() would
36 // return references to original ThreadState.
37 //
38 // However, it's allowed to return regular variable, not reference, from "get<>" function.
39 // In that case variables x and y (in the example above) would become rvalue references which
40 // would point to copy of the appropriate value.
41 //
42 // This allows us to make accessors XxxValues and XxxReferences which either allow one to access
43 // copies of Values stored in the ThreadState or access contents of ThreadState directly.
44 
45 // GuestParamsValues is a syntax sugar for use with structured binding declaration.
46 // Usage looks like this:
47 //   auto [length, angle] = GuestParamsValues<double(int, double)>(state);
48 //   if (length > 100) {
49 //     length = 100;
50 //   }
51 //
52 // Note: variables are copies here not because "auto [x,y,z] =" construct is used but
53 // because GuestParamsValues always returns values. See above.
54 
55 template <typename FunctionType, GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
56 class GuestParamsValues : public GuestParamsAndReturn<FunctionType, kCallingConventionsVariant> {
57  public:
GuestParamsValues(ThreadState * state)58   GuestParamsValues(ThreadState* state)
59       : GuestParamsAndReturn<FunctionType, kCallingConventionsVariant>(state) {}
60 
61   // Adapter for structural bindings.
62   template <std::size_t index>
get()63   auto get() const {
64     return *this->template Params<index>();
65   }
66 };
67 
68 // GuestReturnReference is a syntax sugar for use with structured binding declaration.
69 // Usage looks like this:
70 //   auto&& [ret] = GuestReturnReference<double(int, double)>(state);
71 //   ret = 5.0;
72 //
73 // Note: variable is a reference here not because "auto&& [x,y,z] =" construct is used but
74 // because GuestReturnReference always returns references. See above.
75 
76 template <typename FunctionType,
77           GuestAbi::CallingConventionsVariant kCallingConventionsVariant = GuestAbi::kDefaultAbi>
78 class GuestReturnReference : public GuestParamsAndReturn<FunctionType, kCallingConventionsVariant> {
79  public:
GuestReturnReference(ThreadState * state)80   GuestReturnReference(ThreadState* state)
81       : GuestParamsAndReturn<FunctionType, kCallingConventionsVariant>(state) {}
82 
83   // Adapter for structural bindings.  Note: we return normal references here, not rvalue references
84   // since we are pointing to non-temporary object and don't want to see it moved.
85   template <std::size_t index>
get()86   auto& get() const {
87     static_assert(index == 0);
88     return *this->Return();
89   }
90 };
91 
92 }  // namespace berberis
93 
94 namespace std {
95 
96 // Partial specializations to support structural bindings for templates
97 // GuestParamsValues/GuestReturnReference.
98 
99 // tuple_size for GuestParamsValues<Function>
100 template <typename ReturnType,
101           typename... ParamType,
102           bool kNoexcept,
103           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
104 class tuple_size<berberis::GuestParamsValues<ReturnType(ParamType...) noexcept(kNoexcept),
105                                              kCallingConventionsVariant>>
106     : public std::integral_constant<std::size_t, sizeof...(ParamType)> {};
107 
108 // tuple_size for GuestParamsValues<PointerToFunction>
109 template <typename ReturnType,
110           typename... ParamType,
111           bool kNoexcept,
112           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
113 class tuple_size<berberis::GuestParamsValues<ReturnType (*)(ParamType...) noexcept(kNoexcept),
114                                              kCallingConventionsVariant>>
115     : public std::integral_constant<std::size_t, sizeof...(ParamType)> {};
116 
117 template <typename ReturnType,
118           typename... ParamType,
119           bool kNoexcept,
120           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
121 class tuple_size<berberis::GuestParamsValues<ReturnType(ParamType..., ...) noexcept(kNoexcept),
122                                              kCallingConventionsVariant>>
123     : public std::integral_constant<std::size_t, sizeof...(ParamType)> {};
124 
125 template <typename ReturnType,
126           typename... ParamType,
127           bool kNoexcept,
128           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
129 class tuple_size<berberis::GuestParamsValues<ReturnType (*)(ParamType..., ...) noexcept(kNoexcept),
130                                              kCallingConventionsVariant>>
131     : public std::integral_constant<std::size_t, sizeof...(ParamType)> {};
132 
133 template <std::size_t index,
134           typename FunctionType,
135           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
136 class tuple_element<index, berberis::GuestParamsValues<FunctionType, kCallingConventionsVariant>> {
137  public:
138   using type = std::invoke_result_t<
139       decltype(&berberis::GuestParamsValues<FunctionType,
140                                             kCallingConventionsVariant>::template get<index>),
141       berberis::GuestParamsValues<FunctionType, kCallingConventionsVariant>>;
142 };
143 
144 // tuple_size for GuestReturnReference<Function>
145 template <typename ReturnType,
146           typename... ParamType,
147           bool kNoexcept,
148           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
149 class tuple_size<berberis::GuestReturnReference<ReturnType(ParamType...) noexcept(kNoexcept),
150                                                 kCallingConventionsVariant>>
151     : public std::integral_constant<std::size_t, std::is_same_v<ReturnType, void> ? 0 : 1> {};
152 
153 // tuple_size for GuestReturnReference<PointerToFunction>
154 template <typename ReturnType,
155           typename... ParamType,
156           bool kNoexcept,
157           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
158 class tuple_size<berberis::GuestReturnReference<ReturnType (*)(ParamType...) noexcept(kNoexcept),
159                                                 kCallingConventionsVariant>>
160     : public std::integral_constant<std::size_t, std::is_same_v<ReturnType, void> ? 0 : 1> {};
161 
162 template <typename ReturnType,
163           typename... ParamType,
164           bool kNoexcept,
165           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
166 class tuple_size<berberis::GuestReturnReference<ReturnType(ParamType..., ...) noexcept(kNoexcept),
167                                                 kCallingConventionsVariant>>
168     : public std::integral_constant<std::size_t, std::is_same_v<ReturnType, void> ? 0 : 1> {};
169 
170 template <typename ReturnType,
171           typename... ParamType,
172           bool kNoexcept,
173           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
174 class tuple_size<
175     berberis::GuestReturnReference<ReturnType (*)(ParamType..., ...) noexcept(kNoexcept),
176                                    kCallingConventionsVariant>>
177     : public std::integral_constant<std::size_t, std::is_same_v<ReturnType, void> ? 0 : 1> {};
178 
179 template <typename FunctionType,
180           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
181 class tuple_element<0, berberis::GuestReturnReference<FunctionType, kCallingConventionsVariant>> {
182  public:
183   using type = std::invoke_result_t<
184       decltype(&berberis::GuestReturnReference<FunctionType,
185                                                kCallingConventionsVariant>::template get<0>),
186       berberis::GuestReturnReference<FunctionType, kCallingConventionsVariant>>;
187 };
188 
189 }  // namespace std
190 
191 #endif  // BERBERIS_GUEST_ABI_GUEST_PARAMS_H_
192