1 /*
2 * Copyright (C) 2018 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
18 /*
19 * WARNING: Do not include and use these directly. Use jni_macros.h instead!
20 * The "detail" namespace should be a strong hint not to depend on the internals,
21 * which could change at any time.
22 *
23 * This implements the underlying mechanism for compile-time JNI signature/ctype checking
24 * and inference.
25 *
26 * This file provides the constexpr basic blocks such as strings, arrays, vectors
27 * as well as the JNI-specific parsing functionality.
28 *
29 * Everything is implemented via generic-style (templates without metaprogramming)
30 * wherever possible. Traditional template metaprogramming is used sparingly.
31 *
32 * Everything in this file except ostream<< is constexpr.
33 */
34
35 #pragma once
36
37 #include <iostream> // std::ostream
38 #include <jni.h> // jni typedefs, JniNativeMethod.
39 #include <type_traits> // std::common_type, std::remove_cv
40
41 namespace nativehelper {
42 namespace detail {
43
44 // If CHECK evaluates to false then X_ASSERT will halt compilation.
45 //
46 // Asserts meant to be used only within constexpr context.
47 #if defined(JNI_SIGNATURE_CHECKER_DISABLE_ASSERTS)
48 # define X_ASSERT(CHECK) do { if ((false)) { (CHECK) ? void(0) : void(0); } } while (false)
49 #else
50 # define X_ASSERT(CHECK) \
51 ( (CHECK) ? void(0) : jni_assertion_failure(#CHECK) )
52 #endif
53
54 // The runtime 'jni_assert' will never get called from a constexpr context;
55 // instead compilation will abort with a stack trace.
56 //
57 // Inspect the frame above this one to see the exact nature of the failure.
58 inline void jni_assertion_failure(const char* /*msg*/) __attribute__((noreturn));
jni_assertion_failure(const char *)59 inline void jni_assertion_failure(const char* /*msg*/) {
60 std::terminate();
61 }
62
63 // An immutable constexpr string view, similar to std::string_view but for C++14.
64 // For a mutable string see instead ConstexprVector<char>.
65 //
66 // As it is a read-only view into a string, it is not guaranteed to be zero-terminated.
67 struct ConstexprStringView {
68 // Implicit conversion from string literal:
69 // ConstexprStringView str = "hello_world";
70 template<size_t N>
ConstexprStringViewConstexprStringView71 constexpr ConstexprStringView(const char (& lit)[N]) // NOLINT: explicit.
72 : _array(lit), _size(N - 1) {
73 // Using an array of characters is not allowed because the inferred size would be wrong.
74 // Use the other constructor instead for that.
75 X_ASSERT(lit[N - 1] == '\0');
76 }
77
ConstexprStringViewConstexprStringView78 constexpr ConstexprStringView(const char* ptr, size_t size)
79 : _array(ptr), _size(size) {
80 // See the below constructor instead.
81 X_ASSERT(ptr != nullptr);
82 }
83
84 // No-arg constructor: Create empty view.
ConstexprStringViewConstexprStringView85 constexpr ConstexprStringView() : _array(""), _size(0u) {}
86
sizeConstexprStringView87 constexpr size_t size() const {
88 return _size;
89 }
90
emptyConstexprStringView91 constexpr bool empty() const {
92 return size() == 0u;
93 }
94
95 constexpr char operator[](size_t i) const {
96 X_ASSERT(i <= size());
97 return _array[i];
98 }
99
100 // Create substring from this[start..start+len).
substrConstexprStringView101 constexpr ConstexprStringView substr(size_t start, size_t len) const {
102 X_ASSERT(start <= size());
103 X_ASSERT(len <= size() - start);
104
105 return ConstexprStringView(&_array[start], len);
106 }
107
108 // Create maximum length substring that begins at 'start'.
substrConstexprStringView109 constexpr ConstexprStringView substr(size_t start) const {
110 X_ASSERT(start <= size());
111 return substr(start, size() - start);
112 }
113
114 using const_iterator = const char*;
115
beginConstexprStringView116 constexpr const_iterator begin() const {
117 return &_array[0];
118 }
119
endConstexprStringView120 constexpr const_iterator end() const {
121 return &_array[size()];
122 }
123
124 private:
125 const char* _array; // Never-null for simplicity.
126 size_t _size;
127 };
128
129 constexpr bool
130 operator==(const ConstexprStringView& lhs, const ConstexprStringView& rhs) {
131 if (lhs.size() != rhs.size()) {
132 return false;
133 }
134 for (size_t i = 0; i < lhs.size(); ++i) {
135 if (lhs[i] != rhs[i]) {
136 return false;
137 }
138 }
139 return true;
140 }
141
142 constexpr bool
143 operator!=(const ConstexprStringView& lhs, const ConstexprStringView& rhs) {
144 return !(lhs == rhs);
145 }
146
147 inline std::ostream& operator<<(std::ostream& os, const ConstexprStringView& str) {
148 for (char c : str) {
149 os << c;
150 }
151 return os;
152 }
153
IsValidJniDescriptorStart(char shorty)154 constexpr bool IsValidJniDescriptorStart(char shorty) {
155 constexpr char kValidJniStarts[] =
156 {'V', 'Z', 'B', 'C', 'S', 'I', 'J', 'F', 'D', 'L', '[', '(', ')'};
157
158 for (char c : kValidJniStarts) {
159 if (c == shorty) {
160 return true;
161 }
162 }
163
164 return false;
165 }
166
167 // A constexpr "vector" that supports storing a variable amount of Ts
168 // in an array-like interface.
169 //
170 // An up-front kMaxSize must be given since constexpr does not support
171 // dynamic allocations.
172 template<typename T, size_t kMaxSize>
173 struct ConstexprVector {
174 public:
ConstexprVectorConstexprVector175 constexpr explicit ConstexprVector() : _size(0u), _array{} {
176 }
177
178 private:
179 // Custom iterator to support ptr-one-past-end into the union array without
180 // undefined behavior.
181 template<typename Elem>
182 struct VectorIterator {
183 Elem* ptr;
184
185 constexpr VectorIterator& operator++() {
186 ++ptr;
187 return *this;
188 }
189
190 constexpr VectorIterator operator++(int) const {
191 VectorIterator tmp(*this);
192 ++tmp;
193 return tmp;
194 }
195
196 constexpr /*T&*/ auto& operator*() {
197 // Use 'auto' here since using 'T' is incorrect with const_iterator.
198 return ptr->_value;
199 }
200
201 constexpr const /*T&*/ auto& operator*() const {
202 // Use 'auto' here for consistency with above.
203 return ptr->_value;
204 }
205
206 constexpr bool operator==(const VectorIterator& other) const {
207 return ptr == other.ptr;
208 }
209
210 constexpr bool operator!=(const VectorIterator& other) const {
211 return !(*this == other);
212 }
213 };
214
215 // Do not require that T is default-constructible by using a union.
216 struct MaybeElement {
217 union {
218 T _value;
219 };
220 };
221
222 public:
223 using iterator = VectorIterator<MaybeElement>;
224 using const_iterator = VectorIterator<const MaybeElement>;
225
beginConstexprVector226 constexpr iterator begin() {
227 return {&_array[0]};
228 }
229
endConstexprVector230 constexpr iterator end() {
231 return {&_array[size()]};
232 }
233
beginConstexprVector234 constexpr const_iterator begin() const {
235 return {&_array[0]};
236 }
237
endConstexprVector238 constexpr const_iterator end() const {
239 return {&_array[size()]};
240 }
241
push_backConstexprVector242 constexpr void push_back(const T& value) {
243 X_ASSERT(_size + 1 <= kMaxSize);
244
245 _array[_size]._value = value;
246 _size++;
247 }
248
249 // A pop operation could also be added since constexpr T's
250 // have default destructors, it would just be _size--.
251 // We do not need a pop() here though.
252
253 constexpr const T& operator[](size_t i) const {
254 return _array[i]._value;
255 }
256
257 constexpr T& operator[](size_t i) {
258 return _array[i]._value;
259 }
260
sizeConstexprVector261 constexpr size_t size() const {
262 return _size;
263 }
264 private:
265
266 size_t _size;
267 MaybeElement _array[kMaxSize];
268 };
269
270 // Parsed and validated "long" form of a single JNI descriptor.
271 // e.g. one of "J", "Ljava/lang/Object;" etc.
272 struct JniDescriptorNode {
273 ConstexprStringView longy;
274
JniDescriptorNodeJniDescriptorNode275 constexpr JniDescriptorNode(ConstexprStringView longy) : longy(longy) { // NOLINT(google-explicit-constructor)
276 X_ASSERT(!longy.empty());
277 }
JniDescriptorNodeJniDescriptorNode278 constexpr JniDescriptorNode() : longy() {}
279
shortyJniDescriptorNode280 constexpr char shorty() {
281 // Must be initialized with the non-default constructor.
282 X_ASSERT(!longy.empty());
283 return longy[0];
284 }
285 };
286
287 inline std::ostream& operator<<(std::ostream& os, const JniDescriptorNode& node) {
288 os << node.longy;
289 return os;
290 }
291
292 // Equivalent of C++17 std::optional.
293 //
294 // An optional is essentially a type safe
295 // union {
296 // void Nothing,
297 // T Some;
298 // };
299 //
300 template<typename T>
301 struct ConstexprOptional {
302 // Create a default optional with no value.
ConstexprOptionalConstexprOptional303 constexpr ConstexprOptional() : _has_value(false), _nothing() {
304 }
305
306 // Create an optional with a value.
ConstexprOptionalConstexprOptional307 constexpr ConstexprOptional(const T& value) // NOLINT(google-explicit-constructor)
308 : _has_value(true), _value(value) {
309 }
310
311 constexpr explicit operator bool() const {
312 return _has_value;
313 }
314
has_valueConstexprOptional315 constexpr bool has_value() const {
316 return _has_value;
317 }
318
valueConstexprOptional319 constexpr const T& value() const {
320 X_ASSERT(has_value());
321 return _value;
322 }
323
324 constexpr const T* operator->() const {
325 return &(value());
326 }
327
328 constexpr const T& operator*() const {
329 return value();
330 }
331
332 private:
333 bool _has_value;
334 // The "Nothing" is likely unnecessary but improves readability.
335 struct Nothing {};
336 union {
337 Nothing _nothing;
338 T _value;
339 };
340 };
341
342 template<typename T>
343 constexpr bool
344 operator==(const ConstexprOptional<T>& lhs, const ConstexprOptional<T>& rhs) {
345 if (lhs && rhs) {
346 return lhs.value() == rhs.value();
347 }
348 return lhs.has_value() == rhs.has_value();
349 }
350
351 template<typename T>
352 constexpr bool
353 operator!=(const ConstexprOptional<T>& lhs, const ConstexprOptional<T>& rhs) {
354 return !(lhs == rhs);
355 }
356
357 template<typename T>
358 inline std::ostream& operator<<(std::ostream& os, const ConstexprOptional<T>& val) {
359 if (val) {
360 os << val.value();
361 }
362 return os;
363 }
364
365 // Equivalent of std::nullopt
366 // Allows implicit conversion to any empty ConstexprOptional<T>.
367 // Mostly useful for macros that need to return an empty constexpr optional.
368 struct NullConstexprOptional {
369 template<typename T>
370 constexpr operator ConstexprOptional<T>() const { // NOLINT(google-explicit-constructor)
371 return ConstexprOptional<T>();
372 }
373 };
374
375 inline std::ostream& operator<<(std::ostream& os, NullConstexprOptional) {
376 return os;
377 }
378
379 #if !defined(PARSE_FAILURES_NONFATAL)
380 // Unfortunately we cannot have custom messages here, as it just prints a stack trace with the
381 // macros expanded. This is at least more flexible than static_assert which requires a string
382 // literal.
383 // NOTE: The message string literal must be on same line as the macro to be seen during a
384 // compilation error.
385 #define PARSE_FAILURE(msg) X_ASSERT(! #msg)
386 #define PARSE_ASSERT_MSG(cond, msg) X_ASSERT(#msg && (cond))
387 #define PARSE_ASSERT(cond) X_ASSERT(cond)
388 #else
389 #define PARSE_FAILURE(msg) return NullConstexprOptional{};
390 #define PARSE_ASSERT_MSG(cond, msg) if (!(cond)) { PARSE_FAILURE(msg); }
391 #define PARSE_ASSERT(cond) if (!(cond)) { PARSE_FAILURE(""); }
392 #endif
393
394 // This is a placeholder function and should not be called directly.
ParseFailure(const char * msg)395 constexpr void ParseFailure(const char* msg) {
396 (void) msg; // intentionally no-op.
397 }
398
399 // Temporary parse data when parsing a function descriptor.
400 struct ParseTypeDescriptorResult {
401 // A single argument descriptor, e.g. "V" or "Ljava/lang/Object;"
402 ConstexprStringView token;
403 // The remainder of the function descriptor yet to be parsed.
404 ConstexprStringView remainder;
405
has_tokenParseTypeDescriptorResult406 constexpr bool has_token() const {
407 return token.size() > 0u;
408 }
409
has_remainderParseTypeDescriptorResult410 constexpr bool has_remainder() const {
411 return remainder.size() > 0u;
412 }
413
as_nodeParseTypeDescriptorResult414 constexpr JniDescriptorNode as_node() const {
415 X_ASSERT(has_token());
416 return {token};
417 }
418 };
419
420 // Parse a single type descriptor out of a function type descriptor substring,
421 // and return the token and the remainder string.
422 //
423 // If parsing fails (i.e. illegal syntax), then:
424 // parses are fatal -> assertion is triggered (default behavior),
425 // parses are nonfatal -> returns nullopt (test behavior).
426 constexpr ConstexprOptional<ParseTypeDescriptorResult>
427 ParseSingleTypeDescriptor(ConstexprStringView single_type,
428 bool allow_void = false) {
429 constexpr NullConstexprOptional kUnreachable = {};
430
431 // Nothing else left.
432 if (single_type.size() == 0) {
433 return ParseTypeDescriptorResult{};
434 }
435
436 ConstexprStringView token;
437 ConstexprStringView remainder = single_type.substr(/*start*/1u);
438
439 char c = single_type[0];
440 PARSE_ASSERT(IsValidJniDescriptorStart(c));
441
442 enum State {
443 kSingleCharacter,
444 kArray,
445 kObject
446 };
447
448 State state = kSingleCharacter;
449
450 // Parse the first character to figure out if we should parse the rest.
451 switch (c) {
452 case '!': {
453 constexpr bool fast_jni_is_deprecated = false;
454 PARSE_ASSERT(fast_jni_is_deprecated);
455 break;
456 }
457 case 'V':
458 if (!allow_void) {
459 constexpr bool void_type_descriptor_only_allowed_in_return_type = false;
460 PARSE_ASSERT(void_type_descriptor_only_allowed_in_return_type);
461 }
462 [[clang::fallthrough]];
463 case 'Z':
464 case 'B':
465 case 'C':
466 case 'S':
467 case 'I':
468 case 'J':
469 case 'F':
470 case 'D':
471 token = single_type.substr(/*start*/0u, /*len*/1u);
472 break;
473 case 'L':
474 state = kObject;
475 break;
476 case '[':
477 state = kArray;
478 break;
479 default: {
480 // See JNI Chapter 3: Type Signatures.
481 PARSE_FAILURE("Expected a valid type descriptor character.");
482 return kUnreachable;
483 }
484 }
485
486 // Possibly parse an arbitary-long remainder substring.
487 switch (state) {
488 case kSingleCharacter:
489 return {{token, remainder}};
490 case kArray: {
491 // Recursively parse the array component, as it's just any non-void type descriptor.
492 ConstexprOptional<ParseTypeDescriptorResult>
493 maybe_res = ParseSingleTypeDescriptor(remainder, /*allow_void*/false);
494 PARSE_ASSERT(maybe_res); // Downstream parsing has asserted, bail out.
495
496 ParseTypeDescriptorResult res = maybe_res.value();
497
498 // Reject illegal array type descriptors such as "]".
499 PARSE_ASSERT_MSG(res.has_token(), "All array types must follow by their component type (e.g. ']I', ']]Z', etc. ");
500
501 token = single_type.substr(/*start*/0u, res.token.size() + 1u);
502
503 return {{token, res.remainder}};
504 }
505 case kObject: {
506 // Parse the fully qualified class, e.g. Lfoo/bar/baz;
507 // Note checking that each part of the class name is a valid class identifier
508 // is too complicated (JLS 3.8).
509 // This simple check simply scans until the next ';'.
510 bool found_semicolon = false;
511 size_t semicolon_len = 0;
512 for (size_t i = 0; i < single_type.size(); ++i) {
513 switch (single_type[i]) {
514 case ')':
515 case '(':
516 case '[':
517 PARSE_FAILURE("Object identifiers cannot have ()[ in them.");
518 break;
519 }
520 if (single_type[i] == ';') {
521 semicolon_len = i + 1;
522 found_semicolon = true;
523 break;
524 }
525 }
526
527 PARSE_ASSERT(found_semicolon);
528
529 token = single_type.substr(/*start*/0u, semicolon_len);
530 remainder = single_type.substr(/*start*/semicolon_len);
531
532 bool class_name_is_empty = token.size() <= 2u; // e.g. "L;"
533 PARSE_ASSERT(!class_name_is_empty);
534
535 return {{token, remainder}};
536 }
537 default:
538 X_ASSERT(false);
539 }
540
541 X_ASSERT(false);
542 return kUnreachable;
543 }
544
545 // Abstract data type to represent container for Ret(Args,...).
546 template<typename T, size_t kMaxSize>
547 struct FunctionSignatureDescriptor {
548 ConstexprVector<T, kMaxSize> args;
549 T ret;
550
551 static constexpr size_t max_size = kMaxSize;
552 };
553
554
555 template<typename T, size_t kMaxSize>
556 inline std::ostream& operator<<(
557 std::ostream& os,
558 const FunctionSignatureDescriptor<T, kMaxSize>& signature) {
559 size_t count = 0;
560 os << "args={";
561 for (auto& arg : signature.args) {
562 os << arg;
563
564 if (count != signature.args.size() - 1) {
565 os << ",";
566 }
567
568 ++count;
569 }
570 os << "}, ret=";
571 os << signature.ret;
572 return os;
573 }
574
575 // Ret(Args...) of JniDescriptorNode.
576 template<size_t kMaxSize>
577 using JniSignatureDescriptor = FunctionSignatureDescriptor<JniDescriptorNode,
578 kMaxSize>;
579
580 // Parse a JNI function signature descriptor into a JniSignatureDescriptor.
581 //
582 // If parsing fails (i.e. illegal syntax), then:
583 // parses are fatal -> assertion is triggered (default behavior),
584 // parses are nonfatal -> returns nullopt (test behavior).
585 template<size_t kMaxSize>
586 constexpr ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
ParseSignatureAsList(ConstexprStringView signature)587 ParseSignatureAsList(ConstexprStringView signature) {
588 // The list of JNI descriptors cannot possibly exceed the number of characters
589 // in the JNI string literal. We leverage this to give an upper bound of the strlen.
590 // This is a bit wasteful but in constexpr there *must* be a fixed upper size for data structures.
591 ConstexprVector<JniDescriptorNode, kMaxSize> jni_desc_node_list;
592 JniDescriptorNode return_jni_desc;
593
594 enum State {
595 kInitial = 0,
596 kParsingParameters = 1,
597 kParsingReturnType = 2,
598 kCompleted = 3,
599 };
600
601 State state = kInitial;
602
603 while (!signature.empty()) {
604 switch (state) {
605 case kInitial: {
606 char c = signature[0];
607 PARSE_ASSERT_MSG(c == '(',
608 "First character of a JNI signature must be a '('");
609 state = kParsingParameters;
610 signature = signature.substr(/*start*/1u);
611 break;
612 }
613 case kParsingParameters: {
614 char c = signature[0];
615 if (c == ')') {
616 state = kParsingReturnType;
617 signature = signature.substr(/*start*/1u);
618 break;
619 }
620
621 ConstexprOptional<ParseTypeDescriptorResult>
622 res = ParseSingleTypeDescriptor(signature, /*allow_void*/false);
623 PARSE_ASSERT(res);
624
625 jni_desc_node_list.push_back(res->as_node());
626
627 signature = res->remainder;
628 break;
629 }
630 case kParsingReturnType: {
631 ConstexprOptional<ParseTypeDescriptorResult>
632 res = ParseSingleTypeDescriptor(signature, /*allow_void*/true);
633 PARSE_ASSERT(res);
634
635 return_jni_desc = res->as_node();
636 signature = res->remainder;
637 state = kCompleted;
638 break;
639 }
640 default: {
641 // e.g. "()VI" is illegal because the V terminates the signature.
642 PARSE_FAILURE("Signature had left over tokens after parsing return type");
643 break;
644 }
645 }
646 }
647
648 switch (state) {
649 case kCompleted:
650 // Everything is ok.
651 break;
652 case kParsingParameters:
653 PARSE_FAILURE("Signature was missing ')'");
654 break;
655 case kParsingReturnType:
656 PARSE_FAILURE("Missing return type");
657 case kInitial:
658 PARSE_FAILURE("Cannot have an empty signature");
659 default:
660 X_ASSERT(false);
661 }
662
663 return {{jni_desc_node_list, return_jni_desc}};
664 }
665
666 // What kind of JNI does this type belong to?
667 enum NativeKind {
668 kNotJni, // Illegal parameter used inside of a function type.
669 kNormalJniCallingConventionParameter,
670 kNormalNative,
671 kFastNative, // Also valid in normal.
672 kCriticalNative, // Also valid in fast/normal.
673 };
674
675 // Is this type final, i.e. it cannot be subtyped?
676 enum TypeFinal {
677 kNotFinal,
678 kFinal // e.g. any primitive or any "final" class such as String.
679 };
680
681 // What position is the JNI type allowed to be in?
682 // Ignored when in a CriticalNative context.
683 enum NativePositionAllowed {
684 kNotAnyPosition,
685 kReturnPosition,
686 kZerothPosition,
687 kFirstOrLaterPosition,
688 kSecondOrLaterPosition,
689 };
690
ConvertPositionToAllowed(size_t position)691 constexpr NativePositionAllowed ConvertPositionToAllowed(size_t position) {
692 switch (position) {
693 case 0:
694 return kZerothPosition;
695 case 1:
696 return kFirstOrLaterPosition;
697 default:
698 return kSecondOrLaterPosition;
699 }
700 }
701
702 // Type traits for a JNI parameter type. See below for specializations.
703 template<typename T>
704 struct jni_type_trait {
705 static constexpr NativeKind native_kind = kNotJni;
706 static constexpr const char type_descriptor[] = "(illegal)";
707 static constexpr NativePositionAllowed position_allowed = kNotAnyPosition;
708 static constexpr TypeFinal type_finality = kNotFinal;
709 static constexpr const char type_name[] = "(illegal)";
710 };
711
712 // Access the jni_type_trait<T> from a non-templated constexpr function.
713 // Identical non-static fields to jni_type_trait, see Reify().
714 struct ReifiedJniTypeTrait {
715 NativeKind native_kind;
716 ConstexprStringView type_descriptor;
717 NativePositionAllowed position_allowed;
718 TypeFinal type_finality;
719 ConstexprStringView type_name;
720
721 template<typename T>
ReifyReifiedJniTypeTrait722 static constexpr ReifiedJniTypeTrait Reify() {
723 // This should perhaps be called 'Type Erasure' except we don't use virtuals,
724 // so it's not quite the same idiom.
725 using TR = jni_type_trait<T>;
726 return {TR::native_kind,
727 TR::type_descriptor,
728 TR::position_allowed,
729 TR::type_finality,
730 TR::type_name};
731 }
732
733 // Find the most similar ReifiedJniTypeTrait corresponding to the type descriptor.
734 //
735 // Any type can be found by using the exact canonical type descriptor as listed
736 // in the jni type traits definitions.
737 //
738 // Non-final JNI types have limited support for inexact similarity:
739 // [[* | [L* -> jobjectArray
740 // L* -> jobject
741 //
742 // Otherwise return a nullopt.
743 static constexpr ConstexprOptional<ReifiedJniTypeTrait>
744 MostSimilarTypeDescriptor(ConstexprStringView type_descriptor);
745 };
746
747 constexpr bool
748 operator==(const ReifiedJniTypeTrait& lhs, const ReifiedJniTypeTrait& rhs) {
749 return lhs.native_kind == rhs.native_kind
750 && rhs.type_descriptor == lhs.type_descriptor &&
751 lhs.position_allowed == rhs.position_allowed
752 && rhs.type_finality == lhs.type_finality &&
753 lhs.type_name == rhs.type_name;
754 }
755
756 inline std::ostream& operator<<(std::ostream& os, const ReifiedJniTypeTrait& rjtt) {
757 // os << "ReifiedJniTypeTrait<" << rjft.type_name << ">";
758 os << rjtt.type_name;
759 return os;
760 }
761
762 // Template specialization for any JNI typedefs.
763 #define JNI_TYPE_TRAIT(jtype, the_type_descriptor, the_native_kind, the_type_finality, the_position) \
764 template <> \
765 struct jni_type_trait< jtype > { \
766 static constexpr NativeKind native_kind = the_native_kind; \
767 static constexpr const char type_descriptor[] = the_type_descriptor; \
768 static constexpr NativePositionAllowed position_allowed = the_position; \
769 static constexpr TypeFinal type_finality = the_type_finality; \
770 static constexpr const char type_name[] = #jtype; \
771 };
772
773 #define DEFINE_JNI_TYPE_TRAIT(TYPE_TRAIT_FN) \
774 TYPE_TRAIT_FN(jboolean, "Z", kCriticalNative, kFinal, kSecondOrLaterPosition) \
775 TYPE_TRAIT_FN(jbyte, "B", kCriticalNative, kFinal, kSecondOrLaterPosition) \
776 TYPE_TRAIT_FN(jchar, "C", kCriticalNative, kFinal, kSecondOrLaterPosition) \
777 TYPE_TRAIT_FN(jshort, "S", kCriticalNative, kFinal, kSecondOrLaterPosition) \
778 TYPE_TRAIT_FN(jint, "I", kCriticalNative, kFinal, kSecondOrLaterPosition) \
779 TYPE_TRAIT_FN(jlong, "J", kCriticalNative, kFinal, kSecondOrLaterPosition) \
780 TYPE_TRAIT_FN(jfloat, "F", kCriticalNative, kFinal, kSecondOrLaterPosition) \
781 TYPE_TRAIT_FN(jdouble, "D", kCriticalNative, kFinal, kSecondOrLaterPosition) \
782 TYPE_TRAIT_FN(jobject, "Ljava/lang/Object;", kFastNative, kNotFinal, kFirstOrLaterPosition) \
783 TYPE_TRAIT_FN(jclass, "Ljava/lang/Class;", kFastNative, kFinal, kFirstOrLaterPosition) \
784 TYPE_TRAIT_FN(jstring, "Ljava/lang/String;", kFastNative, kFinal, kSecondOrLaterPosition) \
785 TYPE_TRAIT_FN(jarray, "Ljava/lang/Object;", kFastNative, kNotFinal, kSecondOrLaterPosition) \
786 TYPE_TRAIT_FN(jobjectArray, "[Ljava/lang/Object;", kFastNative, kNotFinal, kSecondOrLaterPosition) \
787 TYPE_TRAIT_FN(jbooleanArray, "[Z", kFastNative, kFinal, kSecondOrLaterPosition) \
788 TYPE_TRAIT_FN(jbyteArray, "[B", kFastNative, kFinal, kSecondOrLaterPosition) \
789 TYPE_TRAIT_FN(jcharArray, "[C", kFastNative, kFinal, kSecondOrLaterPosition) \
790 TYPE_TRAIT_FN(jshortArray, "[S", kFastNative, kFinal, kSecondOrLaterPosition) \
791 TYPE_TRAIT_FN(jintArray, "[I", kFastNative, kFinal, kSecondOrLaterPosition) \
792 TYPE_TRAIT_FN(jlongArray, "[J", kFastNative, kFinal, kSecondOrLaterPosition) \
793 TYPE_TRAIT_FN(jfloatArray, "[F", kFastNative, kFinal, kSecondOrLaterPosition) \
794 TYPE_TRAIT_FN(jdoubleArray, "[D", kFastNative, kFinal, kSecondOrLaterPosition) \
795 TYPE_TRAIT_FN(jthrowable, "Ljava/lang/Throwable;", kFastNative, kNotFinal, kSecondOrLaterPosition) \
796 TYPE_TRAIT_FN(JNIEnv*, "", kNormalJniCallingConventionParameter, kFinal, kZerothPosition) \
797 TYPE_TRAIT_FN(void, "V", kCriticalNative, kFinal, kReturnPosition) \
798
DEFINE_JNI_TYPE_TRAIT(JNI_TYPE_TRAIT)799 DEFINE_JNI_TYPE_TRAIT(JNI_TYPE_TRAIT)
800
801 // See ReifiedJniTypeTrait for documentation.
802 constexpr ConstexprOptional<ReifiedJniTypeTrait>
803 ReifiedJniTypeTrait::MostSimilarTypeDescriptor(ConstexprStringView type_descriptor) {
804 #define MATCH_EXACT_TYPE_DESCRIPTOR_FN(type, type_desc, native_kind, ...) \
805 if (type_descriptor == type_desc && native_kind >= kNormalNative) { \
806 return { Reify<type>() }; \
807 }
808
809 // Attempt to look up by the precise type match first.
810 DEFINE_JNI_TYPE_TRAIT(MATCH_EXACT_TYPE_DESCRIPTOR_FN);
811
812 // Otherwise, we need to do an imprecise match:
813 char shorty = type_descriptor.size() >= 1 ? type_descriptor[0] : '\0';
814 if (shorty == 'L') {
815 // Something more specific like Ljava/lang/Throwable, string, etc
816 // is already matched by the macro-expanded conditions above.
817 return {Reify<jobject>()};
818 } else if (type_descriptor.size() >= 2) {
819 auto shorty_shorty = type_descriptor.substr(/*start*/0, /*size*/2u);
820 if (shorty_shorty == "[[" || shorty_shorty == "[L") {
821 // JNI arrays are covariant, so any type T[] (T!=primitive) is castable to Object[].
822 return {Reify<jobjectArray>()};
823 }
824 }
825
826 // To handle completely invalid values.
827 return NullConstexprOptional{};
828 }
829
830 // Is this actual JNI position consistent with the expected position?
IsValidJniParameterPosition(NativeKind native_kind,NativePositionAllowed position,NativePositionAllowed expected_position)831 constexpr bool IsValidJniParameterPosition(NativeKind native_kind,
832 NativePositionAllowed position,
833 NativePositionAllowed expected_position) {
834 X_ASSERT(expected_position != kNotAnyPosition);
835
836 if (native_kind == kCriticalNative) {
837 // CriticalNatives ignore positions since the first 2 special
838 // parameters are stripped.
839 return true;
840 }
841
842 // Is this a return-only position?
843 if (expected_position == kReturnPosition) {
844 if (position != kReturnPosition) {
845 // void can only be in the return position.
846 return false;
847 }
848 // Don't do the other non-return position checks for a return-only position.
849 return true;
850 }
851
852 // JNIEnv* can only be in the first spot.
853 if (position == kZerothPosition && expected_position != kZerothPosition) {
854 return false;
855 // jobject, jclass can be 1st or anywhere afterwards.
856 } else if (position == kFirstOrLaterPosition && expected_position != kFirstOrLaterPosition) {
857 return false;
858 // All other parameters must be in 2nd+ spot, or in the return type.
859 } else if (position == kSecondOrLaterPosition || position == kReturnPosition) {
860 if (expected_position != kFirstOrLaterPosition && expected_position != kSecondOrLaterPosition) {
861 return false;
862 }
863 }
864
865 return true;
866 }
867
868 // Check if a jni parameter type is valid given its position and native_kind.
869 template <typename T>
IsValidJniParameter(NativeKind native_kind,NativePositionAllowed position)870 constexpr bool IsValidJniParameter(NativeKind native_kind, NativePositionAllowed position) {
871 // const,volatile does not affect JNI compatibility since it does not change ABI.
872 using expected_trait = jni_type_trait<typename std::remove_cv<T>::type>;
873 NativeKind expected_native_kind = expected_trait::native_kind;
874
875 // Most types 'T' are not valid for JNI.
876 if (expected_native_kind == NativeKind::kNotJni) {
877 return false;
878 }
879
880 // The rest of the types might be valid, but it depends on the context (native_kind)
881 // and also on their position within the parameters.
882
883 // Position-check first.
884 NativePositionAllowed expected_position = expected_trait::position_allowed;
885 if (!IsValidJniParameterPosition(native_kind, position, expected_position)) {
886 return false;
887 }
888
889 // Ensure the type appropriate is for the native kind.
890 if (expected_native_kind == kNormalJniCallingConventionParameter) {
891 // It's always wrong to use a JNIEnv* anywhere but the 0th spot.
892 if (native_kind == kCriticalNative) {
893 // CriticalNative does not allow using a JNIEnv*.
894 return false;
895 }
896
897 return true; // OK: JniEnv* used in 0th position.
898 } else if (expected_native_kind == kCriticalNative) {
899 // CriticalNative arguments are always valid JNI types anywhere used.
900 return true;
901 } else if (native_kind == kCriticalNative) {
902 // The expected_native_kind was non-critical but we are in a critical context.
903 // Illegal type.
904 return false;
905 }
906
907 // Everything else is fine, e.g. fast/normal native + fast/normal native parameters.
908 return true;
909 }
910
911 // Is there sufficient number of parameters given the kind of JNI that it is?
IsJniParameterCountValid(NativeKind native_kind,size_t count)912 constexpr bool IsJniParameterCountValid(NativeKind native_kind, size_t count) {
913 if (native_kind == kNormalNative || native_kind == kFastNative) {
914 return count >= 2u;
915 } else if (native_kind == kCriticalNative) {
916 return true;
917 }
918
919 constexpr bool invalid_parameter = false;
920 X_ASSERT(invalid_parameter);
921 return false;
922 }
923
924 // Basic template interface. See below for partial specializations.
925 //
926 // Each instantiation will have a 'value' field that determines whether or not
927 // all of the Args are valid JNI arguments given their native_kind.
928 template<NativeKind native_kind, size_t position, typename ... Args>
929 struct is_valid_jni_argument_type {
930 // static constexpr bool value = ?;
931 };
932
933 template<NativeKind native_kind, size_t position>
934 struct is_valid_jni_argument_type<native_kind, position> {
935 static constexpr bool value = true;
936 };
937
938 template<NativeKind native_kind, size_t position, typename T>
939 struct is_valid_jni_argument_type<native_kind, position, T> {
940 static constexpr bool value =
941 IsValidJniParameter<T>(native_kind, ConvertPositionToAllowed(position));
942 };
943
944 template<NativeKind native_kind, size_t position, typename T, typename ... Args>
945 struct is_valid_jni_argument_type<native_kind, position, T, Args...> {
946 static constexpr bool value =
947 IsValidJniParameter<T>(native_kind, ConvertPositionToAllowed(position))
948 && is_valid_jni_argument_type<native_kind,
949 position + 1,
950 Args...>::value;
951 };
952
953 // This helper is required to decompose the function type into a list of arg types.
954 template<NativeKind native_kind, typename T, T fn>
955 struct is_valid_jni_function_type_helper;
956
957 template<NativeKind native_kind, typename R, typename ... Args, R fn(Args...)>
958 struct is_valid_jni_function_type_helper<native_kind, R(Args...), fn> {
959 static constexpr bool value =
960 IsJniParameterCountValid(native_kind, sizeof...(Args))
961 && IsValidJniParameter<R>(native_kind, kReturnPosition)
962 && is_valid_jni_argument_type<native_kind, /*position*/
963 0,
964 Args...>::value;
965 };
966
967 // Is this function type 'T' a valid C++ function type given the native_kind?
968 template<NativeKind native_kind, typename T, T fn>
969 constexpr bool IsValidJniFunctionType() {
970 return is_valid_jni_function_type_helper<native_kind, T, fn>::value;
971 // TODO: we could replace template metaprogramming with constexpr by
972 // using FunctionTypeMetafunction.
973 }
974
975 // Many parts of std::array is not constexpr until C++17.
976 template<typename T, size_t N>
977 struct ConstexprArray {
978 // Intentionally public to conform to std::array.
979 // This means all constructors are implicit.
980 // *NOT* meant to be used directly, use the below functions instead.
981 //
982 // The reason std::array has it is to support direct-list-initialization,
983 // e.g. "ConstexprArray<T, sz>{T{...}, T{...}, T{...}, ...};"
984 //
985 // Note that otherwise this would need a very complicated variadic
986 // argument constructor to only support list of Ts.
987 T _array[N];
988
989 constexpr size_t size() const {
990 return N;
991 }
992
993 using iterator = T*;
994 using const_iterator = const T*;
995
996 constexpr iterator begin() {
997 return &_array[0];
998 }
999
1000 constexpr iterator end() {
1001 return &_array[N];
1002 }
1003
1004 constexpr const_iterator begin() const {
1005 return &_array[0];
1006 }
1007
1008 constexpr const_iterator end() const {
1009 return &_array[N];
1010 }
1011
1012 constexpr T& operator[](size_t i) {
1013 return _array[i];
1014 }
1015
1016 constexpr const T& operator[](size_t i) const {
1017 return _array[i];
1018 }
1019 };
1020
1021 // Why do we need this?
1022 // auto x = {1,2,3} creates an initializer_list,
1023 // but they can't be returned because it contains pointers to temporaries.
1024 // auto x[] = {1,2,3} doesn't even work because auto for arrays is not supported.
1025 //
1026 // an alternative would be to pull up std::common_t directly into the call site
1027 // std::common_type_t<Args...> array[] = {1,2,3}
1028 // but that's even more cludgier.
1029 //
1030 // As the other "stdlib-wannabe" functions, it's weaker than the library
1031 // fundamentals std::make_array but good enough for our use.
1032 template<typename... Args>
1033 constexpr auto MakeArray(Args&& ... args) {
1034 return ConstexprArray<typename std::common_type<Args...>::type,
1035 sizeof...(Args)>{args...};
1036 }
1037
1038 // See below.
1039 template<typename T, T fn>
1040 struct FunctionTypeMetafunction {
1041 };
1042
1043 // Enables the "map" operation over the function component types.
1044 template<typename R, typename ... Args, R fn(Args...)>
1045 struct FunctionTypeMetafunction<R(Args...), fn> {
1046 // Count how many arguments there are, and add 1 for the return type.
1047 static constexpr size_t
1048 count = sizeof...(Args) + 1u; // args and return type.
1049
1050 // Return an array where the metafunction 'Func' has been applied
1051 // to every argument type. The metafunction must be returning a common type.
1052 template<template<typename Arg> class Func>
1053 static constexpr auto map_args() {
1054 return map_args_impl<Func>(holder < Args > {}...);
1055 }
1056
1057 // Apply the metafunction 'Func' over the return type.
1058 template<template<typename Ret> class Func>
1059 static constexpr auto map_return() {
1060 return Func<R>{}();
1061 }
1062
1063 private:
1064 template<typename T>
1065 struct holder {
1066 };
1067
1068 template<template<typename Arg> class Func, typename Arg0, typename... ArgsRest>
1069 static constexpr auto map_args_impl(holder<Arg0>, holder<ArgsRest>...) {
1070 // One does not simply call MakeArray with 0 template arguments...
1071 auto array = MakeArray(
1072 Func<Args>{}()...
1073 );
1074
1075 return array;
1076 }
1077
1078 template<template<typename Arg> class Func>
1079 static constexpr auto map_args_impl() {
1080 // This overload provides support for MakeArray() with 0 arguments.
1081 using ComponentType = decltype(Func<void>{}());
1082
1083 return ConstexprArray<ComponentType, /*size*/0u>{};
1084 }
1085 };
1086
1087 // Apply ReifiedJniTypeTrait::Reify<T> for every function component type.
1088 template<typename T>
1089 struct ReifyJniTypeMetafunction {
1090 constexpr ReifiedJniTypeTrait operator()() const {
1091 auto res = ReifiedJniTypeTrait::Reify<T>();
1092 X_ASSERT(res.native_kind != kNotJni);
1093 return res;
1094 }
1095 };
1096
1097 // Ret(Args...) where every component is a ReifiedJniTypeTrait.
1098 template<size_t kMaxSize>
1099 using ReifiedJniSignature = FunctionSignatureDescriptor<ReifiedJniTypeTrait,
1100 kMaxSize>;
1101
1102 // Attempts to convert the function type T into a list of ReifiedJniTypeTraits
1103 // that correspond to the function components.
1104 //
1105 // If conversion fails (i.e. non-jni compatible types), then:
1106 // parses are fatal -> assertion is triggered (default behavior),
1107 // parses are nonfatal -> returns nullopt (test behavior).
1108 template <NativeKind native_kind,
1109 typename T,
1110 T fn,
1111 size_t kMaxSize = FunctionTypeMetafunction<T, fn>::count>
1112 constexpr ConstexprOptional<ReifiedJniSignature<kMaxSize>>
1113 MaybeMakeReifiedJniSignature() {
1114 if (!IsValidJniFunctionType<native_kind, T, fn>()) {
1115 PARSE_FAILURE("The function signature has one or more types incompatible with JNI.");
1116 }
1117
1118 ReifiedJniTypeTrait return_jni_trait =
1119 FunctionTypeMetafunction<T,
1120 fn>::template map_return<ReifyJniTypeMetafunction>();
1121
1122 constexpr size_t
1123 kSkipArgumentPrefix = (native_kind != kCriticalNative) ? 2u : 0u;
1124 ConstexprVector<ReifiedJniTypeTrait, kMaxSize> args;
1125 auto args_list =
1126 FunctionTypeMetafunction<T, fn>::template map_args<ReifyJniTypeMetafunction>();
1127 size_t args_index = 0;
1128 for (auto& arg : args_list) {
1129 // Ignore the 'JNIEnv*, jobject' / 'JNIEnv*, jclass' prefix,
1130 // as its not part of the function descriptor string.
1131 if (args_index >= kSkipArgumentPrefix) {
1132 args.push_back(arg);
1133 }
1134
1135 ++args_index;
1136 }
1137
1138 return {{args, return_jni_trait}};
1139 }
1140
1141 #define COMPARE_DESCRIPTOR_CHECK(expr) if (!(expr)) return false
1142 #define COMPARE_DESCRIPTOR_FAILURE_MSG(msg) if ((true)) return false
1143
1144 // Compares a user-defined JNI descriptor (of a single argument or return value)
1145 // to a reified jni type trait that was derived from the C++ function type.
1146 //
1147 // If comparison fails (i.e. non-jni compatible types), then:
1148 // parses are fatal -> assertion is triggered (default behavior),
1149 // parses are nonfatal -> returns false (test behavior).
1150 constexpr bool
1151 CompareJniDescriptorNodeErased(JniDescriptorNode user_defined_descriptor,
1152 ReifiedJniTypeTrait derived) {
1153
1154 ConstexprOptional<ReifiedJniTypeTrait> user_reified_opt =
1155 ReifiedJniTypeTrait::MostSimilarTypeDescriptor(user_defined_descriptor.longy);
1156
1157 if (!user_reified_opt.has_value()) {
1158 COMPARE_DESCRIPTOR_FAILURE_MSG(
1159 "Could not find any JNI C++ type corresponding to the type descriptor");
1160 }
1161
1162 char user_shorty = user_defined_descriptor.longy.size() > 0 ?
1163 user_defined_descriptor.longy[0] :
1164 '\0';
1165
1166 ReifiedJniTypeTrait user = user_reified_opt.value();
1167 if (user == derived) {
1168 // If we had a similar match, immediately return success.
1169 return true;
1170 } else if (derived.type_name == "jthrowable") {
1171 if (user_shorty == 'L') {
1172 // Weakly allow any objects to correspond to a jthrowable.
1173 // We do not know the managed type system so we have to be permissive here.
1174 return true;
1175 } else {
1176 COMPARE_DESCRIPTOR_FAILURE_MSG(
1177 "jthrowable must correspond to an object type descriptor");
1178 }
1179 } else if (derived.type_name == "jarray") {
1180 if (user_shorty == '[') {
1181 // a jarray is the base type for all other array types. Allow.
1182 return true;
1183 } else {
1184 // Ljava/lang/Object; is the root for all array types.
1185 // Already handled above in 'if user == derived'.
1186 COMPARE_DESCRIPTOR_FAILURE_MSG(
1187 "jarray must correspond to array type descriptor");
1188 }
1189 }
1190 // Otherwise, the comparison has failed and the rest of this is only to
1191 // pick the most appropriate error message.
1192 //
1193 // Note: A weaker form of comparison would allow matching 'Ljava/lang/String;'
1194 // against 'jobject', etc. However the policy choice here is to enforce the strictest
1195 // comparison that we can to utilize the type system to its fullest.
1196
1197 if (derived.type_finality == kFinal || user.type_finality == kFinal) {
1198 // Final types, e.g. "I", "Ljava/lang/String;" etc must match exactly
1199 // the C++ jni descriptor string ('I' -> jint, 'Ljava/lang/String;' -> jstring).
1200 COMPARE_DESCRIPTOR_FAILURE_MSG(
1201 "The JNI descriptor string must be the exact type equivalent of the "
1202 "C++ function signature.");
1203 } else if (user_shorty == '[') {
1204 COMPARE_DESCRIPTOR_FAILURE_MSG(
1205 "The array JNI descriptor must correspond to j${type}Array or jarray");
1206 } else if (user_shorty == 'L') {
1207 COMPARE_DESCRIPTOR_FAILURE_MSG(
1208 "The object JNI descriptor must correspond to jobject.");
1209 } else {
1210 X_ASSERT(false); // We should never get here, but either way this means the types did not match
1211 COMPARE_DESCRIPTOR_FAILURE_MSG(
1212 "The JNI type descriptor string does not correspond to the C++ JNI type.");
1213 }
1214 }
1215
1216 // Matches a user-defined JNI function descriptor against the C++ function type.
1217 //
1218 // If matches fails, then:
1219 // parses are fatal -> assertion is triggered (default behavior),
1220 // parses are nonfatal -> returns false (test behavior).
1221 template<NativeKind native_kind, typename T, T fn, size_t kMaxSize>
1222 constexpr bool
1223 MatchJniDescriptorWithFunctionType(ConstexprStringView user_function_descriptor) {
1224 constexpr size_t kReifiedMaxSize = FunctionTypeMetafunction<T, fn>::count;
1225
1226 ConstexprOptional<ReifiedJniSignature<kReifiedMaxSize>>
1227 reified_signature_opt =
1228 MaybeMakeReifiedJniSignature<native_kind, T, fn>();
1229 if (!reified_signature_opt) {
1230 // Assertion handling done by MaybeMakeReifiedJniSignature.
1231 return false;
1232 }
1233
1234 ConstexprOptional<JniSignatureDescriptor<kMaxSize>> user_jni_sig_desc_opt =
1235 ParseSignatureAsList<kMaxSize>(user_function_descriptor);
1236
1237 if (!user_jni_sig_desc_opt) {
1238 // Assertion handling done by ParseSignatureAsList.
1239 return false;
1240 }
1241
1242 ReifiedJniSignature<kReifiedMaxSize>
1243 reified_signature = reified_signature_opt.value();
1244 JniSignatureDescriptor<kMaxSize>
1245 user_jni_sig_desc = user_jni_sig_desc_opt.value();
1246
1247 if (reified_signature.args.size() != user_jni_sig_desc.args.size()) {
1248 COMPARE_DESCRIPTOR_FAILURE_MSG(
1249 "Number of parameters in JNI descriptor string"
1250 "did not match number of parameters in C++ function type");
1251 } else if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.ret,
1252 reified_signature.ret)) {
1253 // Assertion handling done by CompareJniDescriptorNodeErased.
1254 return false;
1255 } else {
1256 for (size_t i = 0; i < user_jni_sig_desc.args.size(); ++i) {
1257 if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.args[i],
1258 reified_signature.args[i])) {
1259 // Assertion handling done by CompareJniDescriptorNodeErased.
1260 return false;
1261 }
1262 }
1263 }
1264
1265 return true;
1266 }
1267
1268 // Supports inferring the JNI function descriptor string from the C++
1269 // function type when all type components are final.
1270 template<NativeKind native_kind, typename T, T fn>
1271 struct InferJniDescriptor {
1272 static constexpr size_t kMaxSize = FunctionTypeMetafunction<T, fn>::count;
1273
1274 // Convert the C++ function type into a JniSignatureDescriptor which holds
1275 // the canonical (according to jni_traits) descriptors for each component.
1276 // The C++ type -> JNI mapping must be nonambiguous (see jni_macros.h for exact rules).
1277 //
1278 // If conversion fails (i.e. C++ signatures is illegal for JNI, or the types are ambiguous):
1279 // if parsing is fatal -> assertion failure (default behavior)
1280 // if parsing is nonfatal -> returns nullopt (test behavior).
1281 static constexpr ConstexprOptional<JniSignatureDescriptor<kMaxSize>> FromFunctionType() {
1282 constexpr size_t kReifiedMaxSize = kMaxSize;
1283 ConstexprOptional<ReifiedJniSignature<kReifiedMaxSize>>
1284 reified_signature_opt =
1285 MaybeMakeReifiedJniSignature<native_kind, T, fn>();
1286 if (!reified_signature_opt) {
1287 // Assertion handling done by MaybeMakeReifiedJniSignature.
1288 return NullConstexprOptional{};
1289 }
1290
1291 ReifiedJniSignature<kReifiedMaxSize>
1292 reified_signature = reified_signature_opt.value();
1293
1294 JniSignatureDescriptor<kReifiedMaxSize> signature_descriptor;
1295
1296 if (reified_signature.ret.type_finality != kFinal) {
1297 // e.g. jint, jfloatArray, jstring, jclass are ok. jobject, jthrowable, jarray are not.
1298 PARSE_FAILURE("Bad return type. Only unambigous (final) types can be used to infer a signature."); // NOLINT
1299 }
1300 signature_descriptor.ret =
1301 JniDescriptorNode{reified_signature.ret.type_descriptor};
1302
1303 for (size_t i = 0; i < reified_signature.args.size(); ++i) {
1304 const ReifiedJniTypeTrait& arg_trait = reified_signature.args[i];
1305 if (arg_trait.type_finality != kFinal) {
1306 PARSE_FAILURE("Bad parameter type. Only unambigous (final) types can be used to infer a signature."); // NOLINT
1307 }
1308 signature_descriptor.args.push_back(JniDescriptorNode{
1309 arg_trait.type_descriptor});
1310 }
1311
1312 return {signature_descriptor};
1313 }
1314
1315 // Calculate the exact string size that the JNI descriptor will be
1316 // at runtime.
1317 //
1318 // Without this we cannot allocate enough space within static storage
1319 // to fit the compile-time evaluated string.
1320 static constexpr size_t CalculateStringSize() {
1321 ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
1322 signature_descriptor_opt =
1323 FromFunctionType();
1324 if (!signature_descriptor_opt) {
1325 // Assertion handling done by FromFunctionType.
1326 return 0u;
1327 }
1328
1329 JniSignatureDescriptor<kMaxSize> signature_descriptor =
1330 signature_descriptor_opt.value();
1331
1332 size_t acc_size = 1u; // All sigs start with '('.
1333
1334 // Now add every parameter.
1335 for (size_t j = 0; j < signature_descriptor.args.size(); ++j) {
1336 const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j];
1337 // for (const JniDescriptorNode& arg_descriptor : signature_descriptor.args) {
1338 acc_size += arg_descriptor.longy.size();
1339 }
1340
1341 acc_size += 1u; // Add space for ')'.
1342
1343 // Add space for the return value.
1344 acc_size += signature_descriptor.ret.longy.size();
1345
1346 return acc_size;
1347 }
1348
1349 static constexpr size_t kMaxStringSize = CalculateStringSize();
1350 using ConstexprStringDescriptorType = ConstexprArray<char,
1351 kMaxStringSize + 1>;
1352
1353 // Convert the JniSignatureDescriptor we get in FromFunctionType()
1354 // into a flat constexpr char array.
1355 //
1356 // This is done by repeated string concatenation at compile-time.
1357 static constexpr ConstexprStringDescriptorType GetString() {
1358 ConstexprStringDescriptorType c_str{};
1359
1360 ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
1361 signature_descriptor_opt =
1362 FromFunctionType();
1363 if (!signature_descriptor_opt.has_value()) {
1364 // Assertion handling done by FromFunctionType.
1365 c_str[0] = '\0';
1366 return c_str;
1367 }
1368
1369 JniSignatureDescriptor<kMaxSize> signature_descriptor =
1370 signature_descriptor_opt.value();
1371
1372 size_t pos = 0u;
1373 c_str[pos++] = '(';
1374
1375 // Copy all parameter descriptors.
1376 for (size_t j = 0; j < signature_descriptor.args.size(); ++j) {
1377 const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j];
1378 ConstexprStringView longy = arg_descriptor.longy;
1379 for (size_t i = 0; i < longy.size(); ++i) {
1380 c_str[pos++] = longy[i];
1381 }
1382 }
1383
1384 c_str[pos++] = ')';
1385
1386 // Copy return descriptor.
1387 ConstexprStringView longy = signature_descriptor.ret.longy;
1388 for (size_t i = 0; i < longy.size(); ++i) {
1389 c_str[pos++] = longy[i];
1390 }
1391
1392 X_ASSERT(pos == kMaxStringSize);
1393
1394 c_str[pos] = '\0';
1395
1396 return c_str;
1397 }
1398
1399 // Turn a pure constexpr string into one that can be accessed at non-constexpr
1400 // time. Note that the 'static constexpr' storage must be in the scope of a
1401 // function (prior to C++17) to avoid linking errors.
1402 static const char* GetStringAtRuntime() {
1403 static constexpr ConstexprStringDescriptorType str = GetString();
1404 return &str[0];
1405 }
1406 };
1407
1408 // Expression to return JNINativeMethod, performs checking on signature+fn.
1409 #define MAKE_CHECKED_JNI_NATIVE_METHOD(native_kind, name_, signature_, fn) \
1410 ([]() { \
1411 using namespace nativehelper::detail; /* NOLINT(google-build-using-namespace) */ \
1412 static_assert( \
1413 MatchJniDescriptorWithFunctionType<native_kind, \
1414 decltype(fn), \
1415 fn, \
1416 sizeof(signature_)>(signature_),\
1417 "JNI signature doesn't match C++ function type."); \
1418 /* Suppress implicit cast warnings by explicitly casting. */ \
1419 return JNINativeMethod { \
1420 const_cast<decltype(JNINativeMethod::name)>(name_), \
1421 const_cast<decltype(JNINativeMethod::signature)>(signature_), \
1422 reinterpret_cast<void*>(&(fn))}; \
1423 })()
1424
1425 // Expression to return JNINativeMethod, infers signature from fn.
1426 #define MAKE_INFERRED_JNI_NATIVE_METHOD(native_kind, name_, fn) \
1427 ([]() { \
1428 using namespace nativehelper::detail; /* NOLINT(google-build-using-namespace) */ \
1429 /* Suppress implicit cast warnings by explicitly casting. */ \
1430 return JNINativeMethod { \
1431 const_cast<decltype(JNINativeMethod::name)>(name_), \
1432 const_cast<decltype(JNINativeMethod::signature)>( \
1433 InferJniDescriptor<native_kind, \
1434 decltype(fn), \
1435 fn>::GetStringAtRuntime()), \
1436 reinterpret_cast<void*>(&(fn))}; \
1437 })()
1438
1439 } // namespace detail
1440 } // namespace nativehelper
1441
1442