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