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_ARGUMENTS_H_
18 #define BERBERIS_GUEST_ABI_GUEST_ARGUMENTS_H_
19 
20 #include "berberis/guest_abi/guest_abi.h"  // IWYU pragma: export.
21 #include "berberis/guest_abi/guest_arguments_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 aplied 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 GuestArgumentBuffer.
35 // There are no difference which copy you use -- all of the calls to XxxArgument() or XxxResult()
36 // would return references to original GuestArgumentBuffer.
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 accessorts XxxValues and XxxReferences which either allow one to access
43 // copies of Values stored in the GuestArgumentBuffer or access contents of GuestArgumentBuffer
44 // directly.
45 
46 // GuestArgumentsReferences is a syntax sugar for use with structured binding declaration.
47 // Usage looks like this:
48 //   auto&& [length, angle] = GuestArgumentsReferences<double(int, double)>(buf);
49 //   if (length > 100) {
50 //     length = 100;
51 //   }
52 //
53 // Note: variables are references here not because "auto&& [x,y,z] =" construct is used but
54 // because GuestArgumentsReferences always returns references. See above.
55 
56 template <typename FunctionType,
57           GuestAbi::CallingConventionsVariant kCallingConventionsVariant = GuestAbi::kDefaultAbi>
58 class GuestArgumentsReferences : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant> {
59  public:
GuestArgumentsReferences(GuestArgumentBuffer * buffer)60   GuestArgumentsReferences(GuestArgumentBuffer* buffer)
61       : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant>(buffer) {}
62 
63   // Adapter for structural bindings.  Note: we return normal references here, not rvalue references
64   // since we are pointing to non-temporary object and don't want to see it moved.
65   template <std::size_t index>
get()66   auto& get() const {
67     return this->template GuestArgument<index>();
68   }
69 };
70 
71 // HostArgumentsValue is a syntax sugar for use with structured binding declaration.
72 // Usage looks like this:
73 //   auto [length, angle] = HostArgumentsValues<double(int, double)>(buf);
74 //   if (length > 100) {
75 //     length = 100;
76 //   }
77 //
78 // Note: variables are copies here not because "auto [x,y,z] =" construct is used but
79 // because HostArgumentsValues always returns values. See above.
80 
81 template <typename FunctionType,
82           GuestAbi::CallingConventionsVariant kCallingConventionsVariant = GuestAbi::kDefaultAbi>
83 class HostArgumentsValues : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant> {
84  public:
HostArgumentsValues(GuestArgumentBuffer * buffer)85   HostArgumentsValues(GuestArgumentBuffer* buffer)
86       : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant>(buffer) {}
87 
88   // Adapter for structural bindings.
89   template <std::size_t index>
get()90   auto get() const {
91     return this->template HostArgument<index>();
92   }
93 };
94 
95 // GuestResultValue is a syntax sugar for use with structured binding declaration.
96 // Usage looks like this:
97 //   auto [result] = GuestResultValue<double(int, double)>(buf);
98 //   if (result == 5.0) {
99 //     ...
100 //   }
101 //
102 // Note: the variable is a copy here not because "auto [x,y,z] =" construct is used but
103 // because GuestResultValue always returns values. See above.
104 
105 template <typename FunctionType,
106           GuestAbi::CallingConventionsVariant kCallingConventionsVariant = GuestAbi::kDefaultAbi>
107 class GuestResultValue : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant> {
108  public:
GuestResultValue(GuestArgumentBuffer * buffer)109   GuestResultValue(GuestArgumentBuffer* buffer)
110       : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant>(buffer) {}
111 
112   // Adapter for structural bindings.
113   template <std::size_t index>
get()114   auto get() const {
115     static_assert(index == 0);
116     return this->GuestResult();
117   }
118 };
119 
120 // HostResultReference is a syntax sugar for use with structured binding declaration.
121 // Usage looks like this:
122 //   auto&& [result] = HostResultReference<double(int, double)>(buf);
123 //   result = 5.0;
124 //
125 // Note: variable is a reference here not because "auto&& [x,y,z] =" construct is used but
126 // because GuestArgumentsReferences always returns references. See above.
127 
128 template <typename FunctionType,
129           GuestAbi::CallingConventionsVariant kCallingConventionsVariant = GuestAbi::kDefaultAbi>
130 class HostResultReference : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant> {
131  public:
HostResultReference(GuestArgumentBuffer * buffer)132   HostResultReference(GuestArgumentBuffer* buffer)
133       : GuestArgumentsAndResult<FunctionType, kCallingConventionsVariant>(buffer) {}
134 
135   // Adapter for structural bindings.  Note: we return normal references here, not rvalue references
136   // since we are pointing to non-temporary object and don't want to see it moved.
137   template <std::size_t index>
get()138   auto& get() const {
139     static_assert(index == 0);
140     return this->HostResult();
141   }
142 };
143 
144 }  // namespace berberis
145 
146 namespace std {
147 
148 // Partial specializations to support structural bindings for templates
149 // GuestArgumentsReferences/GuestResultValue.
150 
151 // tuple_size for GuestArgumentsReferences<Function>
152 template <typename ResultType,
153           typename... ArgumentType,
154           bool kNoexcept,
155           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
156 class tuple_size<berberis::GuestArgumentsReferences<ResultType(ArgumentType...) noexcept(kNoexcept),
157                                                     kCallingConventionsVariant>>
158     : public std::integral_constant<std::size_t, sizeof...(ArgumentType)> {};
159 
160 template <typename ResultType,
161           typename... ArgumentType,
162           bool kNoexcept,
163           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
164 class tuple_size<berberis::HostArgumentsValues<ResultType(ArgumentType...) noexcept(kNoexcept),
165                                                kCallingConventionsVariant>>
166     : public std::integral_constant<std::size_t, sizeof...(ArgumentType)> {};
167 
168 // tuple_size for GuestArgumentsReferences<PointerToFunction>
169 template <typename ResultType,
170           typename... ArgumentType,
171           bool kNoexcept,
172           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
173 class tuple_size<
174     berberis::GuestArgumentsReferences<ResultType (*)(ArgumentType...) noexcept(kNoexcept),
175                                        kCallingConventionsVariant>>
176     : public std::integral_constant<std::size_t, sizeof...(ArgumentType)> {};
177 
178 template <typename ResultType,
179           typename... ArgumentType,
180           bool kNoexcept,
181           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
182 class tuple_size<berberis::HostArgumentsValues<ResultType (*)(ArgumentType...) noexcept(kNoexcept),
183                                                kCallingConventionsVariant>>
184     : public std::integral_constant<std::size_t, sizeof...(ArgumentType)> {};
185 
186 template <std::size_t index,
187           typename FunctionType,
188           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
189 class tuple_element<index,
190                     berberis::GuestArgumentsReferences<FunctionType, kCallingConventionsVariant>> {
191  public:
192   using type = std::invoke_result_t<
193       decltype(&berberis::GuestArgumentsReferences<FunctionType, kCallingConventionsVariant>::
194                    template get<index>),
195       berberis::GuestArgumentsReferences<FunctionType, kCallingConventionsVariant>>;
196 };
197 
198 template <std::size_t index,
199           typename FunctionType,
200           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
201 class tuple_element<index,
202                     berberis::HostArgumentsValues<FunctionType, kCallingConventionsVariant>> {
203  public:
204   using type = std::invoke_result_t<
205       decltype(&berberis::HostArgumentsValues<FunctionType,
206                                               kCallingConventionsVariant>::template get<index>),
207       berberis::HostArgumentsValues<FunctionType, kCallingConventionsVariant>>;
208 };
209 
210 // tuple_size for GuestResultValue<Function>
211 template <typename ResultType,
212           typename... ArgumentType,
213           bool kNoexcept,
214           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
215 class tuple_size<berberis::GuestResultValue<ResultType(ArgumentType...) noexcept(kNoexcept),
216                                             kCallingConventionsVariant>>
217     : public std::integral_constant<std::size_t, std::is_same_v<ResultType, void> ? 0 : 1> {};
218 
219 template <typename ResultType,
220           typename... ArgumentType,
221           bool kNoexcept,
222           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
223 class tuple_size<berberis::HostResultReference<ResultType(ArgumentType...) noexcept(kNoexcept),
224                                                kCallingConventionsVariant>>
225     : public std::integral_constant<std::size_t, std::is_same_v<ResultType, void> ? 0 : 1> {};
226 
227 // tuple_size for GuestResultValue<PointerToFunction>
228 template <typename ResultType,
229           typename... ArgumentType,
230           bool kNoexcept,
231           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
232 class tuple_size<berberis::GuestResultValue<ResultType (*)(ArgumentType...) noexcept(kNoexcept),
233                                             kCallingConventionsVariant>>
234     : public std::integral_constant<std::size_t, std::is_same_v<ResultType, void> ? 0 : 1> {};
235 
236 template <typename ResultType,
237           typename... ArgumentType,
238           bool kNoexcept,
239           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
240 class tuple_size<berberis::HostResultReference<ResultType (*)(ArgumentType...) noexcept(kNoexcept),
241                                                kCallingConventionsVariant>>
242     : public std::integral_constant<std::size_t, std::is_same_v<ResultType, void> ? 0 : 1> {};
243 
244 template <typename FunctionType,
245           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
246 class tuple_element<0, berberis::GuestResultValue<FunctionType, kCallingConventionsVariant>> {
247  public:
248   using type = std::invoke_result_t<
249       decltype(&berberis::GuestResultValue<FunctionType,
250                                            kCallingConventionsVariant>::template get<0>),
251       berberis::GuestResultValue<FunctionType, kCallingConventionsVariant>>;
252 };
253 
254 template <typename FunctionType,
255           berberis::GuestAbi::CallingConventionsVariant kCallingConventionsVariant>
256 class tuple_element<0, berberis::HostResultReference<FunctionType, kCallingConventionsVariant>> {
257  public:
258   using type = std::invoke_result_t<
259       decltype(&berberis::HostResultReference<FunctionType,
260                                               kCallingConventionsVariant>::template get<0>),
261       berberis::HostResultReference<FunctionType, kCallingConventionsVariant>>;
262 };
263 
264 }  // namespace std
265 
266 #endif  // BERBERIS_GUEST_ABI_GUEST_ARGUMENTS_H_
267